From 7eeec900d8f7be13753d3333c82911a1b3d5af0c Mon Sep 17 00:00:00 2001 From: riQQ Date: Thu, 17 Feb 2022 00:53:47 +0100 Subject: [PATCH 1/5] Editorial improvements in architecture docs --- Docs/Architecture.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Docs/Architecture.md b/Docs/Architecture.md index 69a08018f7..b6446884bd 100644 --- a/Docs/Architecture.md +++ b/Docs/Architecture.md @@ -84,9 +84,9 @@ To add a translation: ### Input elements -Input elements are a special kind of BaseElement and which offer a piece of a form to the user, e.g. a TextField, a Radio button, a dropdown, ... +Input elements are a special kind of BaseElement which offer a piece of a form to the user, e.g. a TextField, a Radio button, a dropdown, ... -The constructor will ask all the parameters to configure them. The actual value can be obtained via `inputElement.GetValue()`, which is a UIEVentSource that will be triggered every time the user changes the input. +The constructor will ask all the parameters to configure them. The actual value can be obtained via `inputElement.GetValue()`, which is a `UIEventSource` that will be triggered every time the user changes the input. ### Advanced elements @@ -215,7 +215,7 @@ Other files (mostly images that are part of the core of MapComplete) go into `as Logic ----- -The last part is the business logic of the application, found in 'Logic'. Actors are small objects which react to `UIEventSources` to update other eventSources. +The last part is the business logic of the application, found in the directory [Logic](../Logic). Actors are small objects which react to `UIEventSources` to update other eventSources. `State.state` is a big singleton object containing a lot of the state of the entire application. That one is a bit of a mess. From 2e6069b95b9eaff8a22aca7f58fcef4adb34e2cf Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 17 Feb 2022 23:54:14 +0100 Subject: [PATCH 2/5] Add icon size options to mapping icons --- Models/ThemeConfig/Conversion/Conversion.ts | 2 +- Models/ThemeConfig/Conversion/FixImages.ts | 6 +- Models/ThemeConfig/Conversion/PrepareTheme.ts | 2 +- Models/ThemeConfig/Conversion/Validation.ts | 87 +++++++++++++++---- .../Json/TagRenderingConfigJson.ts | 13 ++- Models/ThemeConfig/TagRenderingConfig.ts | 17 ++-- UI/Popup/TagRenderingAnswer.ts | 2 +- UI/Popup/TagRenderingQuestion.ts | 8 +- css/index-tailwind-output.css | 39 ++++++--- index.css | 27 ++++++ 10 files changed, 159 insertions(+), 44 deletions(-) diff --git a/Models/ThemeConfig/Conversion/Conversion.ts b/Models/ThemeConfig/Conversion/Conversion.ts index 2926fb3de8..051f004652 100644 --- a/Models/ThemeConfig/Conversion/Conversion.ts +++ b/Models/ThemeConfig/Conversion/Conversion.ts @@ -130,7 +130,7 @@ export class Fuse extends DesugaringStep { Utils.Dedup([].concat(...steps.map(step => step.modifiedAttributes))), "Fuse of "+steps.map(s => s.name).join(", ") ); - this.steps = steps; + this.steps = Utils.NoNull(steps); } convert(json: T, context: string): { result: T; errors: string[]; warnings: string[], information: string[] } { diff --git a/Models/ThemeConfig/Conversion/FixImages.ts b/Models/ThemeConfig/Conversion/FixImages.ts index 79a3610594..ef315fd952 100644 --- a/Models/ThemeConfig/Conversion/FixImages.ts +++ b/Models/ThemeConfig/Conversion/FixImages.ts @@ -32,7 +32,7 @@ export class ExtractImages extends Conversion { for (const foundImage of found) { if (typeof foundImage === "string") { allFoundImages.push(foundImage) - } else { + } else{ // This is a tagRendering where every rendered value might be an icon! for (const trpath of trpaths) { if (trpath.typeHint !== "rendered") { @@ -54,7 +54,9 @@ export class ExtractImages extends Conversion { } } - const splitParts = [].concat(...Utils.NoNull(allFoundImages).map(img => img.split(";"))) + const splitParts = [].concat(...Utils.NoNull(allFoundImages) + .map(img => img["path"] ?? img) + .map(img => img.split(";"))) .map(img => img.split(":")[0]) return {result: Utils.Dedup(splitParts), errors, warnings}; } diff --git a/Models/ThemeConfig/Conversion/PrepareTheme.ts b/Models/ThemeConfig/Conversion/PrepareTheme.ts index b79f118b38..6dbbb70d39 100644 --- a/Models/ThemeConfig/Conversion/PrepareTheme.ts +++ b/Models/ThemeConfig/Conversion/PrepareTheme.ts @@ -16,7 +16,7 @@ class SubstituteLayer extends Conversion<(string | LayerConfigJson), LayerConfig constructor( state: DesugaringContext, ) { - super("Converts the identifier of a builtin layer into the actual layer, or converts a 'builtin' syntax with override in the fully expanded form", [],"SubstuteLayers"); + super("Converts the identifier of a builtin layer into the actual layer, or converts a 'builtin' syntax with override in the fully expanded form", [],"SubstituteLayer"); this._state = state; } diff --git a/Models/ThemeConfig/Conversion/Validation.ts b/Models/ThemeConfig/Conversion/Validation.ts index 6837c5f387..398a78268a 100644 --- a/Models/ThemeConfig/Conversion/Validation.ts +++ b/Models/ThemeConfig/Conversion/Validation.ts @@ -11,6 +11,7 @@ import {TagUtils} from "../../../Logic/Tags/TagUtils"; import {ExtractImages} from "./FixImages"; import ScriptUtils from "../../../scripts/ScriptUtils"; import {And} from "../../../Logic/Tags/And"; +import Translations from "../../../UI/i18n/Translations"; class ValidateLanguageCompleteness extends DesugaringStep { @@ -51,7 +52,7 @@ class ValidateTheme extends DesugaringStep { private readonly _isBuiltin: boolean; constructor(knownImagePaths: Set, path: string, isBuiltin: boolean) { - super("Doesn't change anything, but emits warnings and errors", [],"ValidateTheme"); + super("Doesn't change anything, but emits warnings and errors", [], "ValidateTheme"); this.knownImagePaths = knownImagePaths; this._path = path; this._isBuiltin = isBuiltin; @@ -61,6 +62,9 @@ class ValidateTheme extends DesugaringStep { const errors = [] const warnings = [] const information = [] + + const theme = new LayoutConfig(json, true, "test") + { // Legacy format checks if (this._isBuiltin) { @@ -105,7 +109,7 @@ class ValidateTheme extends DesugaringStep { const width: string = svg.$.width; const height: string = svg.$.height; if (width !== height) { - const e = `the icon for theme ${json.id} is not square. Please square the icon at ${json.icon}` + + const e = `the icon for theme ${json.id} is not square. Please square the icon at ${json.icon}` + ` Width = ${width} height = ${height}`; (json.hideFromOverview ? warnings : errors).push(e) } @@ -116,8 +120,8 @@ class ValidateTheme extends DesugaringStep { } } + try { - const theme = new LayoutConfig(json, true, "test") if (theme.id !== theme.id.toLowerCase()) { errors.push("Theme ids should be in lowercase, but it is " + theme.id) } @@ -138,7 +142,7 @@ class ValidateTheme extends DesugaringStep { .convert(theme, theme.id) errors.push(...checked.errors) } - if(!json.hideFromOverview && theme.id !== "personal"){ + if (!json.hideFromOverview && theme.id !== "personal") { // Official, public themes must have a full english translation const checked = new ValidateLanguageCompleteness("en") .convert(theme, theme.id) @@ -171,7 +175,7 @@ export class ValidateThemeAndLayers extends Fuse { class OverrideShadowingCheck extends DesugaringStep { constructor() { - super("Checks that an 'overrideAll' does not override a single override",[],"OverrideShadowingCheck"); + super("Checks that an 'overrideAll' does not override a single override", [], "OverrideShadowingCheck"); } convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[]; warnings?: string[] } { @@ -211,11 +215,12 @@ export class PrevalidateTheme extends Fuse { export class DetectShadowedMappings extends DesugaringStep { constructor() { - super("Checks that the mappings don't shadow each other",[],"DetectShadowedMappings"); + super("Checks that the mappings don't shadow each other", [], "DetectShadowedMappings"); } convert(json: TagRenderingConfigJson, context: string): { result: TagRenderingConfigJson; errors?: string[]; warnings?: string[] } { const errors = [] + const warnings = [] if (json.mappings === undefined || json.mappings.length === 0) { return {result: json} } @@ -230,10 +235,13 @@ export class DetectShadowedMappings extends DesugaringStep { + constructor() { + super("Checks that 'then'clauses in mappings don't have images, but use 'icon' instead", [], "DetectMappingsWithImages"); + } + + convert(json: TagRenderingConfigJson, context: string): { result: TagRenderingConfigJson; errors?: string[]; warnings?: string[] } { + const warnings = [] + if (json.mappings === undefined || json.mappings.length === 0) { + return {result: json} + } + for (let i = 0; i < json.mappings.length; i++) { + + const mapping = json.mappings[i] + const images = Utils.Dedup(Translations.T(mapping.then).ExtractImages()) + if (images.length > 0) { + warnings.push(context + ".mappings[" + i + "]: A mapping has an image in the 'then'-clause. Remove the image there and use `\"icon\": ` instead. The images found are "+images.join(", ")) + } + } + + return { + warnings, + result: json + }; + } +} + +export class ValidateTagRenderings extends Fuse { + constructor() { + super("Various validation on tagRenderingConfigs", + // TODO enable these checks again + // new DetectShadowedMappings(), + // new DetectMappingsWithImages() e + ); + } +} + export class ValidateLayer extends DesugaringStep { /** * The paths where this layer is originally saved. Triggers some extra checks @@ -261,16 +306,16 @@ export class ValidateLayer extends DesugaringStep { private readonly _isBuiltin: boolean; constructor(knownImagePaths: Set, path: string, isBuiltin: boolean) { - super("Doesn't change anything, but emits warnings and errors", [],"ValidateLayer"); + super("Doesn't change anything, but emits warnings and errors", [], "ValidateLayer"); this.knownImagePaths = knownImagePaths; this._path = path; this._isBuiltin = isBuiltin; } - convert(json: LayerConfigJson, context: string): { result: LayerConfigJson; errors: string[]; warnings?: string[] } { + convert(json: LayerConfigJson, context: string): { result: LayerConfigJson; errors: string[]; warnings?: string[], information?: string[] } { const errors = [] const warnings = [] - + const information = [] if (typeof json === "string") { errors.push(context + ": This layer hasn't been expanded: " + json) return { @@ -343,23 +388,26 @@ export class ValidateLayer extends DesugaringStep { } } if (json.tagRenderings !== undefined) { - new DetectShadowedMappings().convertAll(json.tagRenderings, context + ".tagRenderings") + const r = new OnEvery("tagRenderings", new ValidateTagRenderings()).convert(json, context) + warnings.push(...(r.warnings??[])) + errors.push(...(r.errors??[])) + information.push(...(r.information??[])) } - if(json.presets !== undefined){ - + if (json.presets !== undefined) { + // Check that a preset will be picked up by the layer itself - const baseTags = TagUtils.Tag( json.source.osmTags) - for (let i = 0; i < json.presets.length; i++){ + const baseTags = TagUtils.Tag(json.source.osmTags) + for (let i = 0; i < json.presets.length; i++) { const preset = json.presets[i]; - const tags : {k: string,v: string}[]= new And(preset.tags.map(t => TagUtils.Tag(t))).asChange({id:"node/-1"}) + const tags: { k: string, v: string }[] = new And(preset.tags.map(t => TagUtils.Tag(t))).asChange({id: "node/-1"}) const properties = {} for (const tag of tags) { properties[tag.k] = tag.v } const doMatch = baseTags.matchesProperties(properties) - if(!doMatch){ - errors.push(context+".presets["+i+"]: This preset does not match the required tags of this layer. This implies that a newly added point will not show up.\n A newly created point will have properties: "+JSON.stringify(properties)+"\n The required tags are: "+baseTags.asHumanString(false, false, {})) + if (!doMatch) { + errors.push(context + ".presets[" + i + "]: This preset does not match the required tags of this layer. This implies that a newly added point will not show up.\n A newly created point will have properties: " + JSON.stringify(properties) + "\n The required tags are: " + baseTags.asHumanString(false, false, {})) } } } @@ -372,7 +420,8 @@ export class ValidateLayer extends DesugaringStep { return { result: json, errors, - warnings + warnings, + information }; } } \ No newline at end of file diff --git a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts index f1aee4ff60..36ccbf3cce 100644 --- a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts @@ -122,7 +122,18 @@ export interface TagRenderingConfigJson { * An icon supporting this mapping; typically shown pretty small * Type: icon */ - icon?: string + icon?: string | { + /** + * The path to the icon + * Type: icon + */ + path: string, + /** + * A hint to mapcomplete on how to render this icon within the mapping. + * This is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged) + */ + class: "small" | "medium" | "large" | string + } /** * In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation). * diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index 11d17dee9e..5c1c9b28c2 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -44,6 +44,7 @@ export default class TagRenderingConfig { readonly ifnot?: TagsFilter, readonly then: Translation, readonly icon: string, + readonly iconClass: string readonly hideInAnswer: boolean | TagsFilter readonly addExtraTags: Tag[] }[] @@ -180,8 +181,14 @@ export default class TagRenderingConfig { hideInAnswer = TagUtils.Tag(mapping.hideInAnswer, `${context}.mapping[${i}].hideInAnswer`); } let icon = undefined; - if (mapping.icon !== "") { - icon = mapping.icon + let iconClass = "small" + if(mapping.icon !== undefined){ + if (typeof mapping.icon === "string" && mapping.icon !== "") { + icon = mapping.icon + }else{ + icon = mapping.icon["path"] + iconClass = mapping.icon["class"] ?? iconClass + } } const mp = { if: TagUtils.Tag(mapping.if, `${ctx}.if`), @@ -189,6 +196,7 @@ export default class TagRenderingConfig { then: Translations.T(mapping.then, `${ctx}.then`), hideInAnswer, icon, + iconClass, addExtraTags: (mapping.addExtraTags ?? []).map((str, j) => TagUtils.SimpleTag(str, `${ctx}.addExtraTags[${j}]`)) }; if (this.question) { @@ -350,7 +358,7 @@ export default class TagRenderingConfig { * @param tags * @constructor */ - public GetRenderValues(tags: any): { then: Translation, icon?: string }[] { + public GetRenderValues(tags: any): { then: Translation, icon?: string, iconClass?: string }[] { if (!this.multiAnswer) { return [this.GetRenderValueWithImage(tags)] } @@ -399,9 +407,6 @@ export default class TagRenderingConfig { return mapping; } if (mapping.if.matchesProperties(tags)) { - if (this.id === "uk_addresses_placename") { - console.log("Matched", mapping.if, "with ", tags["addr:place"]) - } return mapping; } } diff --git a/UI/Popup/TagRenderingAnswer.ts b/UI/Popup/TagRenderingAnswer.ts index 0d155384de..ffedccefc0 100644 --- a/UI/Popup/TagRenderingAnswer.ts +++ b/UI/Popup/TagRenderingAnswer.ts @@ -45,7 +45,7 @@ export default class TagRenderingAnswer extends VariableUiElement { if(tr.icon === undefined){ return text } - return new Combine([new Img(tr.icon).SetClass("w-6 max-h-6 pr-2"), text]).SetClass("flex") + return new Combine([new Img(tr.icon).SetClass("mapping-icon-"+(tr.iconClass ?? "small")), text]).SetClass("flex items-center") }) if (valuesToRender.length === 1) { return valuesToRender[0]; diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 88e0a751ad..39736a58d4 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -355,7 +355,8 @@ export default class TagRenderingQuestion extends Combine { if: TagsFilter, then: Translation, addExtraTags: Tag[], - img?: string + icon?: string, + iconClass?: string }, ifNot?: TagsFilter[]): InputElement { let tagging: TagsFilter = mapping.if; @@ -375,13 +376,14 @@ export default class TagRenderingQuestion extends Combine { private static GenerateMappingContent(mapping: { then: Translation, - icon?: string + icon?: string, + iconClass?: string }, tagsSource: UIEventSource, state: FeaturePipelineState): BaseUIElement { const text = new SubstitutedTranslation(mapping.then, tagsSource, state) if (mapping.icon === undefined) { return text; } - return new Combine([new Img(mapping.icon).SetClass("w-6 max-h-6 pr-2"), text]).SetClass("flex") + return new Combine([new Img(mapping.icon).SetClass("mapping-icon-"+(mapping.iconClass ?? "small")), text]).SetClass("flex") } private static GenerateFreeform(state, configuration: TagRenderingConfig, applicableUnit: Unit, tags: UIEventSource, feedback: UIEventSource) diff --git a/css/index-tailwind-output.css b/css/index-tailwind-output.css index 0872c2d754..ca9b1f4735 100644 --- a/css/index-tailwind-output.css +++ b/css/index-tailwind-output.css @@ -851,11 +851,6 @@ video { margin-bottom: 0.75rem; } -.mx-4 { - margin-left: 1rem; - margin-right: 1rem; -} - .ml-3 { margin-left: 0.75rem; } @@ -1178,10 +1173,6 @@ video { min-width: min-content; } -.min-w-\[20em\] { - min-width: 20em; -} - .max-w-full { max-width: 100%; } @@ -1781,6 +1772,10 @@ video { filter: var(--tw-filter); } +.\!filter { + filter: var(--tw-filter) !important; +} + .transition { transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; @@ -2405,6 +2400,31 @@ input { /* Additional class on the first layer filter */ } +.mapping-icon-small { + /* A mapping icon type */ + width: 1.5rem; + max-height: 1.5rem; + margin-right: 0.5rem; +} + +.mapping-icon-medium { + /* A mapping icon type */ + width: 3rem; + max-height: 3rem; + margin-right: 1rem; + margin-left: 1rem; +} + +.mapping-icon-large{ + /* A mapping icon type */ + width: 6rem; + max-height: 5rem; + margin-top: 0.5rem; + margin-bottom: 0.5rem; + margin-right: 1.5rem; + margin-left: 1.5rem; +} + .hover\:bg-indigo-200:hover { --tw-bg-opacity: 1; background-color: rgba(199, 210, 254, var(--tw-bg-opacity)); @@ -2696,4 +2716,3 @@ input { display: inline; } } - diff --git a/index.css b/index.css index 7a87c449d9..80320a95fc 100644 --- a/index.css +++ b/index.css @@ -588,3 +588,30 @@ input { /* Additional class on the first layer filter */ } + +.mapping-icon-small { + /* A mapping icon type */ + width: 1.5rem; + max-height: 1.5rem; + margin-right: 0.5rem; +} + +.mapping-icon-medium { + /* A mapping icon type */ + width: 3rem; + max-height: 3rem; + margin-right: 1rem; + margin-left: 1rem; +} + +.mapping-icon-large{ + /* A mapping icon type */ + width: 6rem; + max-height: 5rem; + margin-top: 0.5rem; + margin-bottom: 0.5rem; + margin-right: 1.5rem; + margin-left: 1.5rem; + + +} From 64bfcccbdc286671b0de90e5212426a58978c237 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 17 Feb 2022 23:55:01 +0100 Subject: [PATCH 3/5] Add test for image detection --- test/LegacyThemeLoader.spec.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/test/LegacyThemeLoader.spec.ts b/test/LegacyThemeLoader.spec.ts index aff639d317..a2e8b63d72 100644 --- a/test/LegacyThemeLoader.spec.ts +++ b/test/LegacyThemeLoader.spec.ts @@ -3,7 +3,7 @@ import {FixLegacyTheme} from "../Models/ThemeConfig/Conversion/LegacyJsonConvert import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; import {TagRenderingConfigJson} from "../Models/ThemeConfig/Json/TagRenderingConfigJson"; import {AddMiniMap} from "../Models/ThemeConfig/Conversion/PrepareTheme"; -import {DetectShadowedMappings} from "../Models/ThemeConfig/Conversion/Validation"; +import {DetectMappingsWithImages, DetectShadowedMappings} from "../Models/ThemeConfig/Conversion/Validation"; import * as Assert from "assert"; import {FixImages} from "../Models/ThemeConfig/Conversion/FixImages"; @@ -449,7 +449,25 @@ export default class LegacyThemeLoaderSpec extends T { const fixedMapping = fixed.layers[0]["mapRendering"][0].iconBadges[0].then.mappings[0].then Assert.equal("https://raw.githubusercontent.com/seppesantens/MapComplete-Themes/main/VerkeerdeBordenDatabank/Something.svg", fixedMapping) - } ] + } ], + ["Images in 'thens' are detected", () => { + const r = new DetectMappingsWithImages().convert({ + "mappings": [ + { + "if": "bicycle_parking=stands", + "then": { + "en": "Staple racks ", + "nl": "Nietjes ", + "fr": "Arceaux ", + "gl": "De roda (Stands) ", + "de": "Fahrradbügel ", + "hu": "Korlát ", + "it": "Archetti ", + "zh_Hant": "單車架 " + } + }]}, "test"); + T.isTrue(r.warnings.length > 0, "No images found"); + }] ] ); } From 701050c93b040d6959b7fffff926c3abbab0a2f7 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 17 Feb 2022 23:56:04 +0100 Subject: [PATCH 4/5] Simplify image detection --- UI/Popup/TagRenderingQuestion.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 39736a58d4..db6271a1e7 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -168,10 +168,9 @@ export default class TagRenderingQuestion extends Combine { const ff = TagRenderingQuestion.GenerateFreeform(state, configuration, applicableUnit, tagsSource, feedback); - const hasImages = applicableMappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0 + const hasImages = applicableMappings.findIndex(mapping => mapping.then.icon !== undefined) >= 0 let inputEls: InputElement[]; - const ifNotsPresent = applicableMappings.some(mapping => mapping.ifnot !== undefined) function allIfNotsExcept(excludeIndex: number): TagsFilter[] { From f55e5cf26cb3ce07f4fa279d9928010e9bd52374 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 17 Feb 2022 23:56:53 +0100 Subject: [PATCH 5/5] Fix various issues in themes --- assets/layers/bike_parking/bike_parking.json | 15 --- assets/layers/birdhide/birdhide.json | 3 +- .../layers/nature_reserve/nature_reserve.json | 5 +- langs/layers/en.json | 115 ++++++++++++++++++ langs/layers/nl.json | 1 + 5 files changed, 121 insertions(+), 18 deletions(-) diff --git a/assets/layers/bike_parking/bike_parking.json b/assets/layers/bike_parking/bike_parking.json index 07ebe7f37f..00b6d02d68 100644 --- a/assets/layers/bike_parking/bike_parking.json +++ b/assets/layers/bike_parking/bike_parking.json @@ -272,21 +272,6 @@ "pt": "Estacionamento ao nível da superfície" }, "hideInAnswer": true - }, - { - "if": "location=rooftop", - "then": { - "en": "Rooftop parking", - "nl": "Dakparking", - "fr": "Parking sur un toit", - "hu": "Tetőparkoló", - "it": "Parcheggio sul tetto", - "ru": "Парковка на крыше", - "zh_Hant": "屋頂停車場", - "pt_BR": "Estacionamento no telhado", - "de": "Parkplatz auf dem Dach", - "pt": "Estacionamento no telhado" - } } ], "id": "Underground?" diff --git a/assets/layers/birdhide/birdhide.json b/assets/layers/birdhide/birdhide.json index e6d1990245..07bbc253c5 100644 --- a/assets/layers/birdhide/birdhide.json +++ b/assets/layers/birdhide/birdhide.json @@ -25,7 +25,8 @@ ] }, "then": { - "*": "{name}" + "*": "{name}", + "nl": "{name}" } }, { diff --git a/assets/layers/nature_reserve/nature_reserve.json b/assets/layers/nature_reserve/nature_reserve.json index 33c3b41f21..f264a56939 100644 --- a/assets/layers/nature_reserve/nature_reserve.json +++ b/assets/layers/nature_reserve/nature_reserve.json @@ -35,7 +35,8 @@ ] }, "then": { - "*": "{name}" + "*": "{name}", + "nl": "{name}" } } ] @@ -348,7 +349,7 @@ { "question": { "en": "Is there some extra info?", - "nl": "Is er extra info die je kwijt wil?" + "nl": "Is er extra info die je kwijt wil?" }, "render": { "en": "Extra info: {description:0}", diff --git a/langs/layers/en.json b/langs/layers/en.json index c00b246337..821f2d172d 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -1123,6 +1123,7 @@ } }, "birdhide": { + "description": "A birdhide", "filter": { "0": { "options": { @@ -1130,7 +1131,84 @@ "question": "Wheelchair accessible" } } + }, + "1": { + "options": { + "0": { + "question": "Only covered birdhides" + } + } } + }, + "name": "Bird watching places", + "presets": { + "0": { + "description": "A covered shelter where one can watch birds comfortably", + "title": "Birdhide" + }, + "1": { + "description": "A screen or wall with openings to watch birds", + "title": "Bird blind" + } + }, + "tagRenderings": { + "bird-hide-shelter-or-wall": { + "mappings": { + "0": { + "then": "Bird blind" + }, + "1": { + "then": "Bird hide" + }, + "2": { + "then": "Bird tower hide" + }, + "3": { + "then": "Bird hide shelter" + } + }, + "question": "Is this a bird blind or a bird watching shelter?" + }, + "bird-hide-wheelchair": { + "mappings": { + "0": { + "then": "There are special provisions for wheelchair users" + }, + "1": { + "then": "A wheelchair can easily use this birdhide" + }, + "2": { + "then": "This birdhide is reachable by wheelchair, but it is not easy" + }, + "3": { + "then": "Not accessible to wheelchair users" + } + }, + "question": "Is this bird hide accessible to wheelchair users?" + }, + "birdhide-operator": { + "mappings": { + "0": { + "then": "Operated by Natuurpunt" + }, + "1": { + "then": "Operated by the Agency for Nature and Forests" + } + }, + "question": "Who operates this birdhide?", + "render": "Operated by {operator}" + } + }, + "title": { + "mappings": { + "1": { + "then": "Bird hide {name}" + }, + "2": { + "then": "Bird blind {name}" + } + }, + "render": "Bird watching place" } }, "cafe_pub": { @@ -3515,6 +3593,35 @@ }, "nature_reserve": { "description": "A nature reserve is an area where nature can take its course", + "filter": { + "0": { + "options": { + "0": { + "question": "Freely accesible" + } + } + }, + "1": { + "options": { + "0": { + "question": "All nature reserves" + }, + "1": { + "question": "Dogs are allowed to roam freely" + }, + "2": { + "question": "Dogs are allowed if they are leashed" + } + } + } + }, + "name": "Nature reserve", + "presets": { + "0": { + "description": "Add a missing nature reserve", + "title": "nature reserve" + } + }, "tagRenderings": { "Access tag": { "mappings": { @@ -3558,6 +3665,10 @@ }, "question": "Are dogs allowed in this nature reserve?" }, + "Editable description": { + "question": "Is there some extra info?", + "render": "Extra info: {description:0}" + }, "Email": { "question": "What email adress can one send to with questions and problems with this nature reserve?
Respect privacy - only fill out a personal email address if this is widely published", "render": "{email}" @@ -3571,6 +3682,9 @@ "question": "What is the name of this area?", "render": "This area is named {name}" }, + "Non-editable description": { + "render": "Extra information: {description}" + }, "Operator tag": { "mappings": { "0": { @@ -3764,6 +3878,7 @@ }, "parking": { "description": "A layer showing car parkings", + "name": "Parking", "presets": { "0": { "title": "car parking" diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 9edc12e9ed..045a534139 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3473,6 +3473,7 @@ "question": "Zijn honden toegelaten in dit gebied?" }, "Editable description": { + "question": "Is er extra info die je kwijt wil?", "render": "Extra info: {description:0}" }, "Email": {