From 426128e141361f7bb15ff56cdae37fda34915c73 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 22 Feb 2024 01:39:42 +0100 Subject: [PATCH] LayerServer: add opt-out to get counted for some irrelevant layers, remove some obsolete 'cluster' information --- assets/layers/summary/summary.json | 1 - .../walls_and_buildings.json | 1 + assets/themes/advertising/advertising.json | 2 +- assets/themes/benches/benches.json | 2 +- .../themes/cycle_highways/cycle_highways.json | 8 +- assets/themes/cyclestreets/cyclestreets.json | 9 +- assets/themes/etymology/etymology.json | 16 +- assets/themes/ghostbikes/ghostbikes.json | 8 +- .../mapcomplete-changes.json | 116 ++----- .../Conversion/LegacyJsonConvert.ts | 1 + .../ThemeConfig/Conversion/Validation.ts | 290 +++++++++--------- .../ThemeConfig/Json/LayerConfigJson.ts | 12 + src/Models/ThemeConfig/LayerConfig.ts | 2 + src/Models/ThemeViewState.ts | 5 +- 14 files changed, 210 insertions(+), 263 deletions(-) diff --git a/assets/layers/summary/summary.json b/assets/layers/summary/summary.json index 7cb5c1175..40e139f58 100644 --- a/assets/layers/summary/summary.json +++ b/assets/layers/summary/summary.json @@ -2,7 +2,6 @@ "id": "summary", "description": "Special layer which shows `count`", "source": "special", - "name": "CLusters", "title": { "render": {"en": "Summary"} }, diff --git a/assets/layers/walls_and_buildings/walls_and_buildings.json b/assets/layers/walls_and_buildings/walls_and_buildings.json index f8368a464..53a482adc 100644 --- a/assets/layers/walls_and_buildings/walls_and_buildings.json +++ b/assets/layers/walls_and_buildings/walls_and_buildings.json @@ -31,6 +31,7 @@ ], "minzoom": 18, "shownByDefault": false, + "isCounted": false, "title": { "render": { "en": "Wall or building", diff --git a/assets/themes/advertising/advertising.json b/assets/themes/advertising/advertising.json index 46718f5cd..0b00bf07a 100644 --- a/assets/themes/advertising/advertising.json +++ b/assets/themes/advertising/advertising.json @@ -47,4 +47,4 @@ ], "widenFactor": 0.01, "maintainer": "Offsel" -} \ No newline at end of file +} diff --git a/assets/themes/benches/benches.json b/assets/themes/benches/benches.json index 5161ecaa7..d1ea28881 100644 --- a/assets/themes/benches/benches.json +++ b/assets/themes/benches/benches.json @@ -73,4 +73,4 @@ "bench_at_pt" ], "widenFactor": 1.5 -} \ No newline at end of file +} diff --git a/assets/themes/cycle_highways/cycle_highways.json b/assets/themes/cycle_highways/cycle_highways.json index 2365eaade..5c4408bbd 100644 --- a/assets/themes/cycle_highways/cycle_highways.json +++ b/assets/themes/cycle_highways/cycle_highways.json @@ -266,12 +266,6 @@ ] } ], - "enableDownload": true, - "enablePdfDownload": true, "overpassTimeout": 60, - "widenFactor": 1.1, - "#overpassUrl": "https://overpass.kumi.systems/api/interpreter", - "clustering": { - "maxZoom": 1 - } + "widenFactor": 1.1 } diff --git a/assets/themes/cyclestreets/cyclestreets.json b/assets/themes/cyclestreets/cyclestreets.json index affd01ce7..a33829f1e 100644 --- a/assets/themes/cyclestreets/cyclestreets.json +++ b/assets/themes/cyclestreets/cyclestreets.json @@ -403,7 +403,8 @@ }, "width": "5" } - ] + ], + "doCount": false } ], "overrideAll": { @@ -768,9 +769,5 @@ } ] }, - "widenFactor": 2, - "clustering": { - "maxZoom": 12, - "minNeededElements": 200 - } + "widenFactor": 2 } diff --git a/assets/themes/etymology/etymology.json b/assets/themes/etymology/etymology.json index 877cdcd8b..3928fd728 100644 --- a/assets/themes/etymology/etymology.json +++ b/assets/themes/etymology/etymology.json @@ -68,6 +68,7 @@ "pl": "Ulice bez informacji o etymologii" }, "minzoom": 15, + "isCounted": false, "source": { "=osmTags": { "and": [ @@ -99,6 +100,7 @@ "pl": "Parki i lasy bez informacji o etymologii" }, "minzoom": 18, + "isCounted": false, "source": { "osmTags": { "and": [ @@ -131,6 +133,7 @@ "pl": "Instytucje edukacyjne bez informacji o etymologii" }, "minzoom": 18, + "isCounted": false, "source": { "osmTags": { "and": [ @@ -166,6 +169,7 @@ "pl": "Miejsca kulturowe bez informacji o etymologii" }, "minzoom": 18, + "isCounted": false, "source": { "osmTags": { "and": [ @@ -201,6 +205,7 @@ "pl": "Miejsca turystyczne bez informacji o etymologii" }, "minzoom": 18, + "isCounted": false, "source": { "osmTags": { "and": [ @@ -235,6 +240,7 @@ "pl": "Miejsca związane ze zdrowiem i społeczeństwem bez informacji o etymologii" }, "minzoom": 18, + "isCounted": false, "source": { "osmTags": { "and": [ @@ -268,6 +274,7 @@ "pl": "Miejsca sportowe bez informacji o etymologii" }, "minzoom": 18, + "isCounted": false, "source": { "osmTags": { "and": [ @@ -284,10 +291,5 @@ } } } - ], - "widenFactor": 2, - "clustering": { - "maxZoom": 14, - "minNeededElements": 250 - } -} \ No newline at end of file + ] +} diff --git a/assets/themes/ghostbikes/ghostbikes.json b/assets/themes/ghostbikes/ghostbikes.json index 045e57d72..24aee6c49 100644 --- a/assets/themes/ghostbikes/ghostbikes.json +++ b/assets/themes/ghostbikes/ghostbikes.json @@ -43,8 +43,6 @@ "layers": [ "ghost_bike" ], - "widenFactor": 5, - "clustering": { - "maxZoom": 0 - } -} \ No newline at end of file + "widenFactor": 5 + +} diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index 9d03f8832..2e8d73909 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -1,19 +1,13 @@ { "id": "mapcomplete-changes", "title": { - "en": "Changes made with MapComplete", - "de": "Änderungen mit MapComplete", - "es": "Cambios hechos con MapComplete" + "en": "Changes made with MapComplete" }, "shortDescription": { - "en": "Shows changes made by MapComplete", - "de": "Änderungen von MapComplete anzeigen", - "es": "Muestra los cambios hechos por MapComplete" + "en": "Shows changes made by MapComplete" }, "description": { - "en": "This maps shows all the changes made with MapComplete", - "de": "Diese Karte zeigt alle mit MapComplete vorgenommenen Änderungen", - "es": "Este mapa muestra todos los cambios hechos con MapComplete" + "en": "This maps shows all the changes made with MapComplete" }, "icon": "./assets/svg/logo.svg", "hideFromOverview": true, @@ -26,9 +20,7 @@ { "id": "mapcomplete-changes", "name": { - "en": "Changeset centers", - "de": "Zentrum der Änderungssätze", - "es": "Centro del conjunto de cambios" + "en": "Changeset centers" }, "minzoom": 0, "source": { @@ -39,55 +31,41 @@ }, "title": { "render": { - "en": "Changeset for {theme}", - "de": "Änderungssatz für {theme}", - "es": "Conjunto de cambios para {theme}" + "en": "Changeset for {theme}" } }, "description": { - "en": "Shows all MapComplete changes", - "de": "Alle MapComplete-Änderungen anzeigen", - "es": "Muestra todos los cambios de MapComplete" + "en": "Shows all MapComplete changes" }, "tagRenderings": [ { "id": "show_changeset_id", "render": { - "en": "Changeset {id}", - "de": "Änderungssatz {id}", - "es": "Conjunto de cambios {id}" + "en": "Changeset {id}" } }, { "id": "contributor", "question": { - "en": "What contributor did make this change?", - "de": "Wer hat diese Änderung vorgenommen?", - "es": "¿Quién realizó este cambio?" + "en": "What contributor did make this change?" }, "freeform": { "key": "user" }, "render": { - "en": "Change made by {user}", - "de": "Änderung von {user}", - "es": "Cambio hecho por {user}" + "en": "Change made by {user}" } }, { "id": "theme-id", "question": { - "en": "What theme was used to make this change?", - "de": "Welches Thema wurde für die Änderung verwendet?", - "es": "¿Qué tema se utilizó para realizar este cambio?" + "en": "What theme was used to make this change?" }, "freeform": { "key": "theme" }, "render": { - "en": "Change with theme {theme}", - "de": "Geändert mit Thema {theme}", - "es": "Cambio con el tema {theme}" + "en": "Change with theme {theme}" } }, { @@ -96,27 +74,19 @@ "key": "locale" }, "question": { - "en": "What locale (language) was this change made in?", - "de": "In welcher Benutzersprache wurde die Änderung vorgenommen?", - "es": "¿En qué configuración regional (idioma) se realizó este cambio?" + "en": "What locale (language) was this change made in?" }, "render": { - "en": "User locale is {locale}", - "de": "Benutzersprache {locale}", - "es": "La configuración regional del usuario es {locale}" + "en": "User locale is {locale}" } }, { "id": "host", "render": { - "en": "Change with with {host}", - "de": "Änderung über {host}", - "es": "Cambio con {host}" + "en": "Change with with {host}" }, "question": { - "en": "What host (website) was this change made with?", - "de": "Über welchen Host (Webseite) wurde diese Änderung vorgenommen?", - "es": "¿Con qué host (página web) se realizó este cambio?" + "en": "What host (website) was this change made with?" }, "freeform": { "key": "host" @@ -137,14 +107,10 @@ { "id": "version", "question": { - "en": "What version of MapComplete was used to make this change?", - "de": "Mit welcher MapComplete Version wurde die Änderung vorgenommen?", - "es": "¿Qué versión de MapComplete se usó para realizar este cambio?" + "en": "What version of MapComplete was used to make this change?" }, "render": { - "en": "Made with {editor}", - "de": "Erstellt mit {editor}", - "es": "Hecho con {editor}" + "en": "Made with {editor}" }, "freeform": { "key": "editor" @@ -514,9 +480,7 @@ } ], "question": { - "en": "Themename contains {search}", - "de": "Themename enthält {search}", - "es": "El nombre del tema contiene {search}" + "en": "Themename contains {search}" } } ] @@ -532,9 +496,7 @@ } ], "question": { - "en": "Themename does not contain {search}", - "de": "Der Name enthält nicht {search}", - "es": "El nombre del tema no contiene {search}" + "en": "Themename does not contain {search}" } } ] @@ -550,9 +512,7 @@ } ], "question": { - "en": "Made by contributor {search}", - "de": "Erstellt vom Mitwirkenden {search}", - "es": "Hecho por el colaborador {search}" + "en": "Made by contributor {search}" } } ] @@ -568,9 +528,7 @@ } ], "question": { - "en": "Not made by contributor {search}", - "de": "Nicht erstellt von Mitwirkendem {search}", - "es": "No hecho por el colaborador {search}" + "en": "Not made by contributor {search}" } } ] @@ -587,9 +545,7 @@ } ], "question": { - "en": "Made before {search}", - "de": "Erstellt vor {search}", - "es": "Hecho antes de {search}" + "en": "Made before {search}" } } ] @@ -606,9 +562,7 @@ } ], "question": { - "en": "Made after {search}", - "de": "Erstellt nach {search}", - "es": "Hecho después de {search}" + "en": "Made after {search}" } } ] @@ -624,9 +578,7 @@ } ], "question": { - "en": "User language (iso-code) {search}", - "de": "Benutzersprache (ISO-Code) {search}", - "es": "Idioma del usuario (código ISO) {search}" + "en": "User language (iso-code) {search}" } } ] @@ -642,9 +594,7 @@ } ], "question": { - "en": "Made with host {search}", - "de": "Erstellt mit host {search}", - "es": "Hecho con el host {search}" + "en": "Made with host {search}" } } ] @@ -655,9 +605,7 @@ { "osmTags": "add-image>0", "question": { - "en": "Changeset added at least one image", - "de": "Im Änderungssatz wurde mindestens ein Bild hinzugefügt", - "es": "El conjunto de cambios ha añadido al menos una imagen" + "en": "Changeset added at least one image" } } ] @@ -668,9 +616,7 @@ { "osmTags": "theme!=grb", "question": { - "en": "Exclude GRB theme", - "de": "GRB-Thema ausschließen", - "es": "Excluir el tema del GRB" + "en": "Exclude GRB theme" } } ] @@ -681,9 +627,7 @@ { "osmTags": "theme!=etymology", "question": { - "en": "Exclude etymology theme", - "de": "Etymologie-Thema ausschließen", - "es": "Excluir el tema de la etimología" + "en": "Exclude etymology theme" } } ] @@ -698,9 +642,7 @@ { "id": "link_to_more", "render": { - "en": "More statistics can be found here", - "de": "Weitere Statistiken gibt es hier", - "es": "Puede encontrar más estadísticas aquí" + "en": "More statistics can be found here" } }, { diff --git a/src/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts b/src/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts index 3c7cf7efc..4d066b958 100644 --- a/src/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts +++ b/src/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts @@ -269,6 +269,7 @@ class UpdateLegacyTheme extends DesugaringStep { oldThemeConfig.layers = Utils.NoNull(oldThemeConfig.layers) delete oldThemeConfig["language"] delete oldThemeConfig["version"] + delete oldThemeConfig["clustering"] if (oldThemeConfig.startLat === 0) { delete oldThemeConfig.startLat diff --git a/src/Models/ThemeConfig/Conversion/Validation.ts b/src/Models/ThemeConfig/Conversion/Validation.ts index c6c067070..4e3b0d602 100644 --- a/src/Models/ThemeConfig/Conversion/Validation.ts +++ b/src/Models/ThemeConfig/Conversion/Validation.ts @@ -13,10 +13,7 @@ import { And } from "../../../Logic/Tags/And" import Translations from "../../../UI/i18n/Translations" import FilterConfigJson from "../Json/FilterConfigJson" import DeleteConfig from "../DeleteConfig" -import { - MappingConfigJson, - QuestionableTagRenderingConfigJson, -} from "../Json/QuestionableTagRenderingConfigJson" +import { MappingConfigJson, QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson" import Validators from "../../../UI/InputElement/Validators" import TagRenderingConfig from "../TagRenderingConfig" import { parse as parse_html } from "node-html-parser" @@ -34,7 +31,7 @@ class ValidateLanguageCompleteness extends DesugaringStep { super( "Checks that the given object is fully translated in the specified languages", [], - "ValidateLanguageCompleteness" + "ValidateLanguageCompleteness", ) this._languages = languages ?? ["en"] } @@ -48,18 +45,18 @@ class ValidateLanguageCompleteness extends DesugaringStep { .filter( (t) => t.tr.translations[neededLanguage] === undefined && - t.tr.translations["*"] === undefined + t.tr.translations["*"] === undefined, ) .forEach((missing) => { context .enter(missing.context.split(".")) .err( `The theme ${obj.id} should be translation-complete for ` + - neededLanguage + - ", but it lacks a translation for " + - missing.context + - ".\n\tThe known translation is " + - missing.tr.textFor("en") + neededLanguage + + ", but it lacks a translation for " + + missing.context + + ".\n\tThe known translation is " + + missing.tr.textFor("en"), ) }) } @@ -76,7 +73,7 @@ export class DoesImageExist extends DesugaringStep { constructor( knownImagePaths: Set, checkExistsSync: (path: string) => boolean = undefined, - ignore?: Set + ignore?: Set, ) { super("Checks if an image exists", [], "DoesImageExist") this._ignore = ignore @@ -112,15 +109,15 @@ export class DoesImageExist extends DesugaringStep { if (!this._knownImagePaths.has(image)) { if (this.doesPathExist === undefined) { context.err( - `Image with path ${image} not found or not attributed; it is used in ${context}` + `Image with path ${image} not found or not attributed; it is used in ${context}`, ) } else if (!this.doesPathExist(image)) { context.err( - `Image with path ${image} does not exist.\n Check for typo's and missing directories in the path.` + `Image with path ${image} does not exist.\n Check for typo's and missing directories in the path.`, ) } else { context.err( - `Image with path ${image} is not attributed (but it exists); execute 'npm run query:licenses' to add the license information and/or run 'npm run generate:licenses' to compile all the license info` + `Image with path ${image} is not attributed (but it exists); execute 'npm run query:licenses' to add the license information and/or run 'npm run generate:licenses' to compile all the license info`, ) } } @@ -144,7 +141,7 @@ export class ValidateTheme extends DesugaringStep { doesImageExist: DoesImageExist, path: string, isBuiltin: boolean, - sharedTagRenderings?: Set + sharedTagRenderings?: Set, ) { super("Doesn't change anything, but emits warnings and errors", [], "ValidateTheme") this._validateImage = doesImageExist @@ -163,15 +160,15 @@ export class ValidateTheme extends DesugaringStep { if (json["units"] !== undefined) { context.err( "The theme " + - json.id + - " has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) " + json.id + + " has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) ", ) } if (json["roamingRenderings"] !== undefined) { context.err( "Theme " + - json.id + - " contains an old 'roamingRenderings'. Use an 'overrideAll' instead" + json.id + + " contains an old 'roamingRenderings'. Use an 'overrideAll' instead", ) } } @@ -189,10 +186,10 @@ export class ValidateTheme extends DesugaringStep { for (const remoteImage of remoteImages) { context.err( "Found a remote image: " + - remoteImage.path + - " in theme " + - json.id + - ", please download it." + remoteImage.path + + " in theme " + + json.id + + ", please download it.", ) } for (const image of images) { @@ -208,17 +205,17 @@ export class ValidateTheme extends DesugaringStep { const filename = this._path.substring( this._path.lastIndexOf("/") + 1, - this._path.length - 5 + this._path.length - 5, ) if (theme.id !== filename) { context.err( "Theme ids should be the same as the name.json, but we got id: " + - theme.id + - " and filename " + - filename + - " (" + - this._path + - ")" + theme.id + + " and filename " + + filename + + " (" + + this._path + + ")", ) } this._validateImage.convert(theme.icon, context.enter("icon")) @@ -226,13 +223,13 @@ export class ValidateTheme extends DesugaringStep { const dups = Utils.Duplicates(json.layers.map((layer) => layer["id"])) if (dups.length > 0) { context.err( - `The theme ${json.id} defines multiple layers with id ${dups.join(", ")}` + `The theme ${json.id} defines multiple layers with id ${dups.join(", ")}`, ) } if (json["mustHaveLanguage"] !== undefined) { new ValidateLanguageCompleteness(...json["mustHaveLanguage"]).convert( theme, - context + context, ) } if (!json.hideFromOverview && theme.id !== "personal" && this._isBuiltin) { @@ -240,7 +237,7 @@ export class ValidateTheme extends DesugaringStep { const targetLanguage = theme.title.SupportedLanguages()[0] if (targetLanguage !== "en") { context.err( - `TargetLanguage is not 'en' for public theme ${theme.id}, it is ${targetLanguage}. Move 'en' up in the title of the theme and set it as the first key` + `TargetLanguage is not 'en' for public theme ${theme.id}, it is ${targetLanguage}. Move 'en' up in the title of the theme and set it as the first key`, ) } @@ -301,7 +298,7 @@ export class ValidateThemeAndLayers extends Fuse { doesImageExist: DoesImageExist, path: string, isBuiltin: boolean, - sharedTagRenderings?: Set + sharedTagRenderings?: Set, ) { super( "Validates a theme and the contained layers", @@ -311,10 +308,10 @@ export class ValidateThemeAndLayers extends Fuse { new Each( new Bypass( (layer) => Constants.added_by_default.indexOf(layer.id) < 0, - new ValidateLayerConfig(undefined, isBuiltin, doesImageExist, false, true) - ) - ) - ) + new ValidateLayerConfig(undefined, isBuiltin, doesImageExist, false, true), + ), + ), + ), ) } } @@ -324,7 +321,7 @@ class OverrideShadowingCheck extends DesugaringStep { super( "Checks that an 'overrideAll' does not override a single override", [], - "OverrideShadowingCheck" + "OverrideShadowingCheck", ) } @@ -373,6 +370,9 @@ class MiscThemeChecks extends DesugaringStep { if (json.socialImage === "") { context.warn("Social image for theme " + json.id + " is the emtpy string") } + if (json["clustering"]) { + context.warn("Obsolete field `clustering` is still around") + } { for (let i = 0; i < json.layers.length; i++) { const l = json.layers[i] @@ -395,7 +395,7 @@ class MiscThemeChecks extends DesugaringStep { context .enter("overideAll") .err( - "'overrideAll' is spelled with _two_ `r`s. You only wrote a single one of them." + "'overrideAll' is spelled with _two_ `r`s. You only wrote a single one of them.", ) } return json @@ -407,7 +407,7 @@ export class PrevalidateTheme extends Fuse { super( "Various consistency checks on the raw JSON", new MiscThemeChecks(), - new OverrideShadowingCheck() + new OverrideShadowingCheck(), ) } } @@ -417,7 +417,7 @@ export class DetectConflictingAddExtraTags extends DesugaringStep ["_abc"] */ private static extractCalculatedTagNames( - layerConfig?: LayerConfigJson | { calculatedTags: string[] } + layerConfig?: LayerConfigJson | { calculatedTags: string[] }, ) { return ( layerConfig?.calculatedTags?.map((ct) => { @@ -652,16 +652,16 @@ export class DetectShadowedMappings extends DesugaringStep\` instead. The images found are ${images.join( - ", " - )}. (This check can be turned of by adding "#": "${ignoreToken}" in the mapping, but this is discouraged` + ", ", + )}. (This check can be turned of by adding "#": "${ignoreToken}" in the mapping, but this is discouraged`, ) } else { ctx.info( `Ignored image ${images.join( - ", " - )} in 'then'-clause of a mapping as this check has been disabled` + ", ", + )} in 'then'-clause of a mapping as this check has been disabled`, ) for (const image of images) { @@ -756,7 +756,7 @@ class ValidatePossibleLinks extends DesugaringStep does have `rel='noopener'` set", [], - "ValidatePossibleLinks" + "ValidatePossibleLinks", ) } @@ -786,21 +786,21 @@ class ValidatePossibleLinks extends DesugaringStep, - context: ConversionContext + context: ConversionContext, ): string | Record { if (typeof json === "string") { if (this.isTabnabbingProne(json)) { context.err( "The string " + - json + - " has a link targeting `_blank`, but it doesn't have `rel='noopener'` set. This gives rise to reverse tabnapping" + json + + " has a link targeting `_blank`, but it doesn't have `rel='noopener'` set. This gives rise to reverse tabnapping", ) } } else { for (const k in json) { if (this.isTabnabbingProne(json[k])) { context.err( - `The translation for ${k} '${json[k]}' has a link targeting \`_blank\`, but it doesn't have \`rel='noopener'\` set. This gives rise to reverse tabnapping` + `The translation for ${k} '${json[k]}' has a link targeting \`_blank\`, but it doesn't have \`rel='noopener'\` set. This gives rise to reverse tabnapping`, ) } } @@ -818,7 +818,7 @@ class CheckTranslation extends DesugaringStep { super( "Checks that a translation is valid and internally consistent", ["*"], - "CheckTranslation" + "CheckTranslation", ) this._allowUndefined = allowUndefined } @@ -864,17 +864,17 @@ class MiscTagRenderingChecks extends DesugaringStep { convert( json: TagRenderingConfigJson | QuestionableTagRenderingConfigJson, - context: ConversionContext + context: ConversionContext, ): TagRenderingConfigJson { if (json["special"] !== undefined) { context.err( - 'Detected `special` on the top level. Did you mean `{"render":{ "special": ... }}`' + "Detected `special` on the top level. Did you mean `{\"render\":{ \"special\": ... }}`", ) } if (Object.keys(json).length === 1 && typeof json["render"] === "string") { context.warn( - `use the content directly instead of {render: ${JSON.stringify(json["render"])}}` + `use the content directly instead of {render: ${JSON.stringify(json["render"])}}`, ) } @@ -886,7 +886,7 @@ class MiscTagRenderingChecks extends DesugaringStep { const mapping: MappingConfigJson = json.mappings[i] CheckTranslation.noUndefined.convert( mapping.then, - context.enters("mappings", i, "then") + context.enters("mappings", i, "then"), ) if (!mapping.if) { console.log( @@ -895,7 +895,7 @@ class MiscTagRenderingChecks extends DesugaringStep { "if", mapping.if, context.path.join("."), - mapping.then + mapping.then, ) context.enters("mappings", i, "if").err("No `if` is defined") } @@ -905,7 +905,7 @@ class MiscTagRenderingChecks extends DesugaringStep { context .enters("mappings", i, "addExtraTags", j) .err( - "Detected a 'null' or 'undefined' value. Either specify a tag or delete this item" + "Detected a 'null' or 'undefined' value. Either specify a tag or delete this item", ) } } @@ -916,18 +916,18 @@ class MiscTagRenderingChecks extends DesugaringStep { context .enters("mappings", i, "then") .warn( - "A mapping should not start with 'yes' or 'no'. If the attribute is known, it will only show 'yes' or 'no' without the question, resulting in a weird phrasing in the information box" + "A mapping should not start with 'yes' or 'no'. If the attribute is known, it will only show 'yes' or 'no' without the question, resulting in a weird phrasing in the information box", ) } } } if (json["group"]) { - context.err('Groups are deprecated, use `"label": ["' + json["group"] + '"]` instead') + context.err("Groups are deprecated, use `\"label\": [\"" + json["group"] + "\"]` instead") } if (json["question"] && json.freeform?.key === undefined && json.mappings === undefined) { context.err( - "A question is defined, but no mappings nor freeform (key) are. Add at least one of them" + "A question is defined, but no mappings nor freeform (key) are. Add at least one of them", ) } if (json["question"] && !json.freeform && (json.mappings?.length ?? 0) == 1) { @@ -937,7 +937,7 @@ class MiscTagRenderingChecks extends DesugaringStep { context .enter("questionHint") .err( - "A questionHint is defined, but no question is given. As such, the questionHint will never be shown" + "A questionHint is defined, but no question is given. As such, the questionHint will never be shown", ) } @@ -945,7 +945,7 @@ class MiscTagRenderingChecks extends DesugaringStep { context .enters("icon", "size") .err( - "size is not a valid attribute. Did you mean 'class'? Class can be one of `small`, `medium` or `large`" + "size is not a valid attribute. Did you mean 'class'? Class can be one of `small`, `medium` or `large`", ) } @@ -955,10 +955,10 @@ class MiscTagRenderingChecks extends DesugaringStep { .enter("render") .err( "This tagRendering allows to set a value to key " + - json.freeform.key + - ", but does not define a `render`. Please, add a value here which contains `{" + - json.freeform.key + - "}`" + json.freeform.key + + ", but does not define a `render`. Please, add a value here which contains `{" + + json.freeform.key + + "}`", ) } else { const render = new Translation(json.render) @@ -989,7 +989,7 @@ class MiscTagRenderingChecks extends DesugaringStep { const keyFirstArg = ["canonical", "fediverse_link", "translated"] if ( keyFirstArg.some( - (funcName) => txt.indexOf(`{${funcName}(${json.freeform.key}`) >= 0 + (funcName) => txt.indexOf(`{${funcName}(${json.freeform.key}`) >= 0, ) ) { continue @@ -1012,7 +1012,7 @@ class MiscTagRenderingChecks extends DesugaringStep { context .enter("render") .err( - `The rendering for language ${ln} does not contain \`{${json.freeform.key}}\`. This is a bug, as this rendering should show exactly this freeform key!` + `The rendering for language ${ln} does not contain \`{${json.freeform.key}}\`. This is a bug, as this rendering should show exactly this freeform key!`, ) } } @@ -1020,8 +1020,8 @@ class MiscTagRenderingChecks extends DesugaringStep { if (json.render && json["question"] && json.freeform === undefined) { context.err( `Detected a tagrendering which takes input without freeform key in ${context}; the question is ${new Translation( - json["question"] - ).textFor("en")}` + json["question"], + ).textFor("en")}`, ) } @@ -1032,9 +1032,9 @@ class MiscTagRenderingChecks extends DesugaringStep { .enters("freeform", "type") .err( "Unknown type: " + - freeformType + - "; try one of " + - Validators.availableTypes.join(", ") + freeformType + + "; try one of " + + Validators.availableTypes.join(", "), ) } } @@ -1070,7 +1070,7 @@ export class ValidateTagRenderings extends Fuse { new On("question", new ValidatePossibleLinks()), new On("questionHint", new ValidatePossibleLinks()), new On("mappings", new Each(new On("then", new ValidatePossibleLinks()))), - new MiscTagRenderingChecks() + new MiscTagRenderingChecks(), ) } } @@ -1089,7 +1089,7 @@ export class PrevalidateLayer extends DesugaringStep { path: string, isBuiltin: boolean, doesImageExist: DoesImageExist, - studioValidations: boolean + studioValidations: boolean, ) { super("Runs various checks against common mistakes for a layer", [], "PrevalidateLayer") this._path = path @@ -1115,7 +1115,7 @@ export class PrevalidateLayer extends DesugaringStep { context .enter("source") .err( - "No source section is defined; please define one as data is not loaded otherwise" + "No source section is defined; please define one as data is not loaded otherwise", ) } else { if (json.source === "special" || json.source === "special:library") { @@ -1123,7 +1123,7 @@ export class PrevalidateLayer extends DesugaringStep { context .enters("source", "osmTags") .err( - "No osmTags defined in the source section - these should always be present, even for geojson layer" + "No osmTags defined in the source section - these should always be present, even for geojson layer", ) } else { const osmTags = TagUtils.Tag(json.source["osmTags"], context + "source.osmTags") @@ -1132,7 +1132,7 @@ export class PrevalidateLayer extends DesugaringStep { .enters("source", "osmTags") .err( "The source states tags which give a very wide selection: it only uses negative expressions, which will result in too much and unexpected data. Add at least one required tag. The tags are:\n\t" + - osmTags.asHumanString(false, false, {}) + osmTags.asHumanString(false, false, {}), ) } } @@ -1158,10 +1158,10 @@ export class PrevalidateLayer extends DesugaringStep { .enter("syncSelection") .err( "Invalid sync-selection: must be one of " + - LayerConfig.syncSelectionAllowed.map((v) => `'${v}'`).join(", ") + - " but got '" + - json.syncSelection + - "'" + LayerConfig.syncSelectionAllowed.map((v) => `'${v}'`).join(", ") + + " but got '" + + json.syncSelection + + "'", ) } if (json["pointRenderings"]?.length > 0) { @@ -1180,7 +1180,7 @@ export class PrevalidateLayer extends DesugaringStep { } json.pointRendering?.forEach((pr, i) => - this._validatePointRendering.convert(pr, context.enters("pointeRendering", i)) + this._validatePointRendering.convert(pr, context.enters("pointeRendering", i)), ) if (json["mapRendering"]) { @@ -1197,8 +1197,8 @@ export class PrevalidateLayer extends DesugaringStep { if (!Constants.priviliged_layers.find((x) => x == json.id)) { context.err( "Layer " + - json.id + - " uses 'special' as source.osmTags. However, this layer is not a priviliged layer" + json.id + + " uses 'special' as source.osmTags. However, this layer is not a priviliged layer", ) } } @@ -1213,19 +1213,19 @@ export class PrevalidateLayer extends DesugaringStep { context .enter("title") .err( - "This layer does not have a title defined but it does have tagRenderings. Not having a title will disable the popups, resulting in an unclickable element. Please add a title. If not having a popup is intended and the tagrenderings need to be kept (e.g. in a library layer), set `title: null` to disable this error." + "This layer does not have a title defined but it does have tagRenderings. Not having a title will disable the popups, resulting in an unclickable element. Please add a title. If not having a popup is intended and the tagrenderings need to be kept (e.g. in a library layer), set `title: null` to disable this error.", ) } if (json.title === null) { context.info( - "Title is `null`. This results in an element that cannot be clicked - even though tagRenderings is set." + "Title is `null`. This results in an element that cannot be clicked - even though tagRenderings is set.", ) } { // Check for multiple, identical builtin questions - usability for studio users const duplicates = Utils.Duplicates( - json.tagRenderings.filter((tr) => typeof tr === "string") + json.tagRenderings.filter((tr) => typeof tr === "string"), ) for (let i = 0; i < json.tagRenderings.length; i++) { const tagRendering = json.tagRenderings[i] @@ -1255,7 +1255,7 @@ export class PrevalidateLayer extends DesugaringStep { { // duplicate ids in tagrenderings check const duplicates = Utils.NoNull( - Utils.Duplicates(Utils.NoNull((json.tagRenderings ?? []).map((tr) => tr["id"]))) + Utils.Duplicates(Utils.NoNull((json.tagRenderings ?? []).map((tr) => tr["id"]))), ) if (duplicates.length > 0) { // It is tempting to add an index to this warning; however, due to labels the indices here might be different from the index in the tagRendering list @@ -1293,8 +1293,8 @@ export class PrevalidateLayer extends DesugaringStep { if (json["overpassTags"] !== undefined) { context.err( "Layer " + - json.id + - 'still uses the old \'overpassTags\'-format. Please use "source": {"osmTags": }\' instead of "overpassTags": (note: this isn\'t your fault, the custom theme generator still spits out the old format)' + json.id + + "still uses the old 'overpassTags'-format. Please use \"source\": {\"osmTags\": }' instead of \"overpassTags\": (note: this isn't your fault, the custom theme generator still spits out the old format)", ) } const forbiddenTopLevel = [ @@ -1314,7 +1314,7 @@ export class PrevalidateLayer extends DesugaringStep { } if (json["hideUnderlayingFeaturesMinPercentage"] !== undefined) { context.err( - "Layer " + json.id + " contains an old 'hideUnderlayingFeaturesMinPercentage'" + "Layer " + json.id + " contains an old 'hideUnderlayingFeaturesMinPercentage'", ) } @@ -1331,9 +1331,9 @@ export class PrevalidateLayer extends DesugaringStep { if (this._path != undefined && this._path.indexOf(expected) < 0) { context.err( "Layer is in an incorrect place. The path is " + - this._path + - ", but expected " + - expected + this._path + + ", but expected " + + expected, ) } } @@ -1351,13 +1351,13 @@ export class PrevalidateLayer extends DesugaringStep { .enter(["tagRenderings", ...emptyIndexes]) .err( `Some tagrendering-ids are empty or have an emtpy string; this is not allowed (at ${emptyIndexes.join( - "," - )}])` + ",", + )}])`, ) } const duplicateIds = Utils.Duplicates( - (json.tagRenderings ?? [])?.map((f) => f["id"]).filter((id) => id !== "questions") + (json.tagRenderings ?? [])?.map((f) => f["id"]).filter((id) => id !== "questions"), ) if (duplicateIds.length > 0 && !Utils.runningFromConsole) { context @@ -1381,7 +1381,7 @@ export class PrevalidateLayer extends DesugaringStep { if (json.tagRenderings !== undefined) { new On( "tagRenderings", - new Each(new ValidateTagRenderings(json, this._doesImageExist)) + new Each(new ValidateTagRenderings(json, this._doesImageExist)), ).convert(json, context) } @@ -1408,7 +1408,7 @@ export class PrevalidateLayer extends DesugaringStep { context .enters("pointRendering", i, "marker", indexM, "icon", "condition") .err( - "Don't set a condition in a marker as this will result in an invisible but clickable element. Use extra filters in the source instead." + "Don't set a condition in a marker as this will result in an invisible but clickable element. Use extra filters in the source instead.", ) } } @@ -1446,9 +1446,9 @@ export class PrevalidateLayer extends DesugaringStep { .enters("presets", i, "tags") .err( "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: " + - tags.asHumanString(false, false, {}) + - "\n The required tags are: " + - baseTags.asHumanString(false, false, {}) + tags.asHumanString(false, false, {}) + + "\n The required tags are: " + + baseTags.asHumanString(false, false, {}), ) } } @@ -1465,7 +1465,7 @@ export class ValidateLayerConfig extends DesugaringStep { isBuiltin: boolean, doesImageExist: DoesImageExist, studioValidations: boolean = false, - skipDefaultLayers: boolean = false + skipDefaultLayers: boolean = false, ) { super("Thin wrapper around 'ValidateLayer", [], "ValidateLayerConfig") this.validator = new ValidateLayer( @@ -1473,7 +1473,7 @@ export class ValidateLayerConfig extends DesugaringStep { isBuiltin, doesImageExist, studioValidations, - skipDefaultLayers + skipDefaultLayers, ) } @@ -1501,7 +1501,7 @@ class ValidatePointRendering extends DesugaringStep { context .enter("markers") .err( - `Detected a field 'markerS' in pointRendering. It is written as a singular case` + `Detected a field 'markerS' in pointRendering. It is written as a singular case`, ) } if (json.marker && !Array.isArray(json.marker)) { @@ -1511,7 +1511,7 @@ class ValidatePointRendering extends DesugaringStep { context .enter("location") .err( - "A pointRendering should have at least one 'location' to defined where it should be rendered. " + "A pointRendering should have at least one 'location' to defined where it should be rendered. ", ) } return json @@ -1530,26 +1530,26 @@ export class ValidateLayer extends Conversion< isBuiltin: boolean, doesImageExist: DoesImageExist, studioValidations: boolean = false, - skipDefaultLayers: boolean = false + skipDefaultLayers: boolean = false, ) { super("Doesn't change anything, but emits warnings and errors", [], "ValidateLayer") this._prevalidation = new PrevalidateLayer( path, isBuiltin, doesImageExist, - studioValidations + studioValidations, ) this._skipDefaultLayers = skipDefaultLayers } convert( json: LayerConfigJson, - context: ConversionContext + context: ConversionContext, ): { parsed: LayerConfig; raw: LayerConfigJson } { context = context.inOperation(this.name) if (typeof json === "string") { context.err( - `Not a valid layer: the layerConfig is a string. 'npm run generate:layeroverview' might be needed` + `Not a valid layer: the layerConfig is a string. 'npm run generate:layeroverview' might be needed`, ) return undefined } @@ -1580,7 +1580,7 @@ export class ValidateLayer extends Conversion< context .enters("calculatedTags", i) .err( - `Invalid function definition: the custom javascript is invalid:${e}. The offending javascript code is:\n ${code}` + `Invalid function definition: the custom javascript is invalid:${e}. The offending javascript code is:\n ${code}`, ) } } @@ -1631,8 +1631,8 @@ export class ValidateFilter extends DesugaringStep { .enters("fields", i) .err( `Invalid filter: ${type} is not a valid textfield type.\n\tTry one of ${Array.from( - Validators.availableTypes - ).join(",")}` + Validators.availableTypes, + ).join(",")}`, ) } } @@ -1649,13 +1649,13 @@ export class DetectDuplicateFilters extends DesugaringStep<{ super( "Tries to detect layers where a shared filter can be used (or where similar filters occur)", [], - "DetectDuplicateFilters" + "DetectDuplicateFilters", ) } convert( json: { layers: LayerConfigJson[]; themes: LayoutConfigJson[] }, - context: ConversionContext + context: ConversionContext, ): { layers: LayerConfigJson[]; themes: LayoutConfigJson[] } { const { layers, themes } = json const perOsmTag = new Map< @@ -1719,7 +1719,7 @@ export class DetectDuplicateFilters extends DesugaringStep<{ filter: FilterConfigJson }[] >, - layout?: LayoutConfigJson | undefined + layout?: LayoutConfigJson | undefined, ): void { if (layer.filter === undefined || layer.filter === null) { return @@ -1759,7 +1759,7 @@ export class DetectDuplicatePresets extends DesugaringStep { super( "Detects mappings which have identical (english) names or identical mappings.", ["presets"], - "DetectDuplicatePresets" + "DetectDuplicatePresets", ) } @@ -1770,13 +1770,13 @@ export class DetectDuplicatePresets extends DesugaringStep { if (new Set(enNames).size != enNames.length) { const dups = Utils.Duplicates(enNames) const layersWithDup = json.layers.filter((l) => - l.presets.some((p) => dups.indexOf(p.title.textFor("en")) >= 0) + l.presets.some((p) => dups.indexOf(p.title.textFor("en")) >= 0), ) const layerIds = layersWithDup.map((l) => l.id) context.err( `This themes has multiple presets which are named:${dups}, namely layers ${layerIds.join( - ", " - )} this is confusing for contributors and is probably the result of reusing the same layer multiple times. Use \`{"override": {"=presets": []}}\` to remove some presets` + ", ", + )} this is confusing for contributors and is probably the result of reusing the same layer multiple times. Use \`{"override": {"=presets": []}}\` to remove some presets`, ) } @@ -1791,17 +1791,17 @@ export class DetectDuplicatePresets extends DesugaringStep { Utils.SameObject(presetATags, presetBTags) && Utils.sameList( presetA.preciseInput.snapToLayers, - presetB.preciseInput.snapToLayers + presetB.preciseInput.snapToLayers, ) ) { context.err( `This themes has multiple presets with the same tags: ${presetATags.asHumanString( false, false, - {} + {}, )}, namely the preset '${presets[i].title.textFor("en")}' and '${presets[ j - ].title.textFor("en")}'` + ].title.textFor("en")}'`, ) } } @@ -1825,13 +1825,13 @@ export class ValidateThemeEnsemble extends Conversion< super( "Validates that all themes together are logical, i.e. no duplicate ids exists within (overriden) themes", [], - "ValidateThemeEnsemble" + "ValidateThemeEnsemble", ) } convert( json: LayoutConfig[], - context: ConversionContext + context: ConversionContext, ): Map< string, { @@ -1874,11 +1874,11 @@ export class ValidateThemeEnsemble extends Conversion< context.err( [ "The layer with id '" + - id + - "' is found in multiple themes with different tag definitions:", + id + + "' is found in multiple themes with different tag definitions:", "\t In theme " + oldTheme + ":\t" + oldTags.asHumanString(false, false, {}), "\tIn theme " + theme.id + ":\t" + tags.asHumanString(false, false, {}), - ].join("\n") + ].join("\n"), ) } } diff --git a/src/Models/ThemeConfig/Json/LayerConfigJson.ts b/src/Models/ThemeConfig/Json/LayerConfigJson.ts index ef3b43196..0da64a1d9 100644 --- a/src/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/src/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -176,6 +176,18 @@ export interface LayerConfigJson { */ isShown?: TagConfigJson + /** + * question: should this layer be included in the summary counts? + * + * The layer server can give summary counts for a tile. + * This should however be disabled for some layers, e.g. because there are too many features (walls_and_buildings) or because the count is irrelevant. + * + * ifunset: Do count + * iffalse: Do not include the counts + * iftrue: Do include the count + */ + isCounted?: true | boolean + /** * The minimum needed zoomlevel required to start loading and displaying the data. * This can be used to only show common features (e.g. a bicycle parking) only when the map is zoomed in very much (17). diff --git a/src/Models/ThemeConfig/LayerConfig.ts b/src/Models/ThemeConfig/LayerConfig.ts index 16010c814..e3a0ac247 100644 --- a/src/Models/ThemeConfig/LayerConfig.ts +++ b/src/Models/ThemeConfig/LayerConfig.ts @@ -53,6 +53,7 @@ export default class LayerConfig extends WithContextLoader { public readonly allowMove: MoveConfig | null public readonly allowSplit: boolean public readonly shownByDefault: boolean + public readonly doCount: boolean /** * In seconds */ @@ -158,6 +159,7 @@ export default class LayerConfig extends WithContextLoader { } this.minzoomVisible = json.minzoomVisible ?? this.minzoom this.shownByDefault = json.shownByDefault ?? true + this.doCount = json.isCounted ?? true this.forceLoad = json.forceLoad ?? false if (json.presets === null) json.presets = undefined if (json.presets !== undefined && json.presets?.map === undefined) { diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index d2bc248fd..50fe06d71 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -489,13 +489,11 @@ export default class ThemeViewState implements SpecialVisualizationState { if (!toSelect) { return } - const layer = this.layout.getMatchingLayer(toSelect.properties) this.selectedElement.setData(undefined) this.selectedElement.setData(toSelect) }) return } - const layer = this.layout.getMatchingLayer(toSelect.properties) this.selectedElement.setData(undefined) this.selectedElement.setData(toSelect) } @@ -658,7 +656,8 @@ export default class ThemeViewState implements SpecialVisualizationState { const layers = this.layout.layers.filter( (l) => Constants.priviliged_layers.indexOf(l.id) < 0 && - l.source.geojsonSource === undefined + l.source.geojsonSource === undefined && + l.doCount ) const url = new URL(Constants.VectorTileServer) const summaryTileSource = new SummaryTileSource(