Fix multiple bugs after user testing

This commit is contained in:
Pieter Vander Vennet 2020-06-28 23:33:48 +02:00
parent bcdbf6a2dd
commit 9bd37d9cde
20 changed files with 1529 additions and 77 deletions

View file

@ -1,7 +1,6 @@
import {Basemap} from "./Basemap";
import {TagsFilter, TagUtils} from "./TagsFilter";
import {UIEventSource} from "../UI/UIEventSource";
import {UIElement} from "../UI/UIElement";
import {ElementStorage} from "./ElementStorage";
import {Changes} from "./Changes";
import L from "leaflet"
@ -22,8 +21,7 @@ export class FilteredLayer {
public readonly filters: TagsFilter;
private readonly _map: Basemap;
private readonly _removeContainedElements;
private readonly _removeTouchingElements;
private readonly _maxAllowedOverlap: number;
private readonly _style: (properties) => any;
@ -46,8 +44,7 @@ export class FilteredLayer {
map: Basemap, storage: ElementStorage,
changes: Changes,
filters: TagsFilter,
removeContainedElements: boolean,
removeTouchingElements: boolean,
maxAllowedOverlap: number,
style: ((properties) => any),
selectedElement: UIEventSource<any>) {
this._selectedElement = selectedElement;
@ -62,8 +59,7 @@ export class FilteredLayer {
this.filters = filters;
this._style = style;
this._storage = storage;
this._removeContainedElements = removeContainedElements;
this._removeTouchingElements = removeTouchingElements;
this._maxAllowedOverlap = maxAllowedOverlap;
}
@ -92,8 +88,8 @@ export class FilteredLayer {
const notShadowed = [];
for (const feature of leftoverFeatures) {
if (this._removeContainedElements || this._removeTouchingElements) {
if (GeoOperations.featureIsContainedInAny(feature, selfFeatures, this._removeTouchingElements)) {
if (this._maxAllowedOverlap !== undefined && this._maxAllowedOverlap >= 0) {
if (GeoOperations.featureIsContainedInAny(feature, selfFeatures, this._maxAllowedOverlap)) {
// This feature is filtered away
continue;
}

View file

@ -2,6 +2,7 @@ import {Basemap} from "./Basemap";
import {UIEventSource} from "../UI/UIEventSource";
import {UIElement} from "../UI/UIElement";
import L from "leaflet";
import {Helpers} from "../Helpers";
export class GeoLocationHandler extends UIElement {
@ -11,7 +12,7 @@ export class GeoLocationHandler extends UIElement {
}> = new UIEventSource<{ latlng: number, accuracy: number }>(undefined);
private _isActive: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private _permission: UIEventSource<string> = new UIEventSource<string>("");
private _map: Basemap;
private _marker: any;
@ -20,6 +21,7 @@ export class GeoLocationHandler extends UIElement {
this._map = map;
this.ListenTo(this.currentLocation);
this.ListenTo(this._isActive);
this.ListenTo(this._permission);
const self = this;
@ -27,6 +29,7 @@ export class GeoLocationHandler extends UIElement {
function onAccuratePositionProgress(e) {
console.log(e.accuracy);
console.log(e.latlng);
self.currentLocation.setData({latlng: e.latlng, accuracy: e.accuracy});
}
function onAccuratePositionFound(e) {
@ -62,8 +65,19 @@ export class GeoLocationHandler extends UIElement {
self._marker = newMarker;
});
navigator.permissions.query({ name: 'geolocation' })
.then(function(){self.StartGeolocating()});
navigator.permissions.query({name: 'geolocation'})
.then(function (status) {
console.log("Geolocation is already", status)
if (status.state === "granted") {
self.StartGeolocating();
}
self._permission.setData(status.state);
status.onchange = function () {
self._permission.setData(status.state);
}
});
this.HideOnEmpty(true);
}
@ -79,20 +93,33 @@ export class GeoLocationHandler extends UIElement {
}
private StartGeolocating(){
private StartGeolocating() {
const self = this;
if (self._permission.data === "denied") {
return "";
}
if (self.currentLocation.data !== undefined) {
self._map.map.flyTo(self.currentLocation.data.latlng, 18);
return;
}
self._isActive.setData(true);
console.log("Searching location using GPS")
self._map.map.findAccuratePosition({
maxWait: 15000, // defaults to 10000
desiredAccuracy: 30 // defaults to 20
maxWait: 10000, // defaults to 10000
desiredAccuracy: 50 // defaults to 20
});
if (!self._isActive.data) {
self._isActive.setData(true);
Helpers.DoEvery(60000, () => {
self._map.map.findAccuratePosition({
maxWait: 10000, // defaults to 10000
desiredAccuracy: 50 // defaults to 20
});
})
}
}
InnerUpdate(htmlElement: HTMLElement) {

View file

@ -1,8 +1,15 @@
import * as turf from 'turf'
export class GeoOperations {
static surfaceAreaInSqMeters(feature: any) {
return turf.area(feature);
}
static featureIsContainedInAny(feature: any, shouldNotContain: any[], noTouching: boolean = false): boolean {
static featureIsContainedInAny(feature: any,
shouldNotContain: any[],
maxOverlapPercentage: number): boolean {
// Returns 'false' if no problematic intersection is found
if (feature.geometry.type === "Point") {
const coor = feature.geometry.coordinates;
for (const shouldNotContainElement of shouldNotContain) {
@ -21,38 +28,55 @@ export class GeoOperations {
}
if (feature.geometry.type === "Polygon") {
if (feature.geometry.type === "Polygon" || feature.geometry.type === "MultiPolygon") {
const poly = feature;
let featureBBox = BBox.get(feature);
const featureSurface = GeoOperations.surfaceAreaInSqMeters(poly);
for (const shouldNotContainElement of shouldNotContain) {
let shouldNotContainBBox = BBox.get(shouldNotContainElement);
let featureBBox = BBox.get(feature);
if (!featureBBox.overlapsWith(shouldNotContainBBox)) {
const shouldNotContainBBox = BBox.get(shouldNotContainElement);
const overlaps = featureBBox.overlapsWith(shouldNotContainBBox)
if (!overlaps) {
continue;
}
if (noTouching) {
if (GeoOperations.isPolygonTouching(poly, shouldNotContainElement)) {
return true;
}
} else {
if (GeoOperations.isPolygonInside(poly, shouldNotContainElement)) {
// Calculate the surface area of the intersection
// If it is too big, refuse
try {
const intersection = turf.intersect(poly, shouldNotContainElement);
if (intersection == null) {
continue;
}
const intersectionSize = turf.area(intersection);
const ratio = intersectionSize / featureSurface;
console.log("Intersection ratio", ratio, "intersection:", intersectionSize, "featuresize:", featureSurface, "targetRatio", maxOverlapPercentage / 100);
if (ratio * 100 >= maxOverlapPercentage) {
console.log("Refused", poly.id, " due to ", shouldNotContainElement.id, "intersection ratio is ", ratio, "which is bigger then the target ratio of ", (maxOverlapPercentage / 100))
return true;
}
} catch (exception) {
console.log("EXCEPTION CAUGHT WHILE INTERSECTING: ", exception);
// We assume that this failed due to an intersection
return true;
}
}
return false; // No problematic intersections found
}
return false;
}
/**
* Simple check: that every point of the polygon is inside the container
* @param polygon
* @param container
*/
static isPolygonInside(polygon, container) {
private static isPolygonInside(polygon, container) {
for (const coor of polygon.geometry.coordinates[0]) {
if (!GeoOperations.inside(coor, container)) {
return false;
@ -66,7 +90,7 @@ export class GeoOperations {
* @param polygon
* @param container
*/
static isPolygonTouching(polygon, container) {
private static isPolygonTouching(polygon, container) {
for (const coor of polygon.geometry.coordinates[0]) {
if (GeoOperations.inside(coor, container)) {
return true;
@ -76,7 +100,7 @@ export class GeoOperations {
}
static inside(pointCoordinate, feature): boolean {
private static inside(pointCoordinate, feature): boolean {
// ray-casting algorithm based on
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
@ -134,10 +158,19 @@ class BBox {
this.minLon = Math.min(this.minLon, coordinate[0]);
this.minLat = Math.min(this.minLat, coordinate[1]);
}
this.check();
}
private check() {
if (isNaN(this.maxLon) || isNaN(this.maxLat) || isNaN(this.minLon) || isNaN(this.minLat)) {
console.log(this);
throw "BBOX has NAN";
}
}
public overlapsWith(other: BBox) {
this.check();
other.check();
if (this.maxLon < other.minLon) {
return false;
}
@ -155,13 +188,22 @@ class BBox {
static get(feature) {
if (feature.bbox === undefined) {
if (feature.geometry.type === "Polygon") {
if (feature.geometry.type === "MultiPolygon") {
let coordinates = [];
for (const coorlist of feature.geometry.coordinates) {
coordinates = coordinates.concat(coorlist[0]);
}
feature.bbox = new BBox(coordinates);
} else if (feature.geometry.type === "Polygon") {
feature.bbox = new BBox(feature.geometry.coordinates[0]);
} else if (feature.geometry.type === "LineString") {
feature.bbox = new BBox(feature.geometry.coordinates);
} else {
} else if (feature.geometry.type === "Point") {
// Point
feature.bbox = new BBox([feature.geometry.coordinates]);
} else {
throw "Cannot calculate bbox, unknown type " + feature.geometry.type;
}
}

View file

@ -76,7 +76,14 @@ export class OsmConnection {
console.log(userInfo);
data.name = userInfo.getAttribute('display_name');
data.csCount = userInfo.getElementsByTagName("changesets")[0].getAttribute("count");
data.img = userInfo.getElementsByTagName("img")[0].getAttribute("href");
data.img = undefined;
const imgEl = userInfo.getElementsByTagName("img");
if (imgEl !== undefined && imgEl[0] !== undefined) {
data.img = imgEl[0].getAttribute("href");
}
data.img = data.img ?? "./assets/osm-logo.svg";
const messages = userInfo.getElementsByTagName("messages")[0].getElementsByTagName("received")[0];
data.unreadMessages = parseInt(messages.getAttribute("unread"));
data.totalMessages = parseInt(messages.getAttribute("count"));