diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index 7d42e9d01..2934f93e2 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -26,7 +26,7 @@ export default class SimpleMetaTagger { "_last_edit:changeset", "_last_edit:timestamp", "_version_number", - "_backend"], + "_backend"], doc: "Information about the last edit of this object." }, (feature) => {/*Note: also called by 'UpdateTagsFromOsmAPI'*/ @@ -67,17 +67,100 @@ export default class SimpleMetaTagger { private static layerInfo = new SimpleMetaTagger( { doc: "The layer-id to which this feature belongs. Note that this might be return any applicable if `passAllFeatures` is defined.", - keys:["_layer"], + keys: ["_layer"], includesDates: false, }, (feature, freshness, layer) => { - if(feature.properties._layer === layer.id){ + if (feature.properties._layer === layer.id) { return false; } feature.properties._layer = layer.id return true; } ) + private static noBothButLeftRight = new SimpleMetaTagger( + { + keys: ["sidewalk:left", "sidewalk:right", "generic_key:left:property", "generic_key:right:property"], + doc: "Rewrites tags from 'generic_key:both:property' as 'generic_key:left:property' and 'generic_key:right:property' (and similar for sidewalk tagging). Note that this rewritten tags _will be reuploaded on a change_. To prevent to much unrelated retagging, this is only enabled if the layer has at least some lineRenderings with offset defined", + includesDates: false, + cleanupRetagger: true + }, + ((feature, state, layer) => { + + if(!layer.lineRendering.some(lr => lr.leftRightSensitive)){ + return; + } + + const tgs = feature.properties; + let somethingChanged = false + + /** + * Sets the key onto the properties (but doesn't overwrite if already existing) + */ + function set(key, value) { + if (tgs[key] === undefined || tgs[key] === "") { + tgs[key] = value + somethingChanged = true + } + } + + if (tgs["sidewalk"]) { + + const v = tgs["sidewalk"] + switch (v) { + case "none": + case "no": + set("sidewalk:left", "no"); + set("sidewalk:right", "no"); + break + case "both": + set("sidewalk:left", "yes"); + set("sidewalk:right", "yes"); + break; + case "left": + set("sidewalk:left", "yes"); + set("sidewalk:right", "no"); + break; + case "right": + set("sidewalk:left", "no"); + set("sidewalk:right", "yes"); + break; + default: + set("sidewalk:left", v); + set("sidewalk:right", v); + break; + } + delete tgs["sidewalk"] + somethingChanged = true + } + + + const regex = /\([^:]*\):both:\(.*\)/ + for (const key in tgs) { + const v = tgs[key] + if (key.endsWith(":both")) { + const strippedKey = key.substring(0, key.length - ":both".length) + set(strippedKey + ":left", v) + set(strippedKey + ":right", v) + delete tgs[key] + continue + } + + const match = key.match(regex) + if (match !== null) { + const strippedKey = match[1] + const property = match[1] + set(strippedKey + ":left:" + property, v) + set(strippedKey + ":right:" + property, v) + console.log("Left-right rewritten " + key) + delete tgs[key] + } + } + + + return somethingChanged + }) + ) private static surfaceArea = new SimpleMetaTagger( { keys: ["_surface", "_surface:ha"], @@ -85,12 +168,12 @@ export default class SimpleMetaTagger { isLazy: true }, (feature => { - + Object.defineProperty(feature.properties, "_surface", { enumerable: false, configurable: true, get: () => { - const sqMeters = ""+ GeoOperations.surfaceAreaInSqMeters(feature); + const sqMeters = "" + GeoOperations.surfaceAreaInSqMeters(feature); delete feature.properties["_surface"] feature.properties["_surface"] = sqMeters; return sqMeters @@ -108,7 +191,7 @@ export default class SimpleMetaTagger { return sqMetersHa } }) - + return true; }) ); @@ -219,8 +302,8 @@ export default class SimpleMetaTagger { // isOpen is irrelevant return false } - - Object.defineProperty(feature.properties, "_isOpen",{ + + Object.defineProperty(feature.properties, "_isOpen", { enumerable: false, configurable: true, get: () => { @@ -247,7 +330,7 @@ export default class SimpleMetaTagger { if (oldNextChange > (new Date()).getTime() && tags["_isOpen:oldvalue"] === tags["opening_hours"] - && tags["_isOpen"] !== undefined) { + && tags["_isOpen"] !== undefined) { // Already calculated and should not yet be triggered return false; } @@ -354,7 +437,8 @@ export default class SimpleMetaTagger { SimpleMetaTagger.isOpen, SimpleMetaTagger.directionSimplified, SimpleMetaTagger.currentTime, - SimpleMetaTagger.objectMetaInfo + SimpleMetaTagger.objectMetaInfo, + SimpleMetaTagger.noBothButLeftRight ]; public static readonly lazyTags: string[] = [].concat(...SimpleMetaTagger.metatags.filter(tagger => tagger.isLazy) @@ -365,22 +449,24 @@ export default class SimpleMetaTagger { public readonly isLazy: boolean; public readonly includesDates: boolean public readonly applyMetaTagsOnFeature: (feature: any, freshness: Date, layer: LayerConfig) => boolean; - + /*** * A function that adds some extra data to a feature * @param docs: what does this extra data do? * @param f: apply the changes. Returns true if something changed */ - constructor(docs: { keys: string[], doc: string, includesDates?: boolean, isLazy?: boolean }, + constructor(docs: { keys: string[], doc: string, includesDates?: boolean, isLazy?: boolean, cleanupRetagger?: boolean }, f: ((feature: any, freshness: Date, layer: LayerConfig) => boolean)) { this.keys = docs.keys; this.doc = docs.doc; this.isLazy = docs.isLazy this.applyMetaTagsOnFeature = f; this.includesDates = docs.includesDates ?? false; - for (const key of docs.keys) { - if (!key.startsWith('_') && key.toLowerCase().indexOf("theme") < 0) { - throw `Incorrect metakey ${key}: it should start with underscore (_)` + if (!docs.cleanupRetagger) { + for (const key of docs.keys) { + if (!key.startsWith('_') && key.toLowerCase().indexOf("theme") < 0) { + throw `Incorrect metakey ${key}: it should start with underscore (_)` + } } } } diff --git a/Models/ThemeConfig/Json/LineRenderingConfigJson.ts b/Models/ThemeConfig/Json/LineRenderingConfigJson.ts index 80ee19009..708366767 100644 --- a/Models/ThemeConfig/Json/LineRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/LineRenderingConfigJson.ts @@ -29,7 +29,10 @@ export default interface LineRenderingConfigJson { /** * The number of pixels this line should be moved. - * Use a positive numbe to move to the right, a negative to move to the left (left/right as defined by the drawing direction of the line) + * Use a positive numbe to move to the right, a negative to move to the left (left/right as defined by the drawing direction of the line). + * + * IMPORTANT: MapComplete will already normalize 'key:both:property' and 'key:both' into the corresponding 'key:left' and 'key:right' tagging (same for 'sidewalk=left/right/both' which is rewritten to 'sidewalk:left' and 'sidewalk:right') + * This simplifies programming. Refer to the CalculatedTags.md-documentation for more details */ offset?: number | TagRenderingConfigJson } \ No newline at end of file diff --git a/Models/ThemeConfig/LineRenderingConfig.ts b/Models/ThemeConfig/LineRenderingConfig.ts index 04e5bf2ec..ae7e4cb60 100644 --- a/Models/ThemeConfig/LineRenderingConfig.ts +++ b/Models/ThemeConfig/LineRenderingConfig.ts @@ -12,12 +12,16 @@ export default class LineRenderingConfig extends WithContextLoader { public readonly width: TagRenderingConfig; public readonly dashArray: TagRenderingConfig; public readonly offset: TagRenderingConfig; - + public readonly leftRightSensitive: boolean + constructor(json: LineRenderingConfigJson, context: string) { super(json, context) this.color = this.tr("color", "#0000ff"); this.width = this.tr("width", "7"); this.dashArray = this.tr("dashArray", ""); + + this.leftRightSensitive = json.offset !== undefined && json.offset !== 0 && json.offset !== "0" + this.offset = this.tr("offset", "0"); } diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index e2b634839..9e44784e1 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -48,7 +48,7 @@ export default class TagRenderingConfig { } if(typeof json === "number"){ - this.render =Translations.WT( ""+json) + this.render = Translations.WT( ""+json) return; } diff --git a/assets/themes/sidewalks/sidewalks.json b/assets/themes/sidewalks/sidewalks.json index 8890b70f6..5248c97c0 100644 --- a/assets/themes/sidewalks/sidewalks.json +++ b/assets/themes/sidewalks/sidewalks.json @@ -45,8 +45,23 @@ "width": 8 }, { - "color": "#888", - "width": 8, + "color": { + "render": "#888" + }, + "width": { + "render": "8", + "mappings": [ + { + "if": { + "or": [ + "sidewalk:left=no", + "sidewalk:left=separate" + ] + }, + "then": 0 + } + ] + }, "offset": -8 }, { @@ -55,7 +70,7 @@ "offset": 8 } ], - "allowSplit": true + "allowSplit": true } ] } \ No newline at end of file