diff --git a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts index 6d3c12aa6b..3ae3ff0536 100644 --- a/Logic/FeatureSource/Sources/FilteringFeatureSource.ts +++ b/Logic/FeatureSource/Sources/FilteringFeatureSource.ts @@ -1,7 +1,6 @@ import {UIEventSource} from "../../UIEventSource"; import FilteredLayer from "../../../Models/FilteredLayer"; import {FeatureSourceForLayer, Tiled} from "../FeatureSource"; -import Hash from "../../Web/Hash"; import {BBox} from "../../BBox"; import {ElementStorage} from "../../ElementStorage"; import {TagsFilter} from "../../Tags/TagsFilter"; @@ -20,7 +19,8 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti }; private readonly _alreadyRegistered = new Set>(); private readonly _is_dirty = new UIEventSource(false) - + private previousFeatureSet : Set = undefined; + constructor( state: { locationControl: UIEventSource<{ zoom: number }>, @@ -65,18 +65,12 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti private update() { const self = this; const layer = this.upstream.layer; - const features: { feature: any; freshness: Date }[] = this.upstream.features.data; + const features: { feature: any; freshness: Date }[] = (this.upstream.features.data ?? []); + const includedFeatureIds = new Set(); const newFeatures = features.filter((f) => { self.registerCallback(f.feature) - if ( - (this.state.selectedElement !== undefined && this.state.selectedElement.data?.id === f.feature.properties.id) || - (Hash.hash.data !== undefined && f.feature.properties.id === Hash.hash.data)) { - // This is the selected object - it gets a free pass even if zoom is not sufficient or it is filtered away - return true; - } - const isShown = layer.layerDef.isShown; const tags = f.feature.properties; if (isShown.IsKnown(tags)) { @@ -97,12 +91,30 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti } } - + includedFeatureIds.add(f.feature.properties.id) return true; }); - this.features.setData(newFeatures); + const previousSet = this.previousFeatureSet; this._is_dirty.setData(false) + + // Is there any difference between the two sets? + if(previousSet !== undefined && previousSet.size === includedFeatureIds.size){ + // The size of the sets is the same - they _might_ be identical + const newItemFound = Array.from(includedFeatureIds).some(id => !previousSet.has(id)) + if(!newItemFound){ + // We know that: + // - The sets have the same size + // - Every item from the new set has been found in the old set + // which means they are identical! + return; + } + + } + + // Something new has been found! + this.features.setData(newFeatures); + } private registerCallback(feature: any) { @@ -115,10 +127,11 @@ export default class FilteringFeatureSource implements FeatureSourceForLayer, Ti } this._alreadyRegistered.add(src) - const self = this; - src.addCallbackAndRunD(_ => { - self._is_dirty.setData(true) - }) + const self = this; + // Add a callback as a changed tag migh change the filter + src.addCallbackAndRunD(_ => { + self._is_dirty.setData(true) + }) } } diff --git a/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts index 11b6f986fe..9c3f1fd2da 100644 --- a/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts @@ -89,6 +89,10 @@ export default class OsmFeatureSource { if (z > 20) { throw "This is an absurd high zoom level" } + + if( z < 14){ + throw `Zoom ${z} is too much for OSM to handle! Use a higher zoom level!` + } const bbox = BBox.fromTile(z, x, y) const url = `${this._backend}/api/0.6/map?bbox=${bbox.minLon},${bbox.minLat},${bbox.maxLon},${bbox.maxLat}` diff --git a/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts b/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts index 0f696560ca..8ea2783ce4 100644 --- a/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts +++ b/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts @@ -8,6 +8,7 @@ import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson"; import LineRenderingConfigJson from "../Json/LineRenderingConfigJson"; import {LayerConfigJson} from "../Json/LayerConfigJson"; import Constants from "../../Constants"; +import {AllKnownLayouts} from "../../../Customizations/AllKnownLayouts"; export interface DesugaringContext { tagRenderings: Map @@ -15,19 +16,14 @@ export interface DesugaringContext { } export abstract class Conversion { - protected readonly doc: string; public readonly modifiedAttributes: string[]; + protected readonly doc: string; constructor(doc: string, modifiedAttributes: string[] = []) { this.modifiedAttributes = modifiedAttributes; this.doc = doc + "\n\nModified attributes are\n" + modifiedAttributes.join(", "); } - public convertStrict(state: DesugaringContext, json: TIn, context: string): TOut { - const fixed = this.convert(state, json, context) - return DesugaringStep.strict(fixed) - } - public static strict(fixed: { errors: string[], warnings: string[], result?: T }): T { if (fixed?.errors?.length > 0) { throw fixed.errors.join("\n"); @@ -36,6 +32,11 @@ export abstract class Conversion { return fixed.result; } + public convertStrict(state: DesugaringContext, json: TIn, context: string): TOut { + const fixed = this.convert(state, json, context) + return DesugaringStep.strict(fixed) + } + abstract convert(state: DesugaringContext, json: TIn, context: string): { result: TOut, errors: string[], warnings: string[] } public convertAll(state: DesugaringContext, jsons: TIn[], context: string): { result: TOut[], errors: string[], warnings: string[] } { @@ -133,9 +134,9 @@ class Fuse extends DesugaringStep { convert(state: DesugaringContext, json: T, context: string): { result: T; errors: string[]; warnings: string[] } { const errors = [] const warnings = [] - for (let i = 0; i < this.steps.length; i++){ + for (let i = 0; i < this.steps.length; i++) { const step = this.steps[i]; - let r = step.convert(state, json, context + "(fusion "+this.constructor.name+"."+i+")") + let r = step.convert(state, json, context + "(fusion " + this.constructor.name + "." + i + ")") errors.push(...r.errors) warnings.push(...r.warnings) json = r.result @@ -157,6 +158,16 @@ class ExpandTagRendering extends Conversion(tr) - Utils.Merge(tr["override"] ?? {}, tr) - trs.push(tr) + for (let foundTr of lookup) { + foundTr = Utils.Clone(foundTr) + Utils.Merge(tr["override"] ?? {}, foundTr) + trs.push(foundTr) } } return trs; @@ -257,17 +276,6 @@ class ExpandTagRendering extends Conversion replaceRecursive(o)) - } - transl = {...transl} - for (const key in transl) { - transl[key] = replaceRecursive(transl[key]) - } - return transl - } - - const orig = tr; - tr = replaceRecursive(tr) - - tr.id = target + "-" + orig.id - tr.group = target - return tr - } - convert(state: DesugaringContext, json: { rewrite: @@ -344,14 +327,14 @@ class ExpandGroupRewrite extends Conversion<{ const trs: TagRenderingConfigJson[] = [] for (const tr of subRenderings) { - trs.push( this.prepConfig(source, target, tr)) + trs.push(this.prepConfig(source, target, tr)) } - if(rewrittenPerGroup.has(groupName)){ + if (rewrittenPerGroup.has(groupName)) { rewrittenPerGroup.get(groupName).push(...trs) - }else{ - rewrittenPerGroup.set(groupName, trs) - + } else { + rewrittenPerGroup.set(groupName, trs) + } } } @@ -365,9 +348,9 @@ class ExpandGroupRewrite extends Conversion<{ }) - rewrittenPerGroup.forEach((group, groupName) => { + rewrittenPerGroup.forEach((group, _) => { group.forEach(tr => { - if(tr.id === undefined || tr.id === ""){ + if (tr.id === undefined || tr.id === "") { errors.push("A tagrendering has an empty ID after expanding the tag") } }) @@ -378,6 +361,31 @@ class ExpandGroupRewrite extends Conversion<{ errors, warnings }; } + + /* Used for left|right group creation and replacement */ + private prepConfig(keyToRewrite: string, target: string, tr: TagRenderingConfigJson) { + + function replaceRecursive(transl: string | any) { + if (typeof transl === "string") { + return transl.replace(keyToRewrite, target) + } + if (transl.map !== undefined) { + return transl.map(o => replaceRecursive(o)) + } + transl = {...transl} + for (const key in transl) { + transl[key] = replaceRecursive(transl[key]) + } + return transl + } + + const orig = tr; + tr = replaceRecursive(tr) + + tr.id = target + "-" + orig.id + tr.group = target + return tr + } } @@ -406,10 +414,10 @@ export class UpdateLegacyLayer extends DesugaringStep { } - if (this._isBuiltin ) { + if (this._isBuiltin) { if (json.tagRenderings?.some(tr => tr["id"] === "")) { - const emptyIndexes : number[] = [] - for (let i = 0; i < json.tagRenderings.length; i++){ + const emptyIndexes: number[] = [] + for (let i = 0; i < json.tagRenderings.length; i++) { const tagRendering = json.tagRenderings[i]; - if(tagRendering["id"] === ""){ + if (tagRendering["id"] === "") { emptyIndexes.push(i) } } @@ -634,19 +642,20 @@ export class ValidateLayer extends DesugaringStep { if (duplicateIds.length > 0 && !Utils.runningFromConsole) { errors.push(`Some tagRenderings have a duplicate id: ${duplicateIds} (at ${context}.tagRenderings)`) } - - - if(json.description === undefined){ - - if (Constants.priviliged_layers.indexOf(json.id) >= 0) { - errors.push( - context + ": A priviliged layer must have a description" - ) - } else { - warnings.push( - context + ": A builtin layer should have a description" - ) - }} + + + if (json.description === undefined) { + + if (Constants.priviliged_layers.indexOf(json.id) >= 0) { + errors.push( + context + ": A priviliged layer must have a description" + ) + } else { + warnings.push( + context + ": A builtin layer should have a description" + ) + } + } } } catch (e) { errors.push(e) @@ -774,7 +783,7 @@ class AddDependencyLayersToTheme extends DesugaringStep { private static CalculateDependencies(alreadyLoaded: LayerConfigJson[], allKnownLayers: Map, themeId: string): LayerConfigJson[] { const dependenciesToAdd: LayerConfigJson[] = [] const loadedLayerIds: Set = new Set(alreadyLoaded.map(l => l.id)); - + // Verify cross-dependencies let unmetDependencies: { neededLayer: string, neededBy: string, reason: string, context?: string }[] = [] do { @@ -789,7 +798,7 @@ class AddDependencyLayersToTheme extends DesugaringStep { // Their existance is checked elsewhere, so this is fine unmetDependencies = dependencies.filter(dep => !loadedLayerIds.has(dep.neededLayer)) for (const unmetDependency of unmetDependencies) { - if(loadedLayerIds.has(unmetDependency.neededLayer)){ + if (loadedLayerIds.has(unmetDependency.neededLayer)) { continue } const dep = allKnownLayers.get(unmetDependency.neededLayer) @@ -825,9 +834,9 @@ class AddDependencyLayersToTheme extends DesugaringStep { }) const dependencies = AddDependencyLayersToTheme.CalculateDependencies(layers, allKnownLayers, theme.id); - if(dependencies.length > 0){ - - warnings.push(context+": added "+dependencies.map(d => d.id).join(", ")+" to the theme as they are needed") + if (dependencies.length > 0) { + + warnings.push(context + ": added " + dependencies.map(d => d.id).join(", ") + " to the theme as they are needed") } layers.unshift(...dependencies); @@ -842,12 +851,36 @@ class AddDependencyLayersToTheme extends DesugaringStep { } } +class SetDefault extends DesugaringStep { + private readonly value: object; + private readonly key: string; + + constructor(key: string, value: object) { + super("Sets " + key + " to a default value if undefined"); + this.key = key; + this.value = value; + } + + convert(state: DesugaringContext, json: LayerConfigJson, context: string): { result: LayerConfigJson; errors: string[]; warnings: string[] } { + if (json[this.key] === undefined) { + json = {...json} + json[this.key] = this.value + } + + return { + errors: [], warnings: [], + result: json + }; + } +} + export class PrepareLayer extends Fuse { constructor() { super( "Fully prepares and expands a layer for the LayerConfig.", new OnEveryConcat("tagRenderings", new ExpandGroupRewrite()), new OnEveryConcat("tagRenderings", new ExpandTagRendering()), + new SetDefault("titleIcons", ["defaults"]), new OnEveryConcat("titleIcons", new ExpandTagRendering()) ); } @@ -888,8 +921,15 @@ class SubstituteLayer extends Conversion<(string | LayerConfigJson), LayerConfig errors.push(context + ": The layer with name " + json + " was not found as a builtin layer") continue } - Utils.Merge(json["override"], found); - layers.push(found) + if (json["override"]["tagRenderings"] !== undefined && (found["tagRenderings"] ?? []).length > 0) { + errors.push(`At ${context}: when overriding a layer, an override is not allowed to override into tagRenderings. Use "+tagRenderings" or "tagRenderings+" instead to prepend or append some questions.`) + } + try { + Utils.Merge(json["override"], found); + layers.push(found) + } catch (e) { + errors.push(`At ${context}: could not apply an override due to: ${e}.\nThe override is: ${JSON.stringify(json["override"],)}`) + } } return { result: layers, @@ -906,19 +946,25 @@ class SubstituteLayer extends Conversion<(string | LayerConfigJson), LayerConfig } -class AddDefaultLayers extends DesugaringStep{ - +class AddDefaultLayers extends DesugaringStep { + constructor() { - super("Adds the default layers, namely: "+Constants.added_by_default.join(", "),["layers"]); + super("Adds the default layers, namely: " + Constants.added_by_default.join(", "), ["layers"]); } - + convert(state: DesugaringContext, json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } { const errors = [] json.layers = [...json.layers] + + if (json.id === "personal") { + const publicIds = AllKnownLayouts.AllPublicLayers().map(l => l.id) + json.layers = publicIds.map(id => state.sharedLayers.get(id)) + } + for (const layerName of Constants.added_by_default) { const v = state.sharedLayers.get(layerName) - if(v === undefined){ - errors.push("Default layer "+layerName+" not found") + if (v === undefined) { + errors.push("Default layer " + layerName + " not found") } json.layers.push(v) } @@ -928,7 +974,7 @@ class AddDefaultLayers extends DesugaringStep{ warnings: [] }; } - + } export class PrepareTheme extends Fuse { @@ -939,7 +985,6 @@ export class PrepareTheme extends Fuse { new AddDefaultLayers(), new AddDependencyLayersToTheme(), new OnEvery("layers", new PrepareLayer()), - ); } } \ No newline at end of file diff --git a/Models/ThemeConfig/Json/LayerConfigJson.ts b/Models/ThemeConfig/Json/LayerConfigJson.ts index f570d627c3..6cc02f1956 100644 --- a/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -140,9 +140,9 @@ export interface LayerConfigJson { * Small icons shown next to the title. * If not specified, the OsmLink and wikipedia links will be used by default. * Use an empty array to hide them. - * Note that "defaults" will insert all the default titleIcons + * Note that "defaults" will insert all the default titleIcons (which are added automatically) */ - titleIcons?: (string | TagRenderingConfigJson)[]; + titleIcons?: (string | TagRenderingConfigJson)[] | ["defaults"]; mapRendering: null | (PointRenderingConfigJson | LineRenderingConfigJson)[] diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index dbe7c7d752..9385ee57f5 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -67,6 +67,10 @@ export default class LayerConfig extends WithContextLoader { context = context + "." + json.id; super(json, context) this.id = json.id; + + if(json.id === undefined){ + throw "Not a valid layer: id is undefined: "+JSON.stringify(json) + } if (json.source === undefined) { throw "Layer " + this.id + " does not define a source section (" + context + ")" diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index d26d5cee11..bd70be9b8a 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -23,8 +23,8 @@ export default class EditableTagRendering extends Toggle { ) { // The tagrendering is hidden if: - // The answer is unknown. The questionbox will then show the question - // There is a condition hiding the answer + // - The answer is unknown. The questionbox will then show the question + // - There is a condition hiding the answer const renderingIsShown = tags.map(tags => configuration.IsKnown(tags) && (configuration?.condition?.matchesProperties(tags) ?? true)) diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index f893b1070a..f22f389307 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -216,7 +216,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen { /** * Returns true if this tag rendering has a minimap in some language. - * Note: this might be hidden by conditions + * Note: this minimap can be hidden by conditions */ private static hasMinimap(renderingConfig: TagRenderingConfig): boolean { const translations: Translation[] = Utils.NoNull([renderingConfig.render, ...(renderingConfig.mappings ?? []).map(m => m.then)]); diff --git a/UI/Popup/QuestionBox.ts b/UI/Popup/QuestionBox.ts index 5a857eef20..6ca5a59694 100644 --- a/UI/Popup/QuestionBox.ts +++ b/UI/Popup/QuestionBox.ts @@ -68,10 +68,11 @@ export default class QuestionBox extends VariableUiElement { if (tagRendering.IsKnown(tags)) { continue; } - if (tagRendering.condition && - !tagRendering.condition.matchesProperties(tags)) { - // Filtered away by the condition, so it is kindof known - continue; + if (tagRendering.condition) { + if (!tagRendering.condition.matchesProperties(tags)) { + // Filtered away by the condition, so it is kindof known + continue; + } } // this value is NOT known - this is the question we have to show! @@ -79,7 +80,7 @@ export default class QuestionBox extends VariableUiElement { } return undefined; // The questions are depleted }, [skippedQuestions]); - + const questionsToAsk: UIEventSource = tagsSource.map(tags => { if (tags === undefined) { return []; diff --git a/UI/ShowDataLayer/ShowDataLayer.ts b/UI/ShowDataLayer/ShowDataLayer.ts index 0640c22044..27ed05f150 100644 --- a/UI/ShowDataLayer/ShowDataLayer.ts +++ b/UI/ShowDataLayer/ShowDataLayer.ts @@ -148,11 +148,12 @@ export default class ShowDataLayer { const mp = options.leafletMap.data; if(mp === null){ - return true; // Unregister as the map is destroyed + return true; // Unregister as the map has been destroyed } if (mp === undefined) { return; } + this._cleanCount++ // clean all the old stuff away, if any if (this.geoLayer !== undefined) { diff --git a/Utils.ts b/Utils.ts index 9192367004..103b4f949e 100644 --- a/Utils.ts +++ b/Utils.ts @@ -269,6 +269,9 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be v = "" + v } v = v.replace(/\n/g, "
") + }else{ + // v === undefined + v = "" } txt = txt.replace("{" + key + "}", v) match = txt.match(regex) diff --git a/assets/themes/bookcases/bookcases.json b/assets/themes/bookcases/bookcases.json index 5f6e14c565..03747813ed 100644 --- a/assets/themes/bookcases/bookcases.json +++ b/assets/themes/bookcases/bookcases.json @@ -43,7 +43,6 @@ "startZoom": 1, "widenFactor": 1, "layers": [ - "public_bookcase", - "note_import" + "public_bookcase" ] } \ No newline at end of file diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index 46cafc6cec..7fcaf70e9c 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -30,28 +30,6 @@ "minzoom": 19 }, "layers": [ - { - "builtin": "current_view", - "override": { - "+mapRendering": [ - { - "location": [ - "point" - ], - "icon": { - "render": "./assets/svg/robot.svg" - }, - "iconSize": "15,15,center" - } - ], - "tagRenderings": [ - { - "id": "hw", - "render": "Beep boop! I'm a bot!" - } - ] - } - }, { "builtin": "type_node", "override": { diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index 43c13c03f9..392eabd718 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -304,7 +304,7 @@ } ] }, - "iconSize": "30,30,bottom" + "iconSize": "30,30,center" } ], "filter": [ diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index 31d4ab4655..07d1db5f09 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -189,15 +189,46 @@ "description": "Alamat" }, { - "builtin": "address", - "override": { - "calculatedTags": [ - "_closest_3_street_names=feat.closestn('named_streets',3, 'name').map(f => f.feat.properties.name)", - "_closest_street:0:name=JSON.parse(feat.properties._closest_3_street_names)[0]", - "_closest_street:1:name=JSON.parse(feat.properties._closest_3_street_names)[1]", - "_closest_street:2:name=JSON.parse(feat.properties._closest_3_street_names)[2]" - ] + "id": "address", + "name": { + "en": "Known addresses in OSM", + "de": "Bekannte Adressen in OSM", + "zh_Hant": "OSM 上已知的地址", + "hu": "Ismert címek az OSM-en", + "nl": "Bekende adressen in OSM" }, + "minzoom": 18, + "source": { + "osmTags": { + "or": [ + "addr:housenumber~*", + "addr:street~*", + "ref:inspireid~*" + ] + } + }, + "title": { + "render": { + "en": "Known address", + "de": "Bekannte Adresse", + "hu": "Ismert cím", + "nl": "Bekend adres" + } + }, + "description": { + "en": "Addresses", + "nl": "Adressen", + "de": "Adressen", + "ru": "Адреса", + "zh_Hant": "地址", + "hu": "Címek" + }, + "calculatedTags": [ + "_closest_3_street_names=feat.closestn('named_streets',3, 'name').map(f => f.feat.properties.name)", + "_closest_street:0:name=JSON.parse(feat.properties._closest_3_street_names)[0]", + "_closest_street:1:name=JSON.parse(feat.properties._closest_3_street_names)[1]", + "_closest_street:2:name=JSON.parse(feat.properties._closest_3_street_names)[2]" + ], "tagRenderings": [ { "id": "uk_addresses_explanation_osm", @@ -265,6 +296,24 @@ } ] }, + { + "id": "uk_addresses_unit", + "render": "The sub-part of this address is {addr:unit}", + "question": { + "en": "What is the unit indication of this address?
This is the letter or number of the letterbox here, if multiple letterboxes share the same street and housenumber. If there are multiple at the same location, add them here with a ; between them
" + }, + "freeform": { + "key": "addr:unit" + }, + "mappings": [{ + "if": "addr:unit=", + "then": "This address has no subparts.
Subparts are e.g. appartment numbers, extra letters or numbers if there are multiple letterboxes, ...
" + } + ], + "condition": { + "or": ["addr:housenumber~*","addr:housename~*"] + } + }, { "id": "uk_addresses_street", "render": { @@ -281,31 +330,27 @@ "mappings": [ { "if": "addr:street:={_closest_street:0:name}", - "then": "Located in {_closest_street:0:name}", + "then": "This address is in street {_closest_street:0:name}", "hideInAnswer": "_closest_street:0:name=" }, { "if": "addr:street:={_closest_street:1:name}", - "then": "Located in {_closest_street:1:name}", + "then": "This address is in street {_closest_street:1:name}", "hideInAnswer": "_closest_street:1:name=" }, { "if": "addr:street:={_closest_street:2:name}", - "then": "Located in {_closest_street:2:name}", + "then": "This address is in street {_closest_street:2:name}", "hideInAnswer": "_closest_street:2:name=" } - ], - "condition": { - "and": [ - "nohousenumber!~yes" - ] - } + ] + }, { "id": "fixme", - "render": "Fixme description{fixme}", + "render": "This address is complicated - therefore someone has given a description of what should be fixed:{fixme}", "question": { - "en": "What should be fixed here? Please explain" + "en": "What should be fixed here? Please explain what the address is" }, "freeform": { "key": "fixme" @@ -313,7 +358,7 @@ "mappings": [ { "if": "fixme=", - "then": "No fixme - write something here to explain complicated cases" + "then": "Is this address very complicated? - write something here to explain complicated cases" } ] }, @@ -324,6 +369,62 @@ "en": "{image_carousel(image:address)}
{image_upload(image:address, Add image of the address)}" } } + ], + "mapRendering": [ + { + "label": { + "render": "
{addr:housenumber}
", + "condition": "addr:housenumber~*" + }, + "iconSize": "50,50,center", + "icon": { + "render": "./assets/layers/address/housenumber_blank.svg", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" + ] + }, + "then": "./assets/themes/uk_addresses/housenumber_unknown.svg" + } + ] + }, + "location": [ + "point", + "centroid" + ] + }, + { + "color": { + "render": "#00f", + "mappings": [ + { + "if": { + "or": [ + { + "and": [ + "addr:housenumber=", + "nohousenumber!=yes" + ] + }, + "addr:street=" + ] + }, + "then": "#ff0" + } + ] + }, + "width": { + "render": "8" + } + } ] }, "named_streets" diff --git a/langs/themes/de.json b/langs/themes/de.json index 47f2612909..40f8d193ce 100644 --- a/langs/themes/de.json +++ b/langs/themes/de.json @@ -1083,6 +1083,8 @@ "description": "Tragen Sie zu OpenStreetMap bei, indem Sie Adressinformationen ausfüllen", "layers": { "2": { + "description": "Adressen", + "name": "Bekannte Adressen in OSM", "tagRenderings": { "uk_addresses_explanation_osm": { "render": "Diese Adresse ist in OpenStreetMap gespeichert" @@ -1100,6 +1102,9 @@ "question": "In welcher Straße befindet sich diese Adresse?", "render": "Diese Adresse befindet sich in der Straße {addr:street}" } + }, + "title": { + "render": "Bekannte Adresse" } } }, diff --git a/langs/themes/en.json b/langs/themes/en.json index 9d45eeca75..452d3a19b4 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -967,6 +967,9 @@ "contributor": { "render": "Change made by {_last_edit:contributor}" }, + "render_id": { + "render": "Changeset {id}" + }, "theme": { "mappings": { "0": { @@ -1289,12 +1292,14 @@ } }, "2": { + "description": "Addresses", + "name": "Known addresses in OSM", "tagRenderings": { "address-sign-image": { "render": "{image_carousel(image:address)}
{image_upload(image:address, Add image of the address)}" }, "fixme": { - "question": "What should be fixed here? Please explain" + "question": "What should be fixed here? Please explain what the address is" }, "uk_addresses_explanation_osm": { "render": "This address is saved in OpenStreetMap" @@ -1312,6 +1317,9 @@ "question": "What street is this address located in?", "render": "This address is in street {addr:street}" } + }, + "title": { + "render": "Known address" } } }, diff --git a/langs/themes/hu.json b/langs/themes/hu.json index bf4ccc15b4..f62bd8cbd4 100644 --- a/langs/themes/hu.json +++ b/langs/themes/hu.json @@ -8,5 +8,16 @@ }, "ghostbikes": { "title": "Emlékkerékpárok" + }, + "uk_addresses": { + "layers": { + "2": { + "description": "Címek", + "name": "Ismert címek az OSM-en", + "title": { + "render": "Ismert cím" + } + } + } } } \ No newline at end of file diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 19d39adf75..e01e583873 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -1060,6 +1060,8 @@ "description": "Draag bij aan OpenStreetMap door adresinformatie in te vullen", "layers": { "2": { + "description": "Adressen", + "name": "Bekende adressen in OSM", "tagRenderings": { "uk_addresses_housenumber": { "mappings": { @@ -1069,6 +1071,9 @@ }, "render": "Het huisnummer is {addr:housenumber}" } + }, + "title": { + "render": "Bekend adres" } } } diff --git a/langs/themes/ru.json b/langs/themes/ru.json index fdc231a61f..b6166629c3 100644 --- a/langs/themes/ru.json +++ b/langs/themes/ru.json @@ -530,5 +530,12 @@ "description": "Нанесите все деревья на карту!", "shortDescription": "Карта деревьев", "title": "Деревья" + }, + "uk_addresses": { + "layers": { + "2": { + "description": "Адреса" + } + } } } \ No newline at end of file diff --git a/langs/themes/zh_Hant.json b/langs/themes/zh_Hant.json index 69b33da56d..1a4652326b 100644 --- a/langs/themes/zh_Hant.json +++ b/langs/themes/zh_Hant.json @@ -655,6 +655,14 @@ "shortDescription": "所有樹木的地圖", "title": "樹木" }, + "uk_addresses": { + "layers": { + "2": { + "description": "地址", + "name": "OSM 上已知的地址" + } + } + }, "waste_basket": { "description": "在這份地圖當中,你可以找到你附近的垃圾筒。如果地圖有遺漏垃圾筒,你可以自己加上去", "shortDescription": "垃圾筒的地圖",