diff --git a/Logic/Osm/Changes.ts b/Logic/Osm/Changes.ts index a42ffb2..caf0afe 100644 --- a/Logic/Osm/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -6,7 +6,6 @@ import Constants from "../../Models/Constants"; import FeatureSource from "../FeatureSource/FeatureSource"; import {TagsFilter} from "../Tags/TagsFilter"; import {Tag} from "../Tags/Tag"; -import {And} from "../Tags/And"; /** * Handles all changes made to OSM. @@ -29,7 +28,9 @@ export class Changes implements FeatureSource{ /** * Adds a change to the pending changes */ - private static checkChange(key: string, value: string): { k: string, v: string } { + private static checkChange(kv: {k: string, v: string}): { k: string, v: string } { + const key = kv.k; + const value = kv.v; if (key === undefined || key === null) { console.log("Invalid key"); return undefined; @@ -43,22 +44,19 @@ export class Changes implements FeatureSource{ console.warn("Tag starts with or ends with a space - trimming anyway") } - key = key.trim(); - value = value.trim(); - - return {k: key, v: value}; + return {k: key.trim(), v: value.trim()}; } addTag(elementId: string, tagsFilter: TagsFilter, tags?: UIEventSource) { - const changes = this.tagToChange(tagsFilter); + const eventSource = tags ?? State.state?.allElements.getEventSourceById(elementId); + const elementTags = eventSource.data; + const changes = tagsFilter.asChange(elementTags).map(Changes.checkChange) if (changes.length == 0) { return; } - const eventSource = tags ?? State.state?.allElements.getEventSourceById(elementId); - const elementTags = eventSource.data; for (const change of changes) { if (elementTags[change.k] !== change.v) { elementTags[change.k] = change.v; @@ -132,28 +130,6 @@ export class Changes implements FeatureSource{ return geojson; } - private tagToChange(tagsFilter: TagsFilter) { - let changes: { k: string, v: string }[] = []; - - if (tagsFilter instanceof Tag) { - const tag = tagsFilter as Tag; - if (typeof tag.value !== "string") { - throw "Invalid value" - } - return [Changes.checkChange(tag.key, tag.value)]; - } - - if (tagsFilter instanceof And) { - const and = tagsFilter as And; - for (const tag of and.and) { - changes = changes.concat(this.tagToChange(tag)); - } - return changes; - } - console.log("Unsupported tagsfilter element to addTag", tagsFilter); - throw "Unsupported tagsFilter element"; - } - private uploadChangesWithLatestVersions( knownElements, newElements: OsmObject[], pending: { elementId: string; key: string; value: string }[]) { // Here, inside the continuation, we know that all 'neededIds' are loaded in 'knownElements', which maps the ids onto the elements diff --git a/Logic/Tags/And.ts b/Logic/Tags/And.ts index 209ad50..ef5789d 100644 --- a/Logic/Tags/And.ts +++ b/Logic/Tags/And.ts @@ -46,8 +46,8 @@ export class And extends TagsFilter { return allChoices; } - asHumanString(linkToWiki: boolean, shorten: boolean) { - return this.and.map(t => t.asHumanString(linkToWiki, shorten)).join("&"); + asHumanString(linkToWiki: boolean, shorten: boolean, properties) { + return this.and.map(t => t.asHumanString(linkToWiki, shorten, properties)).join("&"); } isUsableAsAnswer(): boolean { @@ -108,4 +108,12 @@ export class And extends TagsFilter { usedKeys(): string[] { return [].concat(...this.and.map(subkeys => subkeys.usedKeys())); } + + asChange(properties: any): { k: string; v: string }[] { + const result = [] + for (const tagsFilter of this.and) { + result.push(...tagsFilter.asChange(properties)) + } + return result; + } } \ No newline at end of file diff --git a/Logic/Tags/Or.ts b/Logic/Tags/Or.ts index 1510b37..40cee60 100644 --- a/Logic/Tags/Or.ts +++ b/Logic/Tags/Or.ts @@ -30,8 +30,8 @@ export class Or extends TagsFilter { return choices; } - asHumanString(linkToWiki: boolean, shorten: boolean) { - return this.or.map(t => t.asHumanString(linkToWiki, shorten)).join("|"); + asHumanString(linkToWiki: boolean, shorten: boolean, properties) { + return this.or.map(t => t.asHumanString(linkToWiki, shorten, properties)).join("|"); } isUsableAsAnswer(): boolean { @@ -59,6 +59,10 @@ export class Or extends TagsFilter { usedKeys(): string[] { return [].concat(...this.or.map(subkeys => subkeys.usedKeys())); } + + asChange(properties: any): { k: string; v: string }[] { + throw "Can not convert an 'or' into a changeset" + } } diff --git a/Logic/Tags/RegexTag.ts b/Logic/Tags/RegexTag.ts index 35fdaa4..7c680b8 100644 --- a/Logic/Tags/RegexTag.ts +++ b/Logic/Tags/RegexTag.ts @@ -55,10 +55,6 @@ export class RegexTag extends TagsFilter { return this.invert; } - substituteValues(tags: any): TagsFilter { - return this; - } - asHumanString() { if (typeof this.key === "string") { return `${this.key}${this.invert ? "!" : ""}~${RegexTag.source(this.value)}`; @@ -82,4 +78,8 @@ export class RegexTag extends TagsFilter { } throw "Key cannot be determined as it is a regex" } + + asChange(properties: any): { k: string; v: string }[] { + throw "Cannot convert a regex-tag into a change"; + } } \ No newline at end of file diff --git a/Logic/Tags/SubstitutingTag.ts b/Logic/Tags/SubstitutingTag.ts index 9aecf2e..58591d5 100644 --- a/Logic/Tags/SubstitutingTag.ts +++ b/Logic/Tags/SubstitutingTag.ts @@ -18,15 +18,15 @@ export default class SubstitutingTag implements TagsFilter { this._value = value; } - private static substituteString(template: string, dict: any) { + private static substituteString(template: string, dict: any): string { for (const k in dict) { template = template.replace(new RegExp("\\{" + k + "\\}", 'g'), dict[k]) } return template; } - asHumanString(linkToWiki: boolean, shorten: boolean) { - return this._key + ":=" + this._value; + asHumanString(linkToWiki: boolean, shorten: boolean, properties) { + return this._key + "=" + SubstitutingTag.substituteString(this._value, properties); } asOverpass(): string[] { @@ -56,4 +56,12 @@ export default class SubstitutingTag implements TagsFilter { usedKeys(): string[] { return [this._key]; } + + asChange(properties: any): { k: string; v: string }[] { + const v = SubstitutingTag.substituteString(this._value, properties); + if (v.match(/{.*}/) !== null) { + throw "Could not calculate all the substitutions: still have " + v + } + return [{k: this._key, v: v}]; + } } \ No newline at end of file diff --git a/Logic/Tags/Tag.ts b/Logic/Tags/Tag.ts index d97c1dd..a3a4650 100644 --- a/Logic/Tags/Tag.ts +++ b/Logic/Tags/Tag.ts @@ -81,4 +81,8 @@ export class Tag extends TagsFilter { usedKeys(): string[] { return [this.key]; } + + asChange(properties: any): { k: string; v: string }[] { + return [{k: this.key, v: this.value}]; + } } \ No newline at end of file diff --git a/Logic/Tags/TagsFilter.ts b/Logic/Tags/TagsFilter.ts index 0f91c74..5400aa8 100644 --- a/Logic/Tags/TagsFilter.ts +++ b/Logic/Tags/TagsFilter.ts @@ -8,8 +8,14 @@ export abstract class TagsFilter { abstract matchesProperties(properties: any): boolean; - abstract asHumanString(linkToWiki: boolean, shorten: boolean); + abstract asHumanString(linkToWiki: boolean, shorten: boolean, properties: any); abstract usedKeys(): string[]; + + /** + * Converts the tagsFilter into a list of key-values that should be uploaded to OSM. + * Throws an error if not applicable + */ + abstract asChange(properties:any): {k: string, v:string}[] } \ No newline at end of file diff --git a/UI/BigComponents/SimpleAddUI.ts b/UI/BigComponents/SimpleAddUI.ts index 7530837..0574d9d 100644 --- a/UI/BigComponents/SimpleAddUI.ts +++ b/UI/BigComponents/SimpleAddUI.ts @@ -158,7 +158,7 @@ export default class SimpleAddUI extends UIElement { let tagInfo = ""; const csCount = State.state.osmConnection.userDetails.data.csCount; if (csCount > Constants.userJourney.tagsVisibleAt) { - tagInfo = this._confirmPreset.data.tags.map(t => t.asHumanString(csCount > Constants.userJourney.tagsVisibleAndWikiLinked, true)).join("&"); + tagInfo = this._confirmPreset.data.tags.map(t => t.asHumanString(csCount > Constants.userJourney.tagsVisibleAndWikiLinked, true, {})).join("&"); tagInfo = `
More information about the preset: ${tagInfo}` } @@ -186,7 +186,7 @@ export default class SimpleAddUI extends UIElement { const csCount = State.state.osmConnection.userDetails.data.csCount; let tagInfo = undefined; if (csCount > Constants.userJourney.tagsVisibleAt) { - const presets = preset.tags.map(t => new Combine ([t.asHumanString(false, true), " "]).SetClass("subtle break-words") ) + const presets = preset.tags.map(t => new Combine ([t.asHumanString(false, true, {}), " "]).SetClass("subtle break-words") ) tagInfo = new Combine(presets) } const button: UIElement = diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index ac6da33..751e2b7 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -86,10 +86,10 @@ export default class TagRenderingQuestion extends UIElement { return Translations.t.general.noTagsSelected.SetClass("subtle").Render(); } if (csCount < Constants.userJourney.tagsVisibleAndWikiLinked) { - const tagsStr = tags.asHumanString(false, true); + const tagsStr = tags.asHumanString(false, true, self._tags.data); return new FixedUiElement(tagsStr).SetClass("subtle").Render(); } - return tags.asHumanString(true, true); + return tags.asHumanString(true, true, self._tags.data); } ) ).SetClass("block") diff --git a/assets/layers/benches/benches.json b/assets/layers/benches/benches.json index 7eb2c4e..b1ac1c2 100644 --- a/assets/layers/benches/benches.json +++ b/assets/layers/benches/benches.json @@ -227,6 +227,24 @@ } } ] + }, + { + "question":{ + "en": "When was this bench last surveyed?" + }, + "render": { + "en": "This bench was last surveyed on {survey:date}" + }, + "freeform": { + "key": "survey:date", + "type": "date" + }, + "mappings": [ + { + "if": "survey:date:={_now:date}", + "then": "Surveyed today!" + } + ] } ], "hideUnderlayingFeaturesMinPercentage": 0,