Better handling of metatags, more robust error handling when calculating tags

This commit is contained in:
pietervdvn 2021-05-10 23:51:03 +02:00
parent 69363fbf0f
commit 6ac8a5373c
10 changed files with 134 additions and 89 deletions

View file

@ -7,6 +7,7 @@ import Bounds from "../../Models/Bounds";
import FeatureSource from "../FeatureSource/FeatureSource";
import {Utils} from "../../Utils";
import {TagsFilter} from "../Tags/TagsFilter";
import SimpleMetaTagger from "../SimpleMetaTagger";
export default class UpdateFromOverpass implements FeatureSource {
@ -158,14 +159,17 @@ export default class UpdateFromOverpass implements FeatureSource {
function (data, date) {
self._previousBounds.get(z).push(queryBounds);
self.retries.setData(0);
self.features.setData(data.features.map(f => ({feature: f, freshness: date})));
const features = data.features.map(f => ({feature: f, freshness: date}));
SimpleMetaTagger.objectMetaInfo.addMetaTags(features)
self.features.setData(features);
self.runningQuery.setData(false);
},
function (reason) {
self.retries.data++;
self.ForceRefresh();
self.timeout.setData(self.retries.data * 5);
console.log(`QUERY FAILED (retrying in ${5 * self.retries.data} sec)`);
console.log(`QUERY FAILED (retrying in ${5 * self.retries.data} sec) due to ${reason}`);
self.retries.ping();
self.runningQuery.setData(false);

View file

@ -32,7 +32,19 @@ export class ElementStorage {
return es;
}
addOrGetById(elementId: string, newProperties: any): UIEventSource<any> {
getEventSourceById(elementId): UIEventSource<any> {
if (this._elements.has(elementId)) {
return this._elements.get(elementId);
}
console.error("Can not find eventsource with id ", elementId);
return undefined;
}
has(id) {
return this._elements.has(id);
}
private addOrGetById(elementId: string, newProperties: any): UIEventSource<any> {
if (!this._elements.has(elementId)) {
const eventSource = new UIEventSource<any>(newProperties, "tags of " + elementId);
this._elements.set(elementId, eventSource);
@ -48,30 +60,33 @@ export class ElementStorage {
const keptKeys = es.data;
// The element already exists
// We use the new feature to overwrite all the properties in the already existing eventsource
console.log("Merging multiple instances of ", elementId)
const debug_msg = []
let somethingChanged = false;
for (const k in newProperties) {
const v = newProperties[k];
if (keptKeys[k] !== v) {
keptKeys[k] = v;
if (v === undefined) {
// The new value is undefined; the tag might have been removed
// It might be a metatag as well
// In the latter case, we do keep the tag!
if (!k.startsWith("_")) {
delete keptKeys[k]
debug_msg.push(("Erased " + k))
}
} else {
keptKeys[k] = v;
debug_msg.push(k + " --> " + v)
}
somethingChanged = true;
}
}
if (somethingChanged) {
console.trace(`Merging multiple instances of ${elementId}: ` + debug_msg.join(", ")+" newProperties: ", newProperties)
es.ping();
}
return es;
}
getEventSourceById(elementId): UIEventSource<any> {
if (this._elements.has(elementId)) {
return this._elements.get(elementId);
}
console.error("Can not find eventsource with id ", elementId);
return undefined;
}
has(id) {
return this._elements.has(id);
}
}

View file

@ -3,16 +3,15 @@ import {UIEventSource} from "../UIEventSource";
import State from "../../State";
export default class RegisteringFeatureSource implements FeatureSource {
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>;
public readonly name;
public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>;
public readonly name;
constructor(source: FeatureSource) {
this.features = source.features;
this.name = "RegisteringSource of "+source.name;
this.name = "RegisteringSource of " + source.name;
this.features.addCallbackAndRun(features => {
for (const feature of features ?? []) {
if (!State.state.allElements.has(feature.feature.properties.id)) {
State.state.allElements.addOrGetElement(feature.feature)
}
State.state.allElements.addOrGetElement(feature.feature)
}
})
}

View file

@ -62,7 +62,12 @@ export default class MetaTagging {
if (f === undefined) {
continue;
}
f({featuresPerLayer: featuresPerLayer, memberships: relations}, feature.feature)
try{
f({featuresPerLayer: featuresPerLayer, memberships: relations}, feature.feature)
}catch(e){
console.error(e)
}
}
}
@ -84,10 +89,21 @@ export default class MetaTagging {
}
const func = new Function("feat", "return " + code + ";");
try{
const f = (featuresPerLayer, feature: any) => {
feature.properties[key] = func(feature);
try{
feature.properties[key] = func(feature);
}catch(e){
console.error("Could not calculate a metatag defined by "+code+" due to "+e+". This is code defined in the theme. Are you the theme creator? Doublecheck your code. Note that the metatags might not be stable on new features")
}
}
functions.push(f)
}catch(e){
console.error("Could not create a dynamic function: ", e)
}
}
return (params: Params, feature) => {
const tags = feature.properties

View file

@ -67,7 +67,46 @@ export abstract class OsmObject {
})
}
private static ParseObjects(elements: any[]) : OsmObject[]{
// bounds should be: [[maxlat, minlon], [minlat, maxlon]] (same as Utils.tile_bounds)
public static LoadArea(bounds: [[number, number], [number, number]], callback: (objects: OsmObject[]) => void) {
const minlon = bounds[0][1]
const maxlon = bounds[1][1]
const minlat = bounds[1][0]
const maxlat = bounds[0][0];
const url = `https://www.openstreetmap.org/api/0.6/map.json?bbox=${minlon},${minlat},${maxlon},${maxlat}`
$.getJSON(url, data => {
const elements: any[] = data.elements;
const objects = OsmObject.ParseObjects(elements)
callback(objects);
})
}
//Loads an area from the OSM-api.
public static DownloadAll(neededIds, knownElements: any = {}, continuation: ((knownObjects: any) => void)) {
// local function which downloads all the objects one by one
// this is one big loop, running one download, then rerunning the entire function
if (neededIds.length == 0) {
continuation(knownElements);
return;
}
const neededId = neededIds.pop();
if (neededId in knownElements) {
OsmObject.DownloadAll(neededIds, knownElements, continuation);
return;
}
OsmObject.DownloadObject(neededId,
function (element) {
knownElements[neededId] = element; // assign the element for later, continue downloading the next element
OsmObject.DownloadAll(neededIds, knownElements, continuation);
}
);
}
private static ParseObjects(elements: any[]): OsmObject[] {
const objects: OsmObject[] = [];
const allNodes: Map<number, OsmNode> = new Map<number, OsmNode>()
for (const element of elements) {
@ -96,44 +135,6 @@ export abstract class OsmObject {
}
return objects;
}
//Loads an area from the OSM-api.
// bounds should be: [[maxlat, minlon], [minlat, maxlon]] (same as Utils.tile_bounds)
public static LoadArea(bounds: [[number, number], [number, number]], callback: (objects: OsmObject[]) => void) {
const minlon = bounds[0][1]
const maxlon = bounds[1][1]
const minlat = bounds[1][0]
const maxlat = bounds[0][0];
const url = `https://www.openstreetmap.org/api/0.6/map.json?bbox=${minlon},${minlat},${maxlon},${maxlat}`
$.getJSON(url, data => {
const elements: any[] = data.elements;
const objects = OsmObject.ParseObjects(elements)
callback(objects);
})
}
public static DownloadAll(neededIds, knownElements: any = {}, continuation: ((knownObjects: any) => void)) {
// local function which downloads all the objects one by one
// this is one big loop, running one download, then rerunning the entire function
if (neededIds.length == 0) {
continuation(knownElements);
return;
}
const neededId = neededIds.pop();
if (neededId in knownElements) {
OsmObject.DownloadAll(neededIds, knownElements, continuation);
return;
}
OsmObject.DownloadObject(neededId,
function (element) {
knownElements[neededId] = element; // assign the element for later, continue downloading the next element
OsmObject.DownloadAll(neededIds, knownElements, continuation);
}
);
}
// The centerpoint of the feature, as [lat, lon]
public abstract centerpoint(): [number, number];
@ -149,10 +150,10 @@ export abstract class OsmObject {
TagsXML(): string {
let tags = "";
for (const key in this.tags) {
if(key.startsWith("_")){
if (key.startsWith("_")) {
continue;
}
if(key === "id"){
if (key === "id") {
continue;
}
const v = this.tags[key];
@ -168,24 +169,25 @@ export abstract class OsmObject {
const full = this.type !== "way" ? "" : "/full";
const url = "https://www.openstreetmap.org/api/0.6/" + this.type + "/" + this.id + full;
$.getJSON(url, function (data) {
const element = data.elements.pop();
let nodes = []
if(data.elements.length > 2){
if (data.elements.length > 2) {
nodes = OsmObject.ParseObjects(data.elements)
}
self.LoadData(element)
self.SaveExtraData(element, nodes);
continuation(self, {
const meta = {
"_last_edit:contributor": element.user,
"_last_edit:contributor:uid": element.uid,
"_last_edit:changeset": element.changeset,
"_last_edit:timestamp": new Date(element.timestamp),
"_version_number": element.version
});
}
continuation(self, meta);
}
);
return this;
@ -220,7 +222,7 @@ export abstract class OsmObject {
this.version = element.version;
this.timestamp = element.timestamp;
const tgs = this.tags;
if(element.tags === undefined){
if (element.tags === undefined) {
// Simple node which is part of a way - not important
return;
}
@ -230,8 +232,6 @@ export abstract class OsmObject {
tgs["_last_edit:timestamp"] = element.timestamp
tgs["_version_number"] = element.version
tgs["id"] = this.type + "/" + this.id;
}
}

View file

@ -45,6 +45,7 @@ export class Overpass {
// @ts-ignore
const geojson = OsmToGeoJson.default(json);
const osmTime = new Date(json.osm3s.timestamp_osm_base);
continuation(geojson, osmTime);
}).fail(onFail)

View file

@ -31,17 +31,20 @@ export default class SimpleMetaTagger {
(feature) => {/*Note: also handled by 'UpdateTagsFromOsmAPI'*/
const tgs = feature.properties;
tgs["_last_edit:contributor"] = tgs["user"]
tgs["_last_edit:contributor:uid"] = tgs["uid"]
tgs["_last_edit:changeset"] = tgs["changeset"]
tgs["_last_edit:timestamp"] = tgs["timestamp"]
tgs["_version_number"] = tgs["version"]
delete tgs["timestamp"]
delete tgs["version"]
delete tgs["changeset"]
delete tgs["user"]
delete tgs["uid"]
function move(src: string, target: string){
if(tgs[src] === undefined){
return;
}
tgs[target] = tgs[src]
delete tgs[src]
}
move("user","_last_edit:contributor")
move("uid","_last_edit:contributor:uid")
move("changeset","_last_edit:changeset")
move("timestamp","_last_edit:timestamp")
move("version","_version_number")
}
)
private static latlon = new SimpleMetaTagger({

View file

@ -16,6 +16,9 @@ export class RegexTag extends TagsFilter {
}
private static doesMatch(fromTag: string, possibleRegex: string | RegExp): boolean {
if(fromTag === undefined){
return;
}
if (typeof possibleRegex === "string") {
return fromTag === possibleRegex;
}
@ -42,6 +45,9 @@ export class RegexTag extends TagsFilter {
matchesProperties(tags: any): boolean {
for (const key in tags) {
if(key === undefined){
continue;
}
if (RegexTag.doesMatch(key, this.key)) {
const value = tags[key]
return RegexTag.doesMatch(value, this.value) != this.invert;

View file

@ -53,7 +53,9 @@ export default class EditableTagRendering extends UIElement {
return this._question.Render();
}
if(!this._configuration.IsKnown(this._tags.data)){
return ""
// Even though it is not known, we hide the question here
// It is the questionbox's task to show the question in edit mode
return "";
}
return new Combine([this._answer,

View file

@ -3,7 +3,6 @@ import {UIEventSource} from "../../Logic/UIEventSource";
import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig";
import TagRenderingQuestion from "./TagRenderingQuestion";
import Translations from "../i18n/Translations";
import {TagUtils} from "../../Logic/TagUtils";
/**
@ -44,6 +43,7 @@ export default class QuestionBox extends UIElement {
.onClick(() => {
self._skippedQuestions.setData([]);
})
this.SetClass("block")
}
InnerRender(): string {
@ -57,7 +57,6 @@ export default class QuestionBox extends UIElement {
if (this._skippedQuestions.data.indexOf(i) >= 0) {
continue;
}
// this value is NOT known
return this._tagRenderingQuestions[i].Render();
}