Move 'asChanges' into tagsFilter; add 'survey:date' option with automatic date to benches
This commit is contained in:
parent
fa5b92e6e1
commit
15a6794e59
10 changed files with 71 additions and 47 deletions
|
@ -6,7 +6,6 @@ import Constants from "../../Models/Constants";
|
||||||
import FeatureSource from "../FeatureSource/FeatureSource";
|
import FeatureSource from "../FeatureSource/FeatureSource";
|
||||||
import {TagsFilter} from "../Tags/TagsFilter";
|
import {TagsFilter} from "../Tags/TagsFilter";
|
||||||
import {Tag} from "../Tags/Tag";
|
import {Tag} from "../Tags/Tag";
|
||||||
import {And} from "../Tags/And";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles all changes made to OSM.
|
* Handles all changes made to OSM.
|
||||||
|
@ -29,7 +28,9 @@ export class Changes implements FeatureSource{
|
||||||
/**
|
/**
|
||||||
* Adds a change to the pending changes
|
* 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) {
|
if (key === undefined || key === null) {
|
||||||
console.log("Invalid key");
|
console.log("Invalid key");
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -43,22 +44,19 @@ export class Changes implements FeatureSource{
|
||||||
console.warn("Tag starts with or ends with a space - trimming anyway")
|
console.warn("Tag starts with or ends with a space - trimming anyway")
|
||||||
}
|
}
|
||||||
|
|
||||||
key = key.trim();
|
return {k: key.trim(), v: value.trim()};
|
||||||
value = value.trim();
|
|
||||||
|
|
||||||
return {k: key, v: value};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
addTag(elementId: string, tagsFilter: TagsFilter,
|
addTag(elementId: string, tagsFilter: TagsFilter,
|
||||||
tags?: UIEventSource<any>) {
|
tags?: UIEventSource<any>) {
|
||||||
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) {
|
if (changes.length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const eventSource = tags ?? State.state?.allElements.getEventSourceById(elementId);
|
|
||||||
const elementTags = eventSource.data;
|
|
||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
if (elementTags[change.k] !== change.v) {
|
if (elementTags[change.k] !== change.v) {
|
||||||
elementTags[change.k] = change.v;
|
elementTags[change.k] = change.v;
|
||||||
|
@ -132,28 +130,6 @@ export class Changes implements FeatureSource{
|
||||||
return geojson;
|
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(
|
private uploadChangesWithLatestVersions(
|
||||||
knownElements, newElements: OsmObject[], pending: { elementId: string; key: string; value: string }[]) {
|
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
|
// Here, inside the continuation, we know that all 'neededIds' are loaded in 'knownElements', which maps the ids onto the elements
|
||||||
|
|
|
@ -46,8 +46,8 @@ export class And extends TagsFilter {
|
||||||
return allChoices;
|
return allChoices;
|
||||||
}
|
}
|
||||||
|
|
||||||
asHumanString(linkToWiki: boolean, shorten: boolean) {
|
asHumanString(linkToWiki: boolean, shorten: boolean, properties) {
|
||||||
return this.and.map(t => t.asHumanString(linkToWiki, shorten)).join("&");
|
return this.and.map(t => t.asHumanString(linkToWiki, shorten, properties)).join("&");
|
||||||
}
|
}
|
||||||
|
|
||||||
isUsableAsAnswer(): boolean {
|
isUsableAsAnswer(): boolean {
|
||||||
|
@ -108,4 +108,12 @@ export class And extends TagsFilter {
|
||||||
usedKeys(): string[] {
|
usedKeys(): string[] {
|
||||||
return [].concat(...this.and.map(subkeys => subkeys.usedKeys()));
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -30,8 +30,8 @@ export class Or extends TagsFilter {
|
||||||
return choices;
|
return choices;
|
||||||
}
|
}
|
||||||
|
|
||||||
asHumanString(linkToWiki: boolean, shorten: boolean) {
|
asHumanString(linkToWiki: boolean, shorten: boolean, properties) {
|
||||||
return this.or.map(t => t.asHumanString(linkToWiki, shorten)).join("|");
|
return this.or.map(t => t.asHumanString(linkToWiki, shorten, properties)).join("|");
|
||||||
}
|
}
|
||||||
|
|
||||||
isUsableAsAnswer(): boolean {
|
isUsableAsAnswer(): boolean {
|
||||||
|
@ -59,6 +59,10 @@ export class Or extends TagsFilter {
|
||||||
usedKeys(): string[] {
|
usedKeys(): string[] {
|
||||||
return [].concat(...this.or.map(subkeys => subkeys.usedKeys()));
|
return [].concat(...this.or.map(subkeys => subkeys.usedKeys()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asChange(properties: any): { k: string; v: string }[] {
|
||||||
|
throw "Can not convert an 'or' into a changeset"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -55,10 +55,6 @@ export class RegexTag extends TagsFilter {
|
||||||
return this.invert;
|
return this.invert;
|
||||||
}
|
}
|
||||||
|
|
||||||
substituteValues(tags: any): TagsFilter {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
asHumanString() {
|
asHumanString() {
|
||||||
if (typeof this.key === "string") {
|
if (typeof this.key === "string") {
|
||||||
return `${this.key}${this.invert ? "!" : ""}~${RegexTag.source(this.value)}`;
|
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"
|
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";
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -18,15 +18,15 @@ export default class SubstitutingTag implements TagsFilter {
|
||||||
this._value = value;
|
this._value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static substituteString(template: string, dict: any) {
|
private static substituteString(template: string, dict: any): string {
|
||||||
for (const k in dict) {
|
for (const k in dict) {
|
||||||
template = template.replace(new RegExp("\\{" + k + "\\}", 'g'), dict[k])
|
template = template.replace(new RegExp("\\{" + k + "\\}", 'g'), dict[k])
|
||||||
}
|
}
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
asHumanString(linkToWiki: boolean, shorten: boolean) {
|
asHumanString(linkToWiki: boolean, shorten: boolean, properties) {
|
||||||
return this._key + ":=" + this._value;
|
return this._key + "=" + SubstitutingTag.substituteString(this._value, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
asOverpass(): string[] {
|
asOverpass(): string[] {
|
||||||
|
@ -56,4 +56,12 @@ export default class SubstitutingTag implements TagsFilter {
|
||||||
usedKeys(): string[] {
|
usedKeys(): string[] {
|
||||||
return [this._key];
|
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}];
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -81,4 +81,8 @@ export class Tag extends TagsFilter {
|
||||||
usedKeys(): string[] {
|
usedKeys(): string[] {
|
||||||
return [this.key];
|
return [this.key];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asChange(properties: any): { k: string; v: string }[] {
|
||||||
|
return [{k: this.key, v: this.value}];
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -8,8 +8,14 @@ export abstract class TagsFilter {
|
||||||
|
|
||||||
abstract matchesProperties(properties: any): boolean;
|
abstract matchesProperties(properties: any): boolean;
|
||||||
|
|
||||||
abstract asHumanString(linkToWiki: boolean, shorten: boolean);
|
abstract asHumanString(linkToWiki: boolean, shorten: boolean, properties: any);
|
||||||
|
|
||||||
abstract usedKeys(): string[];
|
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}[]
|
||||||
|
|
||||||
}
|
}
|
|
@ -158,7 +158,7 @@ export default class SimpleAddUI extends UIElement {
|
||||||
let tagInfo = "";
|
let tagInfo = "";
|
||||||
const csCount = State.state.osmConnection.userDetails.data.csCount;
|
const csCount = State.state.osmConnection.userDetails.data.csCount;
|
||||||
if (csCount > Constants.userJourney.tagsVisibleAt) {
|
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 = `<br/>More information about the preset: ${tagInfo}`
|
tagInfo = `<br/>More information about the preset: ${tagInfo}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ export default class SimpleAddUI extends UIElement {
|
||||||
const csCount = State.state.osmConnection.userDetails.data.csCount;
|
const csCount = State.state.osmConnection.userDetails.data.csCount;
|
||||||
let tagInfo = undefined;
|
let tagInfo = undefined;
|
||||||
if (csCount > Constants.userJourney.tagsVisibleAt) {
|
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)
|
tagInfo = new Combine(presets)
|
||||||
}
|
}
|
||||||
const button: UIElement =
|
const button: UIElement =
|
||||||
|
|
|
@ -86,10 +86,10 @@ export default class TagRenderingQuestion extends UIElement {
|
||||||
return Translations.t.general.noTagsSelected.SetClass("subtle").Render();
|
return Translations.t.general.noTagsSelected.SetClass("subtle").Render();
|
||||||
}
|
}
|
||||||
if (csCount < Constants.userJourney.tagsVisibleAndWikiLinked) {
|
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 new FixedUiElement(tagsStr).SetClass("subtle").Render();
|
||||||
}
|
}
|
||||||
return tags.asHumanString(true, true);
|
return tags.asHumanString(true, true, self._tags.data);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).SetClass("block")
|
).SetClass("block")
|
||||||
|
|
|
@ -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,
|
"hideUnderlayingFeaturesMinPercentage": 0,
|
||||||
|
|
Loading…
Reference in a new issue