From 4658bf42a6c92d7305f4cca190442b426e620f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Constantino=E2=80=93Bodin?= Date: Sun, 10 Aug 2025 18:05:44 +0200 Subject: [PATCH 01/34] Adding support for panorama cameras. --- .../surveillance_camera/surveillance_camera.json | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/assets/layers/surveillance_camera/surveillance_camera.json b/assets/layers/surveillance_camera/surveillance_camera.json index 6f52f9c6b..0295d4524 100644 --- a/assets/layers/surveillance_camera/surveillance_camera.json +++ b/assets/layers/surveillance_camera/surveillance_camera.json @@ -340,7 +340,7 @@ ] }, { - "id": "Camera type: fixed; panning; dome", + "id": "Camera type: fixed; panning; dome; panorama", "question": { "en": "What kind of camera is this?", "ca": "Quin tipus de càmera és aquesta?", @@ -388,7 +388,7 @@ }, "icon": "./assets/themes/surveillance/dome.svg" }, - { + { "if": "camera:type=panning", "then": { "en": "A panning camera", @@ -403,6 +403,13 @@ "ru": "Панорамная камера" } }, + { + "if": "camera:type=panorama", + "then": { + "en": "A camera with a wide field of view", + "fr": "Une caméra 360°" + } + }, { "if": "camera:type=doorbell", "icon": { @@ -958,4 +965,4 @@ "enableRelocation": false }, "enableMorePrivacy": true -} \ No newline at end of file +} From 3dc528f0daeb0f75c098ea2d39ef722efc55231e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Constantino=E2=80=93Bodin?= Date: Sun, 10 Aug 2025 18:11:26 +0200 Subject: [PATCH 02/34] Adding an image for panorama cameras. --- .../surveillance_camera.json | 4 + assets/themes/surveillance/panorama.license | 2 + assets/themes/surveillance/panorama.svg | 105 ++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 assets/themes/surveillance/panorama.license create mode 100644 assets/themes/surveillance/panorama.svg diff --git a/assets/layers/surveillance_camera/surveillance_camera.json b/assets/layers/surveillance_camera/surveillance_camera.json index 0295d4524..ac7500215 100644 --- a/assets/layers/surveillance_camera/surveillance_camera.json +++ b/assets/layers/surveillance_camera/surveillance_camera.json @@ -86,6 +86,10 @@ "if": "camera:type=doorbell", "then": "./assets/layers/surveillance_camera/doorbell.svg" }, + { + "if": "camera:type=panorama", + "then": "./assets/themes/surveillance/panorama.svg" + }, { "if": "_direction:leftright=right", "then": "./assets/themes/surveillance/cam_right.svg" diff --git a/assets/themes/surveillance/panorama.license b/assets/themes/surveillance/panorama.license new file mode 100644 index 000000000..f725fff05 --- /dev/null +++ b/assets/themes/surveillance/panorama.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Martin Bodin +SPDX-License-Identifier: CC0-1.0 diff --git a/assets/themes/surveillance/panorama.svg b/assets/themes/surveillance/panorama.svg new file mode 100644 index 000000000..0a21207b0 --- /dev/null +++ b/assets/themes/surveillance/panorama.svg @@ -0,0 +1,105 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + From ef16f5ccef821ee80ef18fb49f65ab2951f5c6b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Constantino=E2=80=93Bodin?= Date: Sun, 10 Aug 2025 18:17:00 +0200 Subject: [PATCH 03/34] Translation suggestions for panorama cameras. --- assets/layers/surveillance_camera/surveillance_camera.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/assets/layers/surveillance_camera/surveillance_camera.json b/assets/layers/surveillance_camera/surveillance_camera.json index ac7500215..03008ae9b 100644 --- a/assets/layers/surveillance_camera/surveillance_camera.json +++ b/assets/layers/surveillance_camera/surveillance_camera.json @@ -410,7 +410,9 @@ { "if": "camera:type=panorama", "then": { - "en": "A camera with a wide field of view", + "en": "A 360° camera", + "de": "Eine 360°-Kamera", + "es": "Una cámara de 360°", "fr": "Une caméra 360°" } }, @@ -537,7 +539,7 @@ "da": "Hvilken form for overvågning er dette kamera?", "de": "Was überwacht diese Kamera?", "es": "¿Qué tipo de vigilancia es esta cámara?", - "fr": "De quel genre de surveillance cette caméra est-elle ?", + "fr": "De quel genre de surveillance cette caméra est-elle ?", "it": "Che tipo di sorveglianza è questa telecamera?", "nl": "Wat soort bewaking wordt hier uitgevoerd?", "sl": "Kaj nadzoruje ta kamera?" From 877dd260aed3a21ffbb1f638b07e35277df835b6 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Fri, 22 Aug 2025 22:18:54 +0200 Subject: [PATCH 04/34] Themes(shops): Remove brand tags if marked as without brand --- assets/layers/shops/shops.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index 5b562d9be..4835174d6 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -486,7 +486,12 @@ "es": "Esta tienda no tiene una marca específica, no forma parte de una cadena más grande", "it": "Questo negozio non ha un marchio specifico, non fa parte di una catena più grande", "uk": "Цей магазин не має певного бренду, він не є частиною великої мережі" - } + }, + "addExtraTags": [ + "brand=", + "brand:wikidata=", + "brand:wikipedia=" + ] } ] }, From 06ac28dab98777ba91c06ec46800f08c52d14bdc Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Fri, 22 Aug 2025 22:25:25 +0200 Subject: [PATCH 05/34] Themes(shops): Exlcude shop=no --- assets/layers/shops/shops.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index 4835174d6..a8c63e6ef 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -43,7 +43,8 @@ "craft=key_cutter" ] }, - "shop!=mall" + "shop!=mall", + "shop!=no" ] } }, From 03d07b670de7fa84c0e58f647073f67fb6d60046 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 23 Aug 2025 19:21:09 +0200 Subject: [PATCH 06/34] Themes(parking): Add support for access tags (#1797) --- .../layers/parking_spaces/parking_spaces.json | 89 ++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/assets/layers/parking_spaces/parking_spaces.json b/assets/layers/parking_spaces/parking_spaces.json index 64e5f907e..0c36e859b 100644 --- a/assets/layers/parking_spaces/parking_spaces.json +++ b/assets/layers/parking_spaces/parking_spaces.json @@ -69,6 +69,15 @@ "icon": { "render": "./assets/layers/parking_spaces/parking_space.svg", "mappings": [ + { + "if": { + "or": [ + "access=private", + "access=no" + ] + }, + "then": "./assets/layers/parking_spaces/parking_space_private.svg" + }, { "if": "parking_space=disabled", "then": "./assets/layers/toilet/wheelchair.svg" @@ -99,7 +108,7 @@ ], "lineRendering": [ { - "color": "#696969", + "color": "dimgray", "width": "1" } ], @@ -295,6 +304,69 @@ "it": "Questo è un posto auto riservato al car sharing.", "nl": "Deze parkeerplek is gereserveerd voor autodelen." } + }, + { + "if": "parking_space=women", + "then": { + "en": "This is a parking space reserved for women.", + "nl": "Deze parkeerplek is gereserveerd voor vrouwen." + } + } + ] + }, + { + "id": "access", + "question": { + "en": "Who can use this parking space?", + "nl": "Wie mag deze parkeerplek gebruiken?" + }, + "render": { + "en": "Access of parking space: {access}", + "nl": "Toegang tot parkeerplek: {access}" + }, + "freeform": { + "key": "access", + "type": "string", + "addExtraTags": [ + "fixme=Freeform used on 'access'-tag: possibly a wrong value" + ] + }, + "mappings": [ + { + "if": "access=", + "then": { + "en": "Anyone can use this parking space.", + "nl": "Iedereen kan deze parkeerplek gebruiken." + }, + "hideInAnswer": true + }, + { + "if": "access=yes", + "then": { + "en": "Anyone can use this parking space.", + "nl": "Iedereen kan deze parkeerplek gebruiken." + } + }, + { + "if": "access=customers", + "then": { + "en": "This parking space is reserved for customers.", + "nl": "Deze parkeerplek is gereserveerd voor klanten." + } + }, + { + "if": "access=private", + "then": { + "en": "This parking space is private and cannot be used by the general public.", + "nl": "Deze parkeerplek is privé en mag niet door het grote publiek worden gebruikt." + } + }, + { + "if": "access=permit", + "then": { + "en": "This parking space is reserved for permit holders.", + "nl": "Deze parkeerplek is gereserveerd voor vergunninghouders." + } } ] }, @@ -310,6 +382,19 @@ "nl": "Deze parkeerplek heeft {capacity} plaatsen." }, "mappings": [ + { + "if": "capacity=", + "then": { + "en": "This parking space has 1 space.", + "ca": "Aquest espai d'aparcament té 1 plaça.", + "cs": "Toto parkoviště má 1 místo.", + "de": "Dieser Parkplatz hat 1 Stellplatz.", + "es": "Esta plaza de aparcamiento tiene 1 plaza.", + "it": "Questo posto auto ha 1 spazio.", + "nl": "Deze parkeerplek heeft 1 plaats." + }, + "hideInAnswer": true + }, { "if": "capacity=1", "then": { @@ -329,4 +414,4 @@ "enableImproveAccuracy": true, "enableRelocation": false } -} +} \ No newline at end of file From 4294e4930509bb8157597d3d6242046a5fa14e8d Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 24 Aug 2025 00:20:29 +0200 Subject: [PATCH 07/34] Themes(nature): Add picnic sites (#1849) --- assets/layers/picnic_site/picnic_site.json | 320 +++++++++++++++++++++ assets/themes/nature/nature.json | 3 +- 2 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 assets/layers/picnic_site/picnic_site.json diff --git a/assets/layers/picnic_site/picnic_site.json b/assets/layers/picnic_site/picnic_site.json new file mode 100644 index 000000000..d29d94bb4 --- /dev/null +++ b/assets/layers/picnic_site/picnic_site.json @@ -0,0 +1,320 @@ +{ + "id": "picnic_site", + "name": { + "en": "Picnic sites", + "nl": "Picknickplaatsen" + }, + "description": { + "en": "Picnic sites for eating outdoors, featuring amenities like toilets, water taps, BBQ, benches and shelters", + "nl": "Picknickplaatsen voor het eten in de buitenlucht, met voorzieningen zoals toiletten, waterkranen, BBQ, banken en schuilplaatsen" + }, + "source": { + "osmTags": "tourism=picnic_site" + }, + "minzoom": 10, + "title": { + "render": { + "en": "Picnic site", + "nl": "Picknickplaats" + } + }, + "pointRendering": [ + { + "iconSize": "35,35", + "location": [ + "point", + "centroid" + ], + "anchor": "center", + "marker": [ + { + "color": "#3984e6", + "icon": "circle" + }, + { + "icon": "./assets/layers/picnic_table/picnic_table.svg" + } + ] + } + ], + "lineRendering": [ + { + "color": "#3984e6", + "fillColor": "#3984e6bd", + "width": 5 + } + ], + "presets": [ + { + "tags": [ + "tourism=picnic_site" + ], + "title": { + "en": "a picnic site", + "nl": "een picknickplaats" + }, + "description": { + "en": "A picnic site for eating outdoors, featuring amenities like toilets, water taps, BBQ, benches and shelters", + "nl": "Een picknickplaats voor het eten in de buitenlucht, met voorzieningen zoals toiletten, waterkranen, BBQ, banken en schuilplaatsen" + } + } + ], + "tagRenderings": [ + "images", + { + "builtin": "name", + "override": { + "render": { + "en": "This picnic site is called {name}", + "nl": "Deze picknickplaats heet {name}" + } + } + }, + { + "id": "shelter", + "question": { + "en": "Does this picnic site have a shelter?", + "nl": "Heeft deze picknickplaats een schuilplaats?" + }, + "mappings": [ + { + "if": "shelter=yes", + "then": { + "en": "This picnic site has a shelter.", + "nl": "Deze picknickplaats heeft een schuilplaats." + } + }, + { + "if": "shelter=no", + "then": { + "en": "This picnic site does not have a shelter.", + "nl": "Deze picknickplaats heeft geen schuilplaats." + } + }, + { + "if": "shelter=separate", + "then": { + "en": "This picnic site has a shelter, but is is mapped as a different icon.", + "nl": "Deze picknickplaats heeft een schuilplaats, maar deze staat los op de kaart." + } + } + ] + }, + { + "id": "fireplace", + "question": { + "en": "Does this picnic site have a firepit?", + "nl": "Heeft deze picknickplaats een vuurplaats?" + }, + "mappings": [ + { + "if": "fireplace=yes", + "then": { + "en": "This picnic site has a firepit.", + "nl": "Deze picknickplaats heeft een vuurplaats." + } + }, + { + "if": "fireplace=no", + "then": { + "en": "This picnic site does not have a firepit.", + "nl": "Deze picknickplaats heeft geen vuurplaats." + } + }, + { + "if": "fireplace=separate", + "then": { + "en": "This picnic site has a firepit, but it is mapped as a different icon.", + "nl": "Deze picknickplaats heeft een vuurplaats, maar deze staat los op de kaart." + } + } + ] + }, + { + "id": "bbq", + "question": { + "en": "Does this picnic site have a BBQ?", + "nl": "Heeft deze picknickplaats een BBQ?" + }, + "mappings": [ + { + "if": "bbq=yes", + "then": { + "en": "This picnic site has a BBQ.", + "nl": "Deze picknickplaats heeft een BBQ." + } + }, + { + "if": "bbq=no", + "then": { + "en": "This picnic site does not have a BBQ.", + "nl": "Deze picknickplaats heeft geen BBQ." + } + }, + { + "if": "bbq=separate", + "then": { + "en": "This picnic site has a BBQ, but it is mapped as a different icon.", + "nl": "Deze picknickplaats heeft een BBQ, maar deze staat los op de kaart." + } + } + ] + }, + { + "id": "covered", + "question": { + "en": "Is this picnic site covered?", + "nl": "Is deze picknickplaats overdekt?" + }, + "mappings": [ + { + "if": "covered=yes", + "then": { + "en": "This picnic site is covered.", + "nl": "Deze picknickplaats is overdekt." + } + }, + { + "if": "covered=no", + "then": { + "en": "This picnic site is not covered.", + "nl": "Deze picknickplaats is niet overdekt." + } + } + ] + }, + { + "id": "drinking_water", + "question": { + "en": "Does this picnic site have drinking water?", + "nl": "Heeft deze picknickplaats drinkwater?" + }, + "mappings": [ + { + "if": "drinking_water=yes", + "then": { + "en": "This picnic site has drinking water.", + "nl": "Deze picknickplaats heeft drinkwater." + } + }, + { + "if": "drinking_water=no", + "then": { + "en": "This picnic site does not have drinking water.", + "nl": "Deze picknickplaats heeft geen drinkwater." + } + }, + { + "if": "drinking_water=separate", + "then": { + "en": "This picnic site has drinking water, but it is mapped as a different icon.", + "nl": "Deze picknickplaats heeft drinkwater, maar deze staat los op de kaart." + } + } + ] + }, + { + "id": "openfire", + "question": { + "en": "Is open fire allowed at this picnic site?", + "nl": "Is open vuur toegestaan op deze picknickplaats?" + }, + "mappings": [ + { + "if": "openfire=yes", + "then": { + "en": "Open fire is allowed at this picnic site.", + "nl": "Open vuur is toegestaan op deze picknickplaats." + } + }, + { + "if": "openfire=no", + "then": { + "en": "Open fire is not allowed at this picnic site.", + "nl": "Open vuur is niet toegestaan op deze picknickplaats." + } + }, + { + "if": "openfire=permit", + "then": { + "en": "Open fire is allowed at this picnic site with a permit.", + "nl": "Open vuur is toegestaan op deze picknickplaats met een vergunning." + } + } + ] + } + ], + "filter": [ + { + "id": "shelter", + "options": [ + { + "question": { + "en": "With a shelter", + "nl": "Met een schuilplaats" + }, + "osmTags": { + "or": [ + "shelter=yes", + "shelter=separate" + ] + } + } + ] + }, + { + "id": "fireplace", + "options": [ + { + "question": { + "en": "With a firepit", + "nl": "Met een vuurplaats" + }, + "osmTags": { + "or": [ + "fireplace=yes", + "fireplace=separate" + ] + } + } + ] + }, + { + "id": "bbq", + "options": [ + { + "question": { + "en": "With a BBQ", + "nl": "Met een BBQ" + }, + "osmTags": { + "or": [ + "bbq=yes", + "bbq=separate" + ] + } + } + ] + }, + { + "id": "drinking_water", + "options": [ + { + "question": { + "en": "With drinking water", + "nl": "Met drinkwater" + }, + "osmTags": { + "or": [ + "drinking_water=yes", + "drinking_water=separate" + ] + } + } + ] + } + ], + "allowMove": { + "enableImproveAccuracy": true + } +} \ No newline at end of file diff --git a/assets/themes/nature/nature.json b/assets/themes/nature/nature.json index 7158a1d5e..72798228d 100644 --- a/assets/themes/nature/nature.json +++ b/assets/themes/nature/nature.json @@ -66,6 +66,7 @@ "minzoom": 11 } }, + "picnic_site", { "builtin": [ "map", @@ -95,4 +96,4 @@ "observation_tower", "viewpoint" ] -} +} \ No newline at end of file From 48a0a5b5205f345edf32d2144fca77a723553664 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 24 Aug 2025 11:28:46 +0200 Subject: [PATCH 08/34] WIP: test workaround --- src/Logic/Tags/And.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Logic/Tags/And.ts b/src/Logic/Tags/And.ts index b1b69b8ab..77ec8e5bb 100644 --- a/src/Logic/Tags/And.ts +++ b/src/Logic/Tags/And.ts @@ -314,6 +314,14 @@ export class And extends TagsFilter { for (let j = i + 1; j < optimized.length; j++) { const ti = optimized[i] const tj = optimized[j] + if ( + !ti || + !tj || + typeof ti.shadows !== "function" || + typeof tj.shadows !== "function" + ) { + continue + } if (ti.shadows(tj)) { // if 'ti' is true, this implies 'tj' is always true as well. // if 'ti' is false, then 'tj' might be true or false @@ -322,6 +330,7 @@ export class And extends TagsFilter { // If 'ti' is true, then 'tj' will be true too and 'tj' can be ignored // If 'ti' is false, then the entire expression will be false and it doesn't matter what 'tj' yields optimized.splice(j, 1) + j-- } else if (tj.shadows(ti)) { optimized.splice(i, 1) i-- From 2c6574762c30d60440806ace3f9e3320f771c9a4 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 24 Aug 2025 23:08:16 +0200 Subject: [PATCH 09/34] Themes: New arcade theme --- assets/layers/arcade/arcade.json | 115 ++++++++++++++++++++++++ assets/layers/arcade/arcade.svg | 3 + assets/layers/arcade/arcade.svg.license | 2 + assets/layers/arcade/license_info.json | 12 +++ assets/themes/arcade/arcade.json | 13 +++ 5 files changed, 145 insertions(+) create mode 100644 assets/layers/arcade/arcade.json create mode 100644 assets/layers/arcade/arcade.svg create mode 100644 assets/layers/arcade/arcade.svg.license create mode 100644 assets/layers/arcade/license_info.json create mode 100644 assets/themes/arcade/arcade.json diff --git a/assets/layers/arcade/arcade.json b/assets/layers/arcade/arcade.json new file mode 100644 index 000000000..2ab70ba65 --- /dev/null +++ b/assets/layers/arcade/arcade.json @@ -0,0 +1,115 @@ +{ + "id": "arcade", + "name": { + "en": "Arcades" + }, + "description": { + "en": "Layer showing arcades" + }, + "source": { + "osmTags": "leisure=amusement_arcade" + }, + "minzoom": 10, + "title": { + "render": { + "en": "Arcade" + }, + "mappings": [ + { + "if": "name~*", + "then": { + "*": "{name}" + } + } + ] + }, + "pointRendering": [ + { + "location": [ + "point", + "centroid" + ], + "marker": [ + { + "icon": "square", + "color": "white" + }, + { + "icon": "./assets/layers/arcade/arcade.svg" + } + ] + } + ], + "lineRendering": [ + { + "width": 3, + "color": "#0e8517" + } + ], + "presets": [ + { + "title": { + "en": "an arcade" + }, + "tags": [ + "leisure=amusement_arcade" + ] + } + ], + "tagRenderings": [ + "images", + "reviews", + { + "builtin": "name", + "override": { + "question": { + "en": "What is the name of this arcade?" + }, + "render": { + "en": "This arcade is called {name}" + } + } + }, + { + "id": "virtual_reality", + "question": { + "en": "Does this arcade offer virtual-reality gaming?" + }, + "mappings": [ + { + "if": "virtual_reality=yes", + "then": { + "en": "This arcade offers virtual-reality gaming." + } + }, + { + "if": "virtual_reality=only", + "then": { + "en": "This arcade only offers virtual-reality gaming." + } + }, + { + "if": "virtual_reality=", + "then": { + "en": "This arcade doesn't offer virtual-reality gaming" + } + } + ] + }, + "brand", + "opening_hours", + "website", + "email", + "phone", + "payment-options", + "level", + "description", + "toilet_at_amenity_lib.all" + ], + "allowMove": { + "enableImproveAccuracy": true, + "enableRelocation": true + }, + "credits": "Robin van der Linde", + "credits:uid": 5093765 +} \ No newline at end of file diff --git a/assets/layers/arcade/arcade.svg b/assets/layers/arcade/arcade.svg new file mode 100644 index 000000000..5f4031683 --- /dev/null +++ b/assets/layers/arcade/arcade.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/assets/layers/arcade/arcade.svg.license b/assets/layers/arcade/arcade.svg.license new file mode 100644 index 000000000..ff6ee9492 --- /dev/null +++ b/assets/layers/arcade/arcade.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: meased +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/arcade/license_info.json b/assets/layers/arcade/license_info.json new file mode 100644 index 000000000..f82b95ae4 --- /dev/null +++ b/assets/layers/arcade/license_info.json @@ -0,0 +1,12 @@ +[ + { + "path": "arcade.svg", + "license": "CC0-1.0", + "authors": [ + "meased" + ], + "sources": [ + "https://github.com/gravitystorm/openstreetmap-carto/blob/master/symbols/leisure/amusement_arcade.svg" + ] + } +] \ No newline at end of file diff --git a/assets/themes/arcade/arcade.json b/assets/themes/arcade/arcade.json new file mode 100644 index 000000000..137cdf86a --- /dev/null +++ b/assets/themes/arcade/arcade.json @@ -0,0 +1,13 @@ +{ + "id": "arcade", + "title": { + "en": "Arcades" + }, + "description": { + "en": "A map of arcades" + }, + "icon": "./assets/layers/arcade/arcade.svg", + "layers": [ + "arcade" + ] +} \ No newline at end of file From eb317d0bdabb0e5cf1dc4a871d65757a432c4de3 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 24 Aug 2025 23:11:19 +0200 Subject: [PATCH 10/34] Themes: move shelter filter to filters --- assets/layers/filters/filters.json | 22 ++++++++++++++++ assets/layers/picnic_site/picnic_site.json | 18 +------------ assets/layers/questions/questions.json | 5 ++-- .../layers/transit_stops/transit_stops.json | 25 ++----------------- 4 files changed, 28 insertions(+), 42 deletions(-) diff --git a/assets/layers/filters/filters.json b/assets/layers/filters/filters.json index 67556c5e1..f58d3333b 100644 --- a/assets/layers/filters/filters.json +++ b/assets/layers/filters/filters.json @@ -571,6 +571,28 @@ } } ] + }, + { + "id": "shelter", + "options": [ + { + "osmTags": { + "or": [ + "shelter=yes", + "shelter=separate" + ] + }, + "question": { + "en": "With a shelter", + "ca": "Amb refugi", + "cs": "S přístřeškem", + "de": "Mit Unterstand", + "es": "Con refugio", + "fr": "Avec un abri", + "it": "Con una pensilina" + } + } + ] } ], "allowMove": false diff --git a/assets/layers/picnic_site/picnic_site.json b/assets/layers/picnic_site/picnic_site.json index d29d94bb4..ef02be70b 100644 --- a/assets/layers/picnic_site/picnic_site.json +++ b/assets/layers/picnic_site/picnic_site.json @@ -245,23 +245,7 @@ } ], "filter": [ - { - "id": "shelter", - "options": [ - { - "question": { - "en": "With a shelter", - "nl": "Met een schuilplaats" - }, - "osmTags": { - "or": [ - "shelter=yes", - "shelter=separate" - ] - } - } - ] - }, + "shelter", { "id": "fireplace", "options": [ diff --git a/assets/layers/questions/questions.json b/assets/layers/questions/questions.json index 6b56671d2..368eb1cff 100644 --- a/assets/layers/questions/questions.json +++ b/assets/layers/questions/questions.json @@ -3116,7 +3116,8 @@ "if": "nobrand=yes", "addExtraTags": [ "brand=", - "brand:wikidata=" + "brand:wikidata=", + "brand:wikipedia=" ], "then": { "en": "Not part of a bigger brand", @@ -3652,4 +3653,4 @@ } } ] -} +} \ No newline at end of file diff --git a/assets/layers/transit_stops/transit_stops.json b/assets/layers/transit_stops/transit_stops.json index 096ce247e..822d21745 100644 --- a/assets/layers/transit_stops/transit_stops.json +++ b/assets/layers/transit_stops/transit_stops.json @@ -544,28 +544,7 @@ } ], "filter": [ - { - "id": "shelter", - "options": [ - { - "osmTags": { - "or": [ - "shelter=yes", - "shelter=separate" - ] - }, - "question": { - "en": "With a shelter", - "ca": "Amb refugi", - "cs": "S přístřeškem", - "de": "Mit Unterstand", - "es": "Con refugio", - "fr": "Avec un abri", - "it": "Con una pensilina" - } - } - ] - }, + "shelter", { "id": "bench", "options": [ @@ -613,4 +592,4 @@ "tactile_paving" ], "allowMove": false -} +} \ No newline at end of file From 25dfc80535dbcf0cc06703ccc9dbbf7b70c085ab Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 24 Aug 2025 23:11:45 +0200 Subject: [PATCH 11/34] Chore: reset translations --- langs/layers/ca.json | 17 ++-- langs/layers/cs.json | 17 ++-- langs/layers/de.json | 17 ++-- langs/layers/en.json | 208 +++++++++++++++++++++++++++++++++++++++++-- langs/layers/es.json | 17 ++-- langs/layers/fr.json | 14 +-- langs/layers/it.json | 17 ++-- langs/layers/nl.json | 151 +++++++++++++++++++++++++++++++ langs/themes/en.json | 4 + 9 files changed, 413 insertions(+), 49 deletions(-) diff --git a/langs/layers/ca.json b/langs/layers/ca.json index e4ac8aee9..dc8729b1d 100644 --- a/langs/layers/ca.json +++ b/langs/layers/ca.json @@ -5520,6 +5520,13 @@ } } }, + "23": { + "options": { + "0": { + "question": "Amb refugi" + } + } + }, "3": { "options": { "0": { @@ -8006,6 +8013,9 @@ "mappings": { "0": { "then": "Aquest espai d'aparcament té 1 plaça." + }, + "1": { + "then": "Aquest espai d'aparcament té 1 plaça." } }, "render": "Aquests espais d'aparcament tenen {capacity} places." @@ -12449,13 +12459,6 @@ "transit_stops": { "description": "Capa que mostra diferents tipus de parades de transport públic.", "filter": { - "0": { - "options": { - "0": { - "question": "Amb refugi" - } - } - }, "1": { "options": { "0": { diff --git a/langs/layers/cs.json b/langs/layers/cs.json index 7b6cc195a..68be5705c 100644 --- a/langs/layers/cs.json +++ b/langs/layers/cs.json @@ -5824,6 +5824,13 @@ } } }, + "23": { + "options": { + "0": { + "question": "S přístřeškem" + } + } + }, "3": { "options": { "0": { @@ -8616,6 +8623,9 @@ "mappings": { "0": { "then": "Toto parkoviště má 1 místo." + }, + "1": { + "then": "Toto parkoviště má 1 místo." } }, "render": "Toto parkoviště má {capacity} míst." @@ -13507,13 +13517,6 @@ "transit_stops": { "description": "Vrstva zobrazující různé typy zastávek veřejné dopravy.", "filter": { - "0": { - "options": { - "0": { - "question": "S přístřeškem" - } - } - }, "1": { "options": { "0": { diff --git a/langs/layers/de.json b/langs/layers/de.json index 27c384de1..f79fd65c3 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -5498,6 +5498,13 @@ } } }, + "23": { + "options": { + "0": { + "question": "Mit Unterstand" + } + } + }, "3": { "options": { "0": { @@ -7970,6 +7977,9 @@ "mappings": { "0": { "then": "Dieser Parkplatz hat 1 Stellplatz." + }, + "1": { + "then": "Dieser Parkplatz hat 1 Stellplatz." } }, "render": "Dieser Parkplatz hat {capacity} Stellplätze." @@ -12438,13 +12448,6 @@ "transit_stops": { "description": "Ebene mit verschiedenen Arten von Haltestellen.", "filter": { - "0": { - "options": { - "0": { - "question": "Mit Unterstand" - } - } - }, "1": { "options": { "0": { diff --git a/langs/layers/en.json b/langs/layers/en.json index ee422ee16..97fa2e0cb 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -531,6 +531,40 @@ "render": "Animal shelter" } }, + "arcade": { + "description": "Layer showing arcades", + "name": "Arcades", + "presets": { + "0": { + "title": "an arcade" + } + }, + "tagRenderings": { + "name": { + "override": { + "question": "What is the name of this arcade?", + "render": "This arcade is called {name}" + } + }, + "virtual_reality": { + "mappings": { + "0": { + "then": "This arcade offers virtual-reality gaming." + }, + "1": { + "then": "This arcade only offers virtual-reality gaming." + }, + "2": { + "then": "This arcade doesn't offer virtual-reality gaming" + } + }, + "question": "Does this arcade offer virtual-reality gaming?" + } + }, + "title": { + "render": "Arcade" + } + }, "artwork": { "description": "An open map of statues, busts, graffitis and other artwork all over the world", "name": "Artworks", @@ -5848,6 +5882,13 @@ } } }, + "23": { + "options": { + "0": { + "question": "With a shelter" + } + } + }, "3": { "options": { "0": { @@ -8636,10 +8677,34 @@ "description": "Layer showing individual parking spaces.", "name": "Parking Spaces", "tagRenderings": { + "access": { + "mappings": { + "0": { + "then": "Anyone can use this parking space." + }, + "1": { + "then": "Anyone can use this parking space." + }, + "2": { + "then": "This parking space is reserved for customers." + }, + "3": { + "then": "This parking space is private and cannot be used by the general public." + }, + "4": { + "then": "This parking space is reserved for permit holders." + } + }, + "question": "Who can use this parking space?", + "render": "Access of parking space: {access}" + }, "capacity": { "mappings": { "0": { "then": "This parking space has 1 space." + }, + "1": { + "then": "This parking space has 1 space." } }, "render": "This parking spaces has {capacity} spaces." @@ -8664,6 +8729,9 @@ "13": { "then": "This is a parking space reserved for car sharing." }, + "14": { + "then": "This is a parking space reserved for women." + }, "2": { "then": "This is a disabled parking space." }, @@ -8795,6 +8863,130 @@ "render": "Physiotherapist {name}" } }, + "picnic_site": { + "description": "Picnic sites for eating outdoors, featuring amenities like toilets, water taps, BBQ, benches and shelters", + "filter": { + "1": { + "options": { + "0": { + "question": "With a firepit" + } + } + }, + "2": { + "options": { + "0": { + "question": "With a BBQ" + } + } + }, + "3": { + "options": { + "0": { + "question": "With drinking water" + } + } + } + }, + "name": "Picnic sites", + "presets": { + "0": { + "description": "A picnic site for eating outdoors, featuring amenities like toilets, water taps, BBQ, benches and shelters", + "title": "a picnic site" + } + }, + "tagRenderings": { + "bbq": { + "mappings": { + "0": { + "then": "This picnic site has a BBQ." + }, + "1": { + "then": "This picnic site does not have a BBQ." + }, + "2": { + "then": "This picnic site has a BBQ, but it is mapped as a different icon." + } + }, + "question": "Does this picnic site have a BBQ?" + }, + "covered": { + "mappings": { + "0": { + "then": "This picnic site is covered." + }, + "1": { + "then": "This picnic site is not covered." + } + }, + "question": "Is this picnic site covered?" + }, + "drinking_water": { + "mappings": { + "0": { + "then": "This picnic site has drinking water." + }, + "1": { + "then": "This picnic site does not have drinking water." + }, + "2": { + "then": "This picnic site has drinking water, but it is mapped as a different icon." + } + }, + "question": "Does this picnic site have drinking water?" + }, + "fireplace": { + "mappings": { + "0": { + "then": "This picnic site has a firepit." + }, + "1": { + "then": "This picnic site does not have a firepit." + }, + "2": { + "then": "This picnic site has a firepit, but it is mapped as a different icon." + } + }, + "question": "Does this picnic site have a firepit?" + }, + "name": { + "override": { + "render": "This picnic site is called {name}" + } + }, + "openfire": { + "mappings": { + "0": { + "then": "Open fire is allowed at this picnic site." + }, + "1": { + "then": "Open fire is not allowed at this picnic site." + }, + "2": { + "then": "Open fire is allowed at this picnic site with a permit." + } + }, + "question": "Is open fire allowed at this picnic site?" + }, + "shelter": { + "mappings": { + "0": { + "then": "This picnic site has a shelter." + }, + "1": { + "then": "This picnic site does not have a shelter." + }, + "2": { + "then": "This picnic site has a shelter, but is is mapped as a different icon." + } + }, + "question": "Does this picnic site have a shelter?" + } + }, + "title": { + "render": "Picnic site" + } + }, "picnic_table": { "description": "The layer showing picnic tables", "name": "Picnic tables", @@ -11263,6 +11455,15 @@ "second_hand": { "question": "Does this shop sell second-hand items?" }, + "self_checkout": { + "override": { + "+mappings": { + "0": { + "then": "This shop (probably) does not offer self-checkout" + } + } + } + }, "sells_new_bikes": { "mappings": { "0": { @@ -13589,13 +13790,6 @@ "transit_stops": { "description": "Layer showing different types of transit stops.", "filter": { - "0": { - "options": { - "0": { - "question": "With a shelter" - } - } - }, "1": { "options": { "0": { diff --git a/langs/layers/es.json b/langs/layers/es.json index 0baced4d3..183f4966a 100644 --- a/langs/layers/es.json +++ b/langs/layers/es.json @@ -5162,6 +5162,13 @@ } } }, + "23": { + "options": { + "0": { + "question": "Con refugio" + } + } + }, "3": { "options": { "0": { @@ -7594,6 +7601,9 @@ "mappings": { "0": { "then": "Esta plaza de aparcamiento tiene 1 plaza." + }, + "1": { + "then": "Esta plaza de aparcamiento tiene 1 plaza." } }, "render": "Esta plaza de aparcamiento tiene {capacity} plazas." @@ -11317,13 +11327,6 @@ "transit_stops": { "description": "Capa que muestra diferentes tipos de paradas de transporte.", "filter": { - "0": { - "options": { - "0": { - "question": "Con refugio" - } - } - }, "1": { "options": { "0": { diff --git a/langs/layers/fr.json b/langs/layers/fr.json index 8190cf247..6edfeb0ce 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -3609,6 +3609,13 @@ } } }, + "23": { + "options": { + "0": { + "question": "Avec un abri" + } + } + }, "6": { "options": { "0": { @@ -6837,13 +6844,6 @@ }, "transit_stops": { "filter": { - "0": { - "options": { - "0": { - "question": "Avec un abri" - } - } - }, "1": { "options": { "0": { diff --git a/langs/layers/it.json b/langs/layers/it.json index 5a210ede3..6fef93020 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -5768,6 +5768,13 @@ } } }, + "23": { + "options": { + "0": { + "question": "Con una pensilina" + } + } + }, "3": { "options": { "0": { @@ -8469,6 +8476,9 @@ "mappings": { "0": { "then": "Questo posto auto ha 1 spazio." + }, + "1": { + "then": "Questo posto auto ha 1 spazio." } }, "render": "Questo posto auto ha {capacity} spazi." @@ -13134,13 +13144,6 @@ "transit_stops": { "description": "Livello che mostra diversi tipi di fermate dei mezzi pubblici.", "filter": { - "0": { - "options": { - "0": { - "question": "Con una pensilina" - } - } - }, "1": { "options": { "0": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index bac059917..41d0f1822 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -7622,10 +7622,34 @@ "description": "Laag met individuele parkeerplekken.", "name": "Parkeerplekken", "tagRenderings": { + "access": { + "mappings": { + "0": { + "then": "Iedereen kan deze parkeerplek gebruiken." + }, + "1": { + "then": "Iedereen kan deze parkeerplek gebruiken." + }, + "2": { + "then": "Deze parkeerplek is gereserveerd voor klanten." + }, + "3": { + "then": "Deze parkeerplek is privé en mag niet door het grote publiek worden gebruikt." + }, + "4": { + "then": "Deze parkeerplek is gereserveerd voor vergunninghouders." + } + }, + "question": "Wie mag deze parkeerplek gebruiken?", + "render": "Toegang tot parkeerplek: {access}" + }, "capacity": { "mappings": { "0": { "then": "Deze parkeerplek heeft 1 plaats." + }, + "1": { + "then": "Deze parkeerplek heeft 1 plaats." } }, "render": "Deze parkeerplek heeft {capacity} plaatsen." @@ -7650,6 +7674,9 @@ "13": { "then": "Deze parkeerplek is gereserveerd voor autodelen." }, + "14": { + "then": "Deze parkeerplek is gereserveerd voor vrouwen." + }, "2": { "then": "Dit is een gehandicaptenparkeerplaats." }, @@ -7780,6 +7807,130 @@ "render": "Kinesist {name}" } }, + "picnic_site": { + "description": "Picknickplaatsen voor het eten in de buitenlucht, met voorzieningen zoals toiletten, waterkranen, BBQ, banken en schuilplaatsen", + "filter": { + "1": { + "options": { + "0": { + "question": "Met een vuurplaats" + } + } + }, + "2": { + "options": { + "0": { + "question": "Met een BBQ" + } + } + }, + "3": { + "options": { + "0": { + "question": "Met drinkwater" + } + } + } + }, + "name": "Picknickplaatsen", + "presets": { + "0": { + "description": "Een picknickplaats voor het eten in de buitenlucht, met voorzieningen zoals toiletten, waterkranen, BBQ, banken en schuilplaatsen", + "title": "een picknickplaats" + } + }, + "tagRenderings": { + "bbq": { + "mappings": { + "0": { + "then": "Deze picknickplaats heeft een BBQ." + }, + "1": { + "then": "Deze picknickplaats heeft geen BBQ." + }, + "2": { + "then": "Deze picknickplaats heeft een BBQ, maar deze staat los op de kaart." + } + }, + "question": "Heeft deze picknickplaats een BBQ?" + }, + "covered": { + "mappings": { + "0": { + "then": "Deze picknickplaats is overdekt." + }, + "1": { + "then": "Deze picknickplaats is niet overdekt." + } + }, + "question": "Is deze picknickplaats overdekt?" + }, + "drinking_water": { + "mappings": { + "0": { + "then": "Deze picknickplaats heeft drinkwater." + }, + "1": { + "then": "Deze picknickplaats heeft geen drinkwater." + }, + "2": { + "then": "Deze picknickplaats heeft drinkwater, maar deze staat los op de kaart." + } + }, + "question": "Heeft deze picknickplaats drinkwater?" + }, + "fireplace": { + "mappings": { + "0": { + "then": "Deze picknickplaats heeft een vuurplaats." + }, + "1": { + "then": "Deze picknickplaats heeft geen vuurplaats." + }, + "2": { + "then": "Deze picknickplaats heeft een vuurplaats, maar deze staat los op de kaart." + } + }, + "question": "Heeft deze picknickplaats een vuurplaats?" + }, + "name": { + "override": { + "render": "Deze picknickplaats heet {name}" + } + }, + "openfire": { + "mappings": { + "0": { + "then": "Open vuur is toegestaan op deze picknickplaats." + }, + "1": { + "then": "Open vuur is niet toegestaan op deze picknickplaats." + }, + "2": { + "then": "Open vuur is toegestaan op deze picknickplaats met een vergunning." + } + }, + "question": "Is open vuur toegestaan op deze picknickplaats?" + }, + "shelter": { + "mappings": { + "0": { + "then": "Deze picknickplaats heeft een schuilplaats." + }, + "1": { + "then": "Deze picknickplaats heeft geen schuilplaats." + }, + "2": { + "then": "Deze picknickplaats heeft een schuilplaats, maar deze staat los op de kaart." + } + }, + "question": "Heeft deze picknickplaats een schuilplaats?" + } + }, + "title": { + "render": "Picknickplaats" + } + }, "picnic_table": { "description": "Deze laag toont picknicktafels", "name": "Picknicktafels", diff --git a/langs/themes/en.json b/langs/themes/en.json index de5ff9837..0c7b8469d 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -8,6 +8,10 @@ "description": "On this map, one can find and mark nearby defibrillators", "title": "Defibrillators" }, + "arcade": { + "description": "A map of arcades", + "title": "Arcades" + }, "architecture": { "description": "A map showing the architectural style of buildings", "title": "Buildings with an architectural style" From 55632b738b0e466a3bf10bc897ed9ecede012b8b Mon Sep 17 00:00:00 2001 From: Osmwithspace <> Date: Tue, 26 Aug 2025 09:05:28 +0200 Subject: [PATCH 12/34] add hut.json --- assets/layers/hut/hut.json | 160 +++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 assets/layers/hut/hut.json diff --git a/assets/layers/hut/hut.json b/assets/layers/hut/hut.json new file mode 100644 index 000000000..563a37332 --- /dev/null +++ b/assets/layers/hut/hut.json @@ -0,0 +1,160 @@ +{ + "id": "hut", + "name": { + "en": "Huts" + }, + "description": { + "en": "Layer showing basic huts, wilderness huts and alpine huts" + }, + "source": { + "osmTags": { + "or": [ + "tourism=wilderness_hut", + "tourism=alpine_hut", + { + "and": [ + "amenity=shelter", + "shelter_type=basic_hut" + ] + } + ] + } + }, + "minzoom": 10, + "title": { + "render": "Hut", + "mappings": [ + { + "if": "name~*", + "then": "{name}" + }, + { + "if": "tourism=wilderness_hut", + "then": "wilderness hut" + }, + { + "if": "tourism=alpine_hut", + "then": "alpine hut" + }, + { + "if": { + "and": [ + "amenity=shelter", + "shelter_type=basic_hut" + ] + }, + "then": "basic hut" + } + ] + }, + "pointRendering": [ + { + "location": [ + "point", + "centroid" + ], + "marker": [ + { + "icon": { + "render": "./assets/layers/shelter/shelter.svg", + "mappings": [ + { + "if": "tourism=wilderness_hut", + "then": "https://wiki.openstreetmap.org/w/images/8/8e/Wilderness_hut.svg" + }, + { + "if": "tourism=alpine_hut", + "then": "https://wiki.openstreetmap.org/w/images/f/f1/Alpinehut.svg" + }, + { + "if": { + "and": [ + "amenity=shelter", + "shelter_type=basic_hut" + ] + }, + "then": "./assets/layers/shelter/shelter.svg" + } + ] + } + } + ] + } + ], + "lineRendering": [], + "presets": [ + { + "tags": [ + "tourism=wilderness_hut" + ], + "title": { + "en": "wilderness hut" + }, + "description": { + "en": "An unserviced fully enclosed hut (with roof and walls) with beds or suitable sleeping areas and a fireplace or stove for heating and cooking." + } + }, + { + "tags": [ + "tourism=alpine_hut" + ], + "title": { + "en": "alpine hut" + }, + "description": { + "en": "A serviced remote building located in the mountains intended to provide board and lodging." + } + }, + { + "tags": [ + "amenity=shelter", + "shelter_type=basic_hut" + ], + "title": { + "en": "basic hut" + }, + "description": { + "en": "An unserviced fully enclosed hut (with roof and walls) with beds or suitable sleeping areas without a fireplace or stove." + } + } + ], + "tagRenderings": [ + "images", + "name", + "reservation", + "caravansites.caravansites-fee", + { + "id": "drinking_water", + "question": { + "en": "Is drinking water available here?" + }, + "mappings": [ + { + "if": "drinking_water=yes", + "then": { + "en": "Here is drinking water available." + } + }, + { + "if": "drinking_water=no", + "then": { + "en": "Here is no drinking water available." + } + } + ] + }, + "has_toilets", + "description", + { + "id": "preset_type", + "render": "{preset_type_select()}" + } + ], + "filter":[ + "free" + ], + "allowMove": { + "enableRelocation": false, + "enableImproveAccuracy": true + } +} \ No newline at end of file From 32bc69bb32942dae5c859a4c1d223fc9b34105d3 Mon Sep 17 00:00:00 2001 From: Osmwithspace <> Date: Tue, 26 Aug 2025 22:54:04 +0200 Subject: [PATCH 13/34] replace remote icons --- assets/layers/hut/alpine_hut.svg | 15 +++++++++++++ assets/layers/hut/alpine_hut.svg.license | 2 ++ assets/layers/hut/hut.json | 4 ++-- assets/layers/hut/license_info.json | 22 ++++++++++++++++++++ assets/layers/hut/wilderness_hut.svg | 15 +++++++++++++ assets/layers/hut/wilderness_hut.svg.license | 2 ++ 6 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 assets/layers/hut/alpine_hut.svg create mode 100644 assets/layers/hut/alpine_hut.svg.license create mode 100644 assets/layers/hut/license_info.json create mode 100644 assets/layers/hut/wilderness_hut.svg create mode 100644 assets/layers/hut/wilderness_hut.svg.license diff --git a/assets/layers/hut/alpine_hut.svg b/assets/layers/hut/alpine_hut.svg new file mode 100644 index 000000000..e790edd62 --- /dev/null +++ b/assets/layers/hut/alpine_hut.svg @@ -0,0 +1,15 @@ + + + + + + image/svg+xml + + + + + + + + + \ No newline at end of file diff --git a/assets/layers/hut/alpine_hut.svg.license b/assets/layers/hut/alpine_hut.svg.license new file mode 100644 index 000000000..5e3616ed7 --- /dev/null +++ b/assets/layers/hut/alpine_hut.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Geozeisig +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/hut/hut.json b/assets/layers/hut/hut.json index 563a37332..2aac48426 100644 --- a/assets/layers/hut/hut.json +++ b/assets/layers/hut/hut.json @@ -60,11 +60,11 @@ "mappings": [ { "if": "tourism=wilderness_hut", - "then": "https://wiki.openstreetmap.org/w/images/8/8e/Wilderness_hut.svg" + "then": "./assets/layers/hut/wilderness_hut.svg" }, { "if": "tourism=alpine_hut", - "then": "https://wiki.openstreetmap.org/w/images/f/f1/Alpinehut.svg" + "then": "./assets/layers/hut/alpine_hut.svg" }, { "if": { diff --git a/assets/layers/hut/license_info.json b/assets/layers/hut/license_info.json new file mode 100644 index 000000000..f8341f4f8 --- /dev/null +++ b/assets/layers/hut/license_info.json @@ -0,0 +1,22 @@ +[ + { + "path": "alpine_hut.svg", + "license": "CC0-1.0", + "authors": [ + "Geozeisig" + ], + "sources": [ + "https://wiki.openstreetmap.org/wiki/File:Alpinehut.svg" + ] + }, + { + "path": "wilderness_hut.svg", + "license": "CC0-1.0", + "authors": [ + "Geozeisig" + ], + "sources": [ + "https://wiki.openstreetmap.org/wiki/File:Wilderness_hut.svg" + ] + } +] \ No newline at end of file diff --git a/assets/layers/hut/wilderness_hut.svg b/assets/layers/hut/wilderness_hut.svg new file mode 100644 index 000000000..cbf146077 --- /dev/null +++ b/assets/layers/hut/wilderness_hut.svg @@ -0,0 +1,15 @@ + + + + + + image/svg+xml + + + + + + + + + \ No newline at end of file diff --git a/assets/layers/hut/wilderness_hut.svg.license b/assets/layers/hut/wilderness_hut.svg.license new file mode 100644 index 000000000..5e3616ed7 --- /dev/null +++ b/assets/layers/hut/wilderness_hut.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Geozeisig +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file From 05631bb32db7ad31c4ddd46dd8448600999ee014 Mon Sep 17 00:00:00 2001 From: Osmwithspace <> Date: Tue, 26 Aug 2025 22:57:17 +0200 Subject: [PATCH 14/34] add hut layer in nature theme --- assets/themes/nature/nature.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/themes/nature/nature.json b/assets/themes/nature/nature.json index 7158a1d5e..503d53129 100644 --- a/assets/themes/nature/nature.json +++ b/assets/themes/nature/nature.json @@ -60,7 +60,8 @@ "nature_reserve", { "builtin": [ - "shelter" + "shelter", + "hut" ], "override": { "minzoom": 11 From 94fbf1eccf99894eda4d5094eec744a931df4079 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 27 Aug 2025 00:05:14 +0200 Subject: [PATCH 15/34] Refactoring: move methods out of 'utils.ts' --- .../Actors/SelectedElementTagsUpdater.ts | 3 +- .../Actors/SaveFeatureSourceToLocalStorage.ts | 4 +- .../Sources/ChangeGeometryApplicator.ts | 4 +- .../Sources/OverpassFeatureSource.ts | 4 +- src/Logic/Search/FilterSearch.ts | 4 +- src/Logic/Search/LayerSearch.ts | 4 +- src/Logic/Search/LocalElementSearch.ts | 5 +- src/Logic/Search/SearchUtils.ts | 5 +- src/Logic/UIEventSource.ts | 3 +- .../ThemeConfig/Conversion/Validation.ts | 8 +- .../TagRendering/TagRenderingEditable.svelte | 3 +- src/UI/Studio/EditLayerState.ts | 10 +- src/Utils.ts | 99 ------------------- src/Utils/Lists.ts | 49 +++++++-- src/Utils/Objects.ts | 33 +++++++ src/Utils/Strings.ts | 38 +++++++ 16 files changed, 140 insertions(+), 136 deletions(-) create mode 100644 src/Utils/Objects.ts diff --git a/src/Logic/Actors/SelectedElementTagsUpdater.ts b/src/Logic/Actors/SelectedElementTagsUpdater.ts index 67a7c2b44..f34ec863e 100644 --- a/src/Logic/Actors/SelectedElementTagsUpdater.ts +++ b/src/Logic/Actors/SelectedElementTagsUpdater.ts @@ -10,6 +10,7 @@ import { Changes } from "../Osm/Changes" import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig" import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore" import { WithChangesState } from "../../Models/ThemeViewState/WithChangesState" +import Objects from "../../Utils/Objects" export default class SelectedElementTagsUpdater { private static readonly metatags = new Set([ @@ -160,7 +161,7 @@ export default class SelectedElementTagsUpdater { const newGeometry = osmObject.asGeoJson()?.geometry const oldFeature = state.indexedFeatures.featuresById.data.get(id) const oldGeometry = oldFeature?.geometry - if (oldGeometry !== undefined && !Utils.SameObject(newGeometry, oldGeometry)) { + if (oldGeometry !== undefined && !Objects.sameObject(newGeometry, oldGeometry)) { console.log("Detected a difference in geometry for ", id) this.invalidateCache(s) oldFeature.geometry = newGeometry diff --git a/src/Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage.ts b/src/Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage.ts index c9cb4b04c..8f3d009f2 100644 --- a/src/Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage.ts +++ b/src/Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage.ts @@ -4,9 +4,9 @@ import TileLocalStorage from "./TileLocalStorage" import { GeoOperations } from "../../GeoOperations" import FeaturePropertiesStore from "./FeaturePropertiesStore" import { UIEventSource } from "../../UIEventSource" -import { Utils } from "../../../Utils" import { Tiles } from "../../../Models/TileRange" import { BBox } from "../../BBox" +import { Lists } from "../../../Utils/Lists" class SingleTileSaver { private readonly _storage: UIEventSource @@ -31,7 +31,7 @@ class SingleTileSaver { } public saveFeatures(features: Feature[]) { - if (Utils.sameList(features, this._storage.data)) { + if (Lists.sameList(features, this._storage.data)) { return } for (const feature of features) { diff --git a/src/Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts b/src/Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts index dee5f8994..90cc9ef1b 100644 --- a/src/Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts +++ b/src/Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts @@ -6,7 +6,7 @@ import { Stores, UIEventSource } from "../../UIEventSource" import { FeatureSource, IndexedFeatureSource } from "../FeatureSource" import { ChangeDescription, ChangeDescriptionTools } from "../../Osm/Actions/ChangeDescription" import { Feature } from "geojson" -import { Utils } from "../../../Utils" +import Objects from "../../../Utils/Objects" export default class ChangeGeometryApplicator implements FeatureSource { public readonly features: UIEventSource = new UIEventSource([]) @@ -69,7 +69,7 @@ export default class ChangeGeometryApplicator implements FeatureSource { // We only apply the last change as that one'll have the latest geometry const change = changesForFeature[changesForFeature.length - 1] copy.geometry = ChangeDescriptionTools.getGeojsonGeometry(change) - if (Utils.SameObject(copy.geometry, feature.geometry)) { + if (Objects.sameObject(copy.geometry, feature.geometry)) { // No actual changes: pass along the original newFeatures.push(feature) continue diff --git a/src/Logic/FeatureSource/Sources/OverpassFeatureSource.ts b/src/Logic/FeatureSource/Sources/OverpassFeatureSource.ts index 37be66206..7cbad0368 100644 --- a/src/Logic/FeatureSource/Sources/OverpassFeatureSource.ts +++ b/src/Logic/FeatureSource/Sources/OverpassFeatureSource.ts @@ -9,7 +9,7 @@ import { BBox } from "../../BBox" import { OsmFeature } from "../../../Models/OsmFeature" import { Lists } from "../../../Utils/Lists" -;("use strict") +("use strict") /** * A wrapper around the 'Overpass'-object. @@ -229,7 +229,7 @@ export default class OverpassFeatureSource im const requestedBounds = this.state.bounds.data if ( this._lastQueryBBox !== undefined && - Utils.sameList(this._layersToDownload.data, this._lastRequestedLayers) && + Lists.sameList(this._layersToDownload.data, this._lastRequestedLayers) && requestedBounds.isContainedIn(this._lastQueryBBox) ) { return undefined diff --git a/src/Logic/Search/FilterSearch.ts b/src/Logic/Search/FilterSearch.ts index 6dbf346f2..7cc80b653 100644 --- a/src/Logic/Search/FilterSearch.ts +++ b/src/Logic/Search/FilterSearch.ts @@ -32,7 +32,7 @@ export default class FilterSearch { .split(" ") .map((query) => { if (!Strings.isEmoji(query)) { - return Utils.simplifyStringForSearch(query) + return Strings.simplifyStringForSearch(query) } return query }) @@ -64,7 +64,7 @@ export default class FilterSearch { option.searchTerms?.["en"] ?? []), ].flatMap((term) => [term, ...(term?.split(" ") ?? [])]) - terms = terms.map((t) => Utils.simplifyStringForSearch(t)) + terms = terms.map((t) => Strings.simplifyStringForSearch(t)) terms.push(option.emoji) Lists.noNullInplace(terms) const distances = queries.flatMap((query) => diff --git a/src/Logic/Search/LayerSearch.ts b/src/Logic/Search/LayerSearch.ts index 3d23767c4..ebe644122 100644 --- a/src/Logic/Search/LayerSearch.ts +++ b/src/Logic/Search/LayerSearch.ts @@ -2,7 +2,7 @@ import SearchUtils from "./SearchUtils" import ThemeSearch from "./ThemeSearch" import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig" -import { Utils } from "../../Utils" +import { Strings } from "../../Utils/Strings" export default class LayerSearch { private readonly _theme: ThemeConfig @@ -24,7 +24,7 @@ export default class LayerSearch { const queryParts = query .trim() .split(" ") - .map((q) => Utils.simplifyStringForSearch(q)) + .map((q) => Strings.simplifyStringForSearch(q)) for (const id in ThemeSearch.officialThemes.layers) { if (options?.whitelist && !options?.whitelist.has(id)) { continue diff --git a/src/Logic/Search/LocalElementSearch.ts b/src/Logic/Search/LocalElementSearch.ts index b34e34bf4..e4d697816 100644 --- a/src/Logic/Search/LocalElementSearch.ts +++ b/src/Logic/Search/LocalElementSearch.ts @@ -6,6 +6,7 @@ import { GeoOperations } from "../GeoOperations" import { ImmutableStore, Store, Stores } from "../UIEventSource" import OpenStreetMapIdSearch from "./OpenStreetMapIdSearch" import { Lists } from "../../Utils/Lists" +import { Strings } from "../../Utils/Strings" type IntermediateResult = { feature: Feature @@ -61,7 +62,7 @@ export default class LocalElementSearch implements GeocodingProvider { ...searchTerms .flatMap((entry) => entry.split(/ /)) .map((entry) => { - let simplified = Utils.simplifyStringForSearch(entry) + let simplified = Strings.simplifyStringForSearch(entry) if (matchStart) { simplified = simplified.slice(0, query.length) } @@ -103,7 +104,7 @@ export default class LocalElementSearch implements GeocodingProvider { const centerPoint: [number, number] = [center.lon, center.lat] const properties = this._state.perLayer const candidateId = OpenStreetMapIdSearch.extractId(query) - query = Utils.simplifyStringForSearch(query) + query = Strings.simplifyStringForSearch(query) const partials: Store[] = [] diff --git a/src/Logic/Search/SearchUtils.ts b/src/Logic/Search/SearchUtils.ts index a50ac5bf7..e5f993de2 100644 --- a/src/Logic/Search/SearchUtils.ts +++ b/src/Logic/Search/SearchUtils.ts @@ -2,6 +2,7 @@ import Locale from "../../UI/i18n/Locale" import { Utils } from "../../Utils" import ThemeSearch from "./ThemeSearch" import { Lists } from "../../Utils/Lists" +import { Strings } from "../../Utils/Strings" export default class SearchUtils { /** Applies special search terms, such as 'studio', 'osmcha', ... @@ -60,7 +61,7 @@ export default class SearchUtils { const queryParts = query .trim() .split(" ") - .map((q) => Utils.simplifyStringForSearch(q)) + .map((q) => Strings.simplifyStringForSearch(q)) let terms: string[] if (Array.isArray(keywords)) { terms = keywords @@ -74,7 +75,7 @@ export default class SearchUtils { const q = queryParts[i] let minDistance: number = 99 for (const term of termsAll) { - const d = Utils.levenshteinDistance(q, Utils.simplifyStringForSearch(term)) + const d = Utils.levenshteinDistance(q, Strings.simplifyStringForSearch(term)) if (d < minDistance) { minDistance = d } diff --git a/src/Logic/UIEventSource.ts b/src/Logic/UIEventSource.ts index c4af67e6e..3790be513 100644 --- a/src/Logic/UIEventSource.ts +++ b/src/Logic/UIEventSource.ts @@ -1,5 +1,6 @@ import { Utils } from "../Utils" import { Readable, Subscriber, Unsubscriber, Updater, Writable } from "svelte/store" +import { Lists } from "../Utils/Lists" /** * Various static utils @@ -66,7 +67,7 @@ export class Stores { stable.setData(undefined) return } - if (Utils.sameList(stable.data, list)) { + if (Lists.sameList(stable.data, list)) { return } stable.setData(list) diff --git a/src/Models/ThemeConfig/Conversion/Validation.ts b/src/Models/ThemeConfig/Conversion/Validation.ts index 4d4919442..c4383e497 100644 --- a/src/Models/ThemeConfig/Conversion/Validation.ts +++ b/src/Models/ThemeConfig/Conversion/Validation.ts @@ -25,6 +25,7 @@ import { eliCategory } from "../../RasterLayerProperties" import licenses from "../../../assets/generated/license_info.json" import { Strings } from "../../../Utils/Strings" import { Lists } from "../../../Utils/Lists" +import Objects from "../../../Utils/Objects" export class ValidateLanguageCompleteness extends DesugaringStep { private readonly _languages: string[] @@ -1085,11 +1086,8 @@ export class DetectDuplicatePresets extends DesugaringStep { const presetBTags = optimizedTags[j] const presetB = presets[j] if ( - Utils.SameObject(presetATags, presetBTags) && - Utils.sameList( - presetA.preciseInput.snapToLayers, - presetB.preciseInput.snapToLayers - ) + Objects.sameObject(presetATags, presetBTags) && + Lists.sameList(presetA.preciseInput.snapToLayers, presetB.preciseInput.snapToLayers) ) { context.err( `This theme has multiple presets with the same tags: ${presetATags.asHumanString( diff --git a/src/UI/Popup/TagRendering/TagRenderingEditable.svelte b/src/UI/Popup/TagRendering/TagRenderingEditable.svelte index c0b16be48..78f7c3006 100644 --- a/src/UI/Popup/TagRendering/TagRenderingEditable.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingEditable.svelte @@ -12,6 +12,7 @@ import { Utils } from "../../../Utils" import { twMerge } from "tailwind-merge" import EditButton from "./EditButton.svelte" + import { Strings } from "../../../Utils/Strings" export let config: TagRenderingConfig export let tags: UIEventSource> @@ -83,7 +84,7 @@ onDestroy(highlightedRendering?.addCallbackAndRun(() => setHighlighting())) onDestroy(_htmlElement.addCallbackAndRun(() => setHighlighting())) } - let answerId = "answer-" + Utils.randomString(5) + let answerId = "answer-" + Strings.randomString(5) let debug = state?.featureSwitches?.featureSwitchIsDebugging ?? new ImmutableStore(false) let apiState: Store = state?.osmConnection?.apiIsOnline ?? new ImmutableStore("online") diff --git a/src/UI/Studio/EditLayerState.ts b/src/UI/Studio/EditLayerState.ts index f73137ab5..594776421 100644 --- a/src/UI/Studio/EditLayerState.ts +++ b/src/UI/Studio/EditLayerState.ts @@ -1,12 +1,7 @@ import { ConfigMeta } from "./configMeta" import { Store, UIEventSource } from "../../Logic/UIEventSource" import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson" -import { - Conversion, - ConversionMessage, - DesugaringContext, - Pipe, -} from "../../Models/ThemeConfig/Conversion/Conversion" +import { Conversion, ConversionMessage, DesugaringContext, Pipe } from "../../Models/ThemeConfig/Conversion/Conversion" import { PrepareLayer } from "../../Models/ThemeConfig/Conversion/PrepareLayer" import { PrevalidateTheme, ValidateLayer } from "../../Models/ThemeConfig/Conversion/Validation" import { AllSharedLayers } from "../../Customizations/AllSharedLayers" @@ -26,6 +21,7 @@ import { TagRenderingConfigJson } from "../../Models/ThemeConfig/Json/TagRenderi import { ValidateTheme } from "../../Models/ThemeConfig/Conversion/ValidateTheme" import * as questions from "../../../public/assets/generated/layers/questions.json" import { Lists } from "../../Utils/Lists" +import { Strings } from "../../Utils/Strings" export interface HighlightedTagRendering { path: ReadonlyArray @@ -431,7 +427,7 @@ export default class EditLayerState extends EditJsonState { } if (!tr["id"] && !tr["override"]) { const qtr = tr - let id = "" + i + "_" + Utils.randomString(5) + let id = "" + i + "_" + Strings.randomString(5) if (qtr?.freeform?.key) { id = qtr?.freeform?.key } else if (qtr.mappings?.[0]?.if) { diff --git a/src/Utils.ts b/src/Utils.ts index c4f203b6d..123818e2c 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -1378,66 +1378,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be } element.scrollIntoView({ behavior: "smooth", block: "nearest" }) } - - /** - * Returns true if the contents of `a` are the same (and in the same order) as `b`. - * Might have false negatives in some cases - * @param a - * @param b - */ - public static sameList(a: ReadonlyArray, b: ReadonlyArray) { - if (a == b) { - return true - } - if (a === undefined || a === null || b === undefined || b === null) { - return false - } - if (a.length !== b.length) { - return false - } - for (let i = 0; i < a.length; i++) { - const ai = a[i] - const bi = b[i] - if (ai == bi) { - continue - } - if (ai === bi) { - continue - } - return false - } - return true - } - - public static SameObject(a: T, b: T, ignoreKeys?: string[]): boolean { - if (a === b) { - return true - } - if (a === undefined || a === null || b === null || b === undefined) { - return false - } - if (typeof a === "object" && typeof b === "object") { - for (const aKey in a) { - if (!(aKey in b)) { - return false - } - } - - for (const bKey in b) { - if (!(bKey in a)) { - return false - } - } - for (const k in a) { - if (!Utils.SameObject(a[k], b[k])) { - return false - } - } - return true - } - return false - } - /** * * Utils.splitIntoSubstitutionParts("abc") // => [{message: "abc"}] @@ -1510,45 +1450,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be filename: path.substring(path.lastIndexOf("/") + 1), } } - - /** - * Removes accents from a string - * @param str - * @constructor - * - * Utils.RemoveDiacritics("bâtiments") // => "batiments" - * Utils.RemoveDiacritics(undefined) // => undefined - */ - public static RemoveDiacritics(str?: string): string { - // See #1729 - if (!str) { - return str - } - return str.normalize("NFD").replace(/\p{Diacritic}/gu, "") - } - - /** - * Simplifies a string to increase the chance of a match - * @param str - * Utils.simplifyStringForSearch("abc def; ghi 564") // => "abcdefghi564" - * Utils.simplifyStringForSearch("âbc déf; ghi 564") // => "abcdefghi564" - * Utils.simplifyStringForSearch(undefined) // => undefined - */ - public static simplifyStringForSearch(str: string): string { - return Utils.RemoveDiacritics(str) - ?.toLowerCase() - ?.replace(/[^a-z0-9]/g, "") - } - - public static randomString(length: number): string { - let result = "" - for (let i = 0; i < length; i++) { - const chr = Math.random().toString(36).substr(2, 3) - result += chr - } - return result - } - /** * Recursively rewrites all keys from `+key`, `key+` and `=key` into `key * diff --git a/src/Utils/Lists.ts b/src/Utils/Lists.ts index 3e0b71b57..7d06a395a 100644 --- a/src/Utils/Lists.ts +++ b/src/Utils/Lists.ts @@ -43,12 +43,15 @@ export class Lists { * Elements are returned in the same order as they appear in the lists. * Null/Undefined is returned as is. If an empty array is given, a new empty array will be returned */ - public static dedup(arr: NonNullable): NonNullable + public static dedup(arr: NonNullable>): NonNullable public static dedup(arr: undefined): undefined - public static dedup(arr: string[] | undefined): string[] | undefined - public static dedup(arr: string[]): string[] { - if (arr === undefined || arr === null) { - return arr + public static dedup(arr: ReadonlyArray | undefined): string[] | undefined + public static dedup(arr: ReadonlyArray): string[] { + if (arr === undefined) { + return undefined + } + if (arr === null) { + return null } const newArr = [] for (const string of arr) { @@ -60,8 +63,8 @@ export class Lists { } public static dedupT(arr: ReadonlyArray): T[] - public static dedupT(arr: null): null - public static dedupT(arr: undefined): undefined + public static dedupT(arr: null): null + public static dedupT(arr: undefined): undefined public static dedupT(arr: ReadonlyArray): T[] { if (arr === undefined) { return undefined @@ -160,7 +163,7 @@ export class Lists { * Lists.duplicates(["a", "b","c","b","b"] // => ["b"] * */ - public static duplicates(arr: string[]): string[] { + public static duplicates(arr: ReadonlyArray): string[] { if (arr === undefined) { return undefined } @@ -175,4 +178,34 @@ export class Lists { return Array.from(duplicates) } + /** + * Returns true if the contents of `a` are the same (and in the same order) as `b`. + * Might have false negatives in some cases + * @param a + * @param b + */ + public static sameList(a: ReadonlyArray, b: ReadonlyArray): boolean { + if (a == b) { + return true + } + if (a === undefined || a === null || b === undefined || b === null) { + return false + } + if (a.length !== b.length) { + return false + } + for (let i = 0; i < a.length; i++) { + const ai = a[i] + const bi = b[i] + if (ai == bi) { + continue + } + if (ai === bi) { + continue + } + return false + } + return true + } + } diff --git a/src/Utils/Objects.ts b/src/Utils/Objects.ts new file mode 100644 index 000000000..1d8b95c15 --- /dev/null +++ b/src/Utils/Objects.ts @@ -0,0 +1,33 @@ +/** + * Various object-related utils + */ +export default class Objects { + public static sameObject(a: T, b: T, ignoreKeys?: string[]): boolean { + if (a === b) { + return true + } + if (a === undefined || a === null || b === null || b === undefined) { + return false + } + if (typeof a === "object" && typeof b === "object") { + for (const aKey in a) { + if (!(aKey in b)) { + return false + } + } + + for (const bKey in b) { + if (!(bKey in a)) { + return false + } + } + for (const k in a) { + if (!Objects.sameObject(a[k], b[k])) { + return false + } + } + return true + } + return false + } +} diff --git a/src/Utils/Strings.ts b/src/Utils/Strings.ts index ae619a3e4..709014115 100644 --- a/src/Utils/Strings.ts +++ b/src/Utils/Strings.ts @@ -21,4 +21,42 @@ export class Strings { public static isEmojiFlag(string: string): boolean { return /[🇦-🇿]{2}/u.test(string) // flags, see https://stackoverflow.com/questions/53360006/detect-with-regex-if-emoji-is-country-flag } + + /** + * Removes accents from a string + * @param str + * @constructor + * + * Strings.removeDiacritics("bâtiments") // => "batiments" + * Strings.removeDiacritics(undefined) // => undefined + */ + public static removeDiacritics(str?: string): string { + // See #1729 + if (!str) { + return str + } + return str.normalize("NFD").replace(/\p{Diacritic}/gu, "") + } + + /** + * Simplifies a string to increase the chance of a match + * @param str + * Strings.simplifyStringForSearch("abc def; ghi 564") // => "abcdefghi564" + * Strings.simplifyStringForSearch("âbc déf; ghi 564") // => "abcdefghi564" + * Strings.simplifyStringForSearch(undefined) // => undefined + */ + public static simplifyStringForSearch(str: string): string { + return Strings.removeDiacritics(str) + ?.toLowerCase() + ?.replace(/[^a-z0-9]/g, "") + } + + public static randomString(length: number): string { + let result = "" + for (let i = 0; i < length; i++) { + const chr = Math.random().toString(36).substr(2, 3) + result += chr + } + return result + } } From ea0c87b3ff220c3554289b7daf8f255ab01f1715 Mon Sep 17 00:00:00 2001 From: Osmwithspace <> Date: Wed, 27 Aug 2025 09:55:17 +0200 Subject: [PATCH 16/34] add shelter type question in hut layer --- assets/layers/hut/hut.json | 112 +++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/assets/layers/hut/hut.json b/assets/layers/hut/hut.json index 2aac48426..681d1d06d 100644 --- a/assets/layers/hut/hut.json +++ b/assets/layers/hut/hut.json @@ -148,6 +148,118 @@ { "id": "preset_type", "render": "{preset_type_select()}" + }, + { + "id": "shelter-type", + "question": { + "en": "What kind of shelter is this?", + "ca": "Quin tipus de refugi és aquest?", + "cs": "Co je to za přístřešek?", + "de": "Um welche Art von Unterstand handelt es sich?", + "es": "¿Qué tipo de refugio es este?", + "it": "Che tipo di riparo è questo?", + "nl": "Wat voor schuilplaats is dit?", + "uk": "Що це за притулок?" + }, + "render": { + "en": "Shelter type: {shelter_type}", + "ca": "Tipus de refugi: {shelter_type}", + "cs": "Typ přístřešku: {shelter_type}", + "de": "Art des Unterstands: {shelter_type}", + "es": "Tipo de refugio: {shelter_type}", + "it": "Tipo di riparo: {shelter_type}", + "uk": "Тип притулку: {shelter_type}" + }, + "freeform": { + "key": "shelter_type", + "type": "string" + }, + "condition": { + "and": [ + "amenity=shelter", + "shelter_type=basic_hut" + ] + }, + "mappings": [ + { + "if": "shelter_type=public_transport", + "then": { + "en": "This is a shelter at a public transport stop.", + "ca": "Es tracta d'un refugi en una parada de transport públic.", + "cs": "Jedná se o přístřešek u zastávky MHD.", + "de": "Das ist ein Unterstand an einer Haltestelle für öffentliche Verkehrsmittel.", + "es": "Este es un refugio en una parada de transporte público.", + "it": "Questo è un riparo presso una fermata del trasporto pubblico.", + "nl": "Dit is een schuilplaats bij een halte voor openbaar vervoer.", + "uk": "Це притулок на зупинці громадського транспорту." + } + }, + { + "if": "shelter_type=picnic_shelter", + "then": { + "en": "This is a shelter protecting from rain at a picnic site.", + "ca": "Es tracta d'un refugi que protegeix de la pluja en un lloc de pícnic.", + "cs": "Jedná se o přístřešek chránící před deštěm na piknikovém místě.", + "de": "Dies ist ein Unterstand zum Schutz vor Regen auf einem Picknickplatz.", + "es": "Este es un refugio que protege de la lluvia en un área de picnic.", + "it": "Questo è un riparo che protegge dalla pioggia in un'area picnic.", + "uk": "Це накриття, щ�� захищає від дощу на місці для пікніка." + } + }, + { + "if": "shelter_type=gazebo", + "then": { + "en": "This is a gazebo.", + "ca": "Això és una glorieta.", + "cs": "Toto je altán.", + "de": "Das ist ein offener Gartenpavillon.", + "es": "Este es un cenador.", + "it": "Questo è un gazebo.", + "uk": "Це альтанка." + } + }, + { + "if": "shelter_type=weather_shelter", + "then": { + "en": "This is a small shelter, primarily intended for short breaks. Usually found in the mountains or alongside roads.", + "ca": "Es tracta d'un petit refugi, principalment destinat a descansos curts. Normalment es troba a les muntanyes o al costat de les carreteres.", + "cs": "Jedná se o malý přístřešek, primárně určený pro krátké přestávky. Obvykle se vyskytuje v horách nebo podél silnic.", + "de": "Dies ist ein kleiner Unterstand, der vor allem für kurze Pausen gedacht ist. Normalerweise findet man ihn in Bergen oder an Straßen.", + "es": "Este es un refugio pequeño, principalmente destinado a descansos cortos. Normalmente se encuentra en las montañas o al lado de las carreteras.", + "it": "Questo è un piccolo riparo, principalmente destinato a brevi pause. Si trova solitamente in montagna o lungo le strade.", + "uk": "Це невеликий притулок, призначений насамперед для коротких перерв. Зазвичай знаходиться в горах або вздовж доріг." + } + }, + { + "if": "shelter_type=lean_to", + "then": { + "en": "This is a shed with 3 walls, primarily intended for camping.", + "ca": "Es tracta d'un cobert amb 3 parets, destinat principalment a l'acampada.", + "cs": "Jedná se o přístřešek se 3 stěnami, primárně určený pro kempování.", + "da": "Dette er et skur med 3 vægge, primært beregnet til camping.", + "de": "Es handelt sich um einen an 3 Seiten geschlossenen Unterstand, der in erster Linie zum Campen gedacht ist.", + "es": "Este es un cobertizo con 3 paredes, principalmente destinado para acampar.", + "it": "Questa è una tettoia con 3 pareti, principalmente destinata al campeggio.", + "uk": "Це сарай з 3-ма стінами, в першу чергу призначений для кемпінгу." + } + }, + { + "if": "shelter_type=pavilion", + "then": { + "en": "This is a pavilion", + "ca": "Aquest és un pavelló", + "cs": "Toto je pavilon", + "de": "Das ist ein Pavillon", + "es": "Este es un pabellón", + "it": "Questo è un padiglione", + "uk": "Це павільйон" + } + }, + { + "if": "shelter_type=basic_hut", + "then": "This is a basic hut, providing basic shelter and sleeping facilities." + } + ] } ], "filter":[ From 95898fdfcddbb0608270c5d38952d01520c03212 Mon Sep 17 00:00:00 2001 From: Osmwithspace <> Date: Wed, 27 Aug 2025 09:58:21 +0200 Subject: [PATCH 17/34] add website question --- assets/layers/hut/hut.json | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/layers/hut/hut.json b/assets/layers/hut/hut.json index 681d1d06d..8f641e211 100644 --- a/assets/layers/hut/hut.json +++ b/assets/layers/hut/hut.json @@ -121,6 +121,7 @@ "tagRenderings": [ "images", "name", + "website", "reservation", "caravansites.caravansites-fee", { From 3ad699b077b0b04c80607222c3825a7445e67fe5 Mon Sep 17 00:00:00 2001 From: Osmwithspace <> Date: Wed, 27 Aug 2025 10:33:15 +0200 Subject: [PATCH 18/34] use builtin for contact and shelter type --- assets/layers/hut/hut.json | 131 ++++++------------------------------- 1 file changed, 21 insertions(+), 110 deletions(-) diff --git a/assets/layers/hut/hut.json b/assets/layers/hut/hut.json index 8f641e211..8ebb80836 100644 --- a/assets/layers/hut/hut.json +++ b/assets/layers/hut/hut.json @@ -121,7 +121,19 @@ "tagRenderings": [ "images", "name", - "website", + { + "builtin": "website", + "override": { + "condition": "tourism=wilderness_hut", + "id": "website-single" + } + }, + { + "builtin": "contact", + "override": { + "condition": "tourism=alpine_hut" + } + }, "reservation", "caravansites.caravansites-fee", { @@ -151,116 +163,15 @@ "render": "{preset_type_select()}" }, { - "id": "shelter-type", - "question": { - "en": "What kind of shelter is this?", - "ca": "Quin tipus de refugi és aquest?", - "cs": "Co je to za přístřešek?", - "de": "Um welche Art von Unterstand handelt es sich?", - "es": "¿Qué tipo de refugio es este?", - "it": "Che tipo di riparo è questo?", - "nl": "Wat voor schuilplaats is dit?", - "uk": "Що це за притулок?" - }, - "render": { - "en": "Shelter type: {shelter_type}", - "ca": "Tipus de refugi: {shelter_type}", - "cs": "Typ přístřešku: {shelter_type}", - "de": "Art des Unterstands: {shelter_type}", - "es": "Tipo de refugio: {shelter_type}", - "it": "Tipo di riparo: {shelter_type}", - "uk": "Тип притулку: {shelter_type}" - }, - "freeform": { - "key": "shelter_type", - "type": "string" - }, - "condition": { - "and": [ - "amenity=shelter", - "shelter_type=basic_hut" - ] - }, - "mappings": [ - { - "if": "shelter_type=public_transport", - "then": { - "en": "This is a shelter at a public transport stop.", - "ca": "Es tracta d'un refugi en una parada de transport públic.", - "cs": "Jedná se o přístřešek u zastávky MHD.", - "de": "Das ist ein Unterstand an einer Haltestelle für öffentliche Verkehrsmittel.", - "es": "Este es un refugio en una parada de transporte público.", - "it": "Questo è un riparo presso una fermata del trasporto pubblico.", - "nl": "Dit is een schuilplaats bij een halte voor openbaar vervoer.", - "uk": "Це притулок на зупинці громадського транспорту." - } - }, - { - "if": "shelter_type=picnic_shelter", - "then": { - "en": "This is a shelter protecting from rain at a picnic site.", - "ca": "Es tracta d'un refugi que protegeix de la pluja en un lloc de pícnic.", - "cs": "Jedná se o přístřešek chránící před deštěm na piknikovém místě.", - "de": "Dies ist ein Unterstand zum Schutz vor Regen auf einem Picknickplatz.", - "es": "Este es un refugio que protege de la lluvia en un área de picnic.", - "it": "Questo è un riparo che protegge dalla pioggia in un'area picnic.", - "uk": "Це накриття, щ�� захищає від дощу на місці для пікніка." - } - }, - { - "if": "shelter_type=gazebo", - "then": { - "en": "This is a gazebo.", - "ca": "Això és una glorieta.", - "cs": "Toto je altán.", - "de": "Das ist ein offener Gartenpavillon.", - "es": "Este es un cenador.", - "it": "Questo è un gazebo.", - "uk": "Це альтанка." - } - }, - { - "if": "shelter_type=weather_shelter", - "then": { - "en": "This is a small shelter, primarily intended for short breaks. Usually found in the mountains or alongside roads.", - "ca": "Es tracta d'un petit refugi, principalment destinat a descansos curts. Normalment es troba a les muntanyes o al costat de les carreteres.", - "cs": "Jedná se o malý přístřešek, primárně určený pro krátké přestávky. Obvykle se vyskytuje v horách nebo podél silnic.", - "de": "Dies ist ein kleiner Unterstand, der vor allem für kurze Pausen gedacht ist. Normalerweise findet man ihn in Bergen oder an Straßen.", - "es": "Este es un refugio pequeño, principalmente destinado a descansos cortos. Normalmente se encuentra en las montañas o al lado de las carreteras.", - "it": "Questo è un piccolo riparo, principalmente destinato a brevi pause. Si trova solitamente in montagna o lungo le strade.", - "uk": "Це невеликий притулок, призначений насамперед для коротких перерв. Зазвичай знаходиться в горах або вздовж доріг." - } - }, - { - "if": "shelter_type=lean_to", - "then": { - "en": "This is a shed with 3 walls, primarily intended for camping.", - "ca": "Es tracta d'un cobert amb 3 parets, destinat principalment a l'acampada.", - "cs": "Jedná se o přístřešek se 3 stěnami, primárně určený pro kempování.", - "da": "Dette er et skur med 3 vægge, primært beregnet til camping.", - "de": "Es handelt sich um einen an 3 Seiten geschlossenen Unterstand, der in erster Linie zum Campen gedacht ist.", - "es": "Este es un cobertizo con 3 paredes, principalmente destinado para acampar.", - "it": "Questa è una tettoia con 3 pareti, principalmente destinata al campeggio.", - "uk": "Це сарай з 3-ма стінами, в першу чергу призначений для кемпінгу." - } - }, - { - "if": "shelter_type=pavilion", - "then": { - "en": "This is a pavilion", - "ca": "Aquest és un pavelló", - "cs": "Toto je pavilon", - "de": "Das ist ein Pavillon", - "es": "Este es un pabellón", - "it": "Questo è un padiglione", - "uk": "Це павільйон" - } - }, - { - "if": "shelter_type=basic_hut", - "then": "This is a basic hut, providing basic shelter and sleeping facilities." + "builtin": "shelter.shelter-type", + "override": { + "condition": { + "and": [ + "amenity=shelter", + "shelter_type=basic_hut" + ] } - ] + } } ], "filter":[ From ba4d220b63fe707ad9e91f4b1c4980e2c930d443 Mon Sep 17 00:00:00 2001 From: Martin Bodin Date: Wed, 27 Aug 2025 11:34:50 +0200 Subject: [PATCH 19/34] Including feedbacks from @pietervdvn. --- assets/layers/surveillance_camera/surveillance_camera.json | 2 +- .../surveillance/{panorama.license => panorama.svg.license} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename assets/themes/surveillance/{panorama.license => panorama.svg.license} (100%) diff --git a/assets/layers/surveillance_camera/surveillance_camera.json b/assets/layers/surveillance_camera/surveillance_camera.json index 03008ae9b..1843c3977 100644 --- a/assets/layers/surveillance_camera/surveillance_camera.json +++ b/assets/layers/surveillance_camera/surveillance_camera.json @@ -344,7 +344,7 @@ ] }, { - "id": "Camera type: fixed; panning; dome; panorama", + "id": "Camera type: fixed; panning; dome", "question": { "en": "What kind of camera is this?", "ca": "Quin tipus de càmera és aquesta?", diff --git a/assets/themes/surveillance/panorama.license b/assets/themes/surveillance/panorama.svg.license similarity index 100% rename from assets/themes/surveillance/panorama.license rename to assets/themes/surveillance/panorama.svg.license From e9263eb342e7862c3de2a8f356dd8e29fc6ab179 Mon Sep 17 00:00:00 2001 From: Martin Bodin Date: Wed, 27 Aug 2025 11:36:33 +0200 Subject: [PATCH 20/34] I think that I forgot to add this information in the license file. --- assets/themes/surveillance/license_info.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/assets/themes/surveillance/license_info.json b/assets/themes/surveillance/license_info.json index 53a9f3c25..393433ece 100644 --- a/assets/themes/surveillance/license_info.json +++ b/assets/themes/surveillance/license_info.json @@ -23,6 +23,14 @@ ], "sources": [] }, + { + "path": "panorama.svg", + "license": "CC0-1.0", + "authors": [ + "Martin Bodin" + ], + "sources": [] + }, { "path": "logo.svg", "license": "CC0-1.0", @@ -31,4 +39,4 @@ ], "sources": [] } -] \ No newline at end of file +] From 5689376e1278057dd797e111709112431fe4eb1a Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 28 Aug 2025 01:16:30 +0200 Subject: [PATCH 21/34] Chore: reset translations --- langs/layers/ca.json | 2 +- langs/layers/cs.json | 2 +- langs/layers/da.json | 2 +- langs/layers/de.json | 3 +++ langs/layers/en.json | 34 ++++++++++++++++++++++++++++++++++ langs/layers/es.json | 3 +++ langs/layers/fr.json | 5 ++++- langs/layers/it.json | 2 +- 8 files changed, 48 insertions(+), 5 deletions(-) diff --git a/langs/layers/ca.json b/langs/layers/ca.json index e15f8fa0b..4d1c9f080 100644 --- a/langs/layers/ca.json +++ b/langs/layers/ca.json @@ -11556,7 +11556,7 @@ "2": { "then": "Una càmera panoràmica" }, - "3": { + "4": { "then": "Un timbre que es pot activar remotament en qualsevol moment o mitjançant la detecció de moviment. Aquests són típicament Smart, banderes connectades a Internet. Les marques típiques són Ring, Google Nest, Eufy, ..." } }, diff --git a/langs/layers/cs.json b/langs/layers/cs.json index 36a11b112..7f756f766 100644 --- a/langs/layers/cs.json +++ b/langs/layers/cs.json @@ -12500,7 +12500,7 @@ "2": { "then": "Otáčecí kamera" }, - "3": { + "4": { "then": "Domovní zvonek, který lze spouštět kdykoli vzdáleně nebo detekcí pohybu. Jsou to typicky chytré zvonky připojené k Internetu. Typické značky jsou Ring, Google Nest, Eufy…" } }, diff --git a/langs/layers/da.json b/langs/layers/da.json index d1a4d9c39..1133fb708 100644 --- a/langs/layers/da.json +++ b/langs/layers/da.json @@ -3152,7 +3152,7 @@ "2": { "then": "Et kamera, der panorerer" }, - "3": { + "4": { "then": "En dørklokke, som kan tændes på afstand når som helst eller ved bevægelsesregistrering. Disse er typisk intelligente internetforbundne dørklokker. Typiske mærker er Ring, Google Nest, Eufy, …" } } diff --git a/langs/layers/de.json b/langs/layers/de.json index 58e89547a..0dc01e01f 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -11546,6 +11546,9 @@ "then": "Eine bewegliche Kamera" }, "3": { + "then": "Eine 360°-Kamera" + }, + "4": { "then": "Eine Türklingel, die jederzeit oder per Bewegungserkennung ferngeschaltet werden kann. Dies sind typischerweise Smart, internetgebundene Türklingeln. Typische Marken sind Ring, Google Nest, Eufy, ..." } }, diff --git a/langs/layers/en.json b/langs/layers/en.json index 76f94fdc5..f617e8404 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -7016,6 +7016,37 @@ "render": "Hospital" } }, + "hut": { + "description": "Layer showing basic huts, wilderness huts and alpine huts", + "name": "Huts", + "presets": { + "0": { + "description": "An unserviced fully enclosed hut (with roof and walls) with beds or suitable sleeping areas and a fireplace or stove for heating and cooking.", + "title": "wilderness hut" + }, + "1": { + "description": "A serviced remote building located in the mountains intended to provide board and lodging.", + "title": "alpine hut" + }, + "2": { + "description": "An unserviced fully enclosed hut (with roof and walls) with beds or suitable sleeping areas without a fireplace or stove.", + "title": "basic hut" + } + }, + "tagRenderings": { + "drinking_water": { + "mappings": { + "0": { + "then": "Here is drinking water available." + }, + "1": { + "then": "Here is no drinking water available." + } + }, + "question": "Is drinking water available here?" + } + } + }, "hydrant": { "description": "Map layer to show fire hydrants.", "name": "Hydrants", @@ -12543,6 +12574,9 @@ "then": "A panning camera" }, "3": { + "then": "A 360° camera" + }, + "4": { "then": "A doorbell which might be turned on remotely at any time or by motion detection. These are typically Smart, internet-connected doorbells. Typical brands are Ring, Google Nest, Eufy, …" } }, diff --git a/langs/layers/es.json b/langs/layers/es.json index 0baced4d3..08e758918 100644 --- a/langs/layers/es.json +++ b/langs/layers/es.json @@ -10582,6 +10582,9 @@ }, "2": { "then": "Una cámara panorámica" + }, + "3": { + "then": "Una cámara de 360°" } }, "question": "¿Qué tipo de cámara es esta?" diff --git a/langs/layers/fr.json b/langs/layers/fr.json index dad2406a9..c6fbae3b2 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -6519,6 +6519,9 @@ }, "2": { "then": "Une caméra panoramique" + }, + "3": { + "then": "Une caméra 360°" } }, "question": "Quel genre de caméra est-ce ?" @@ -6543,7 +6546,7 @@ "then": "Une zone intérieure privée est surveillée, par exemple un magasin, un parking souterrain privé…" } }, - "question": "De quel genre de surveillance cette caméra est-elle ?" + "question": "De quel genre de surveillance cette caméra est-elle ?" }, "Surveillance:zone": { "mappings": { diff --git a/langs/layers/it.json b/langs/layers/it.json index 65a7fd8d5..5fefddef9 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -12109,7 +12109,7 @@ "2": { "then": "Una telecamera panoramica" }, - "3": { + "4": { "then": "Un campanello che potrebbe essere acceso da remoto in qualsiasi momento o tramite rilevamento del movimento. Questi sono tipicamente campanelli Smart, connessi a Internet. Marchi tipici sono Ring, Google Nest, Eufy, ..." } }, From db62329d396e067dcb2155a7deed2e466065e383 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 28 Aug 2025 03:12:10 +0200 Subject: [PATCH 22/34] Themes(surveillance): add some tweaks to the 'panorama'-camera --- .../surveillance_camera.json | 11 ++- .../mapcomplete-changes.json | 3 + assets/themes/surveillance/license_info.json | 18 ++--- assets/themes/surveillance/panorama.svg | 68 ++++++++++--------- .../themes/surveillance/panorama.svg.license | 2 +- 5 files changed, 57 insertions(+), 45 deletions(-) diff --git a/assets/layers/surveillance_camera/surveillance_camera.json b/assets/layers/surveillance_camera/surveillance_camera.json index 495b297ea..e19ab6d52 100644 --- a/assets/layers/surveillance_camera/surveillance_camera.json +++ b/assets/layers/surveillance_camera/surveillance_camera.json @@ -114,6 +114,10 @@ ] }, "then": "50,35,center" + }, + { + "if": "camera:type=panorama", + "then": "55,55,center" } ], "render": "35,35,center" @@ -396,7 +400,7 @@ }, "icon": "./assets/themes/surveillance/dome.svg" }, - { + { "if": "camera:type=panning", "then": { "en": "A panning camera", @@ -413,10 +417,11 @@ }, { "if": "camera:type=panorama", + "icon": "./assets/themes/surveillance/panorama.svg", "then": { "en": "A 360° camera", - "de": "Eine 360°-Kamera", - "es": "Una cámara de 360°", + "de": "Eine 360°-Kamera", + "es": "Una cámara de 360°", "fr": "Une caméra 360°" } }, diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index 5b19fa6de..80382fd51 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -38,6 +38,9 @@ "zh_Hant": "顯示由MapComplete進行的變動" }, "icon": "./assets/svg/logo.svg", + "startZoom": 1, + "startLat": 0, + "startLon": 0, "hideFromOverview": true, "layers": [ { diff --git a/assets/themes/surveillance/license_info.json b/assets/themes/surveillance/license_info.json index 393433ece..4b40d545c 100644 --- a/assets/themes/surveillance/license_info.json +++ b/assets/themes/surveillance/license_info.json @@ -23,14 +23,6 @@ ], "sources": [] }, - { - "path": "panorama.svg", - "license": "CC0-1.0", - "authors": [ - "Martin Bodin" - ], - "sources": [] - }, { "path": "logo.svg", "license": "CC0-1.0", @@ -38,5 +30,13 @@ "Pieter Vander Vennet" ], "sources": [] + }, + { + "path": "panorama.svg", + "license": "CC0-1.0", + "authors": [ + "Martin Bodin" + ], + "sources": [] } -] +] \ No newline at end of file diff --git a/assets/themes/surveillance/panorama.svg b/assets/themes/surveillance/panorama.svg index 0a21207b0..6e135df24 100644 --- a/assets/themes/surveillance/panorama.svg +++ b/assets/themes/surveillance/panorama.svg @@ -1,19 +1,19 @@ + inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> @@ -28,7 +28,7 @@ + inkscape:current-layer="surface1" + inkscape:showpageshadow="2" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" /> + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:4.36475;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1" + d="M 11.439583,18.537673 H 87.78125 c 12.54792,0.03646 12.93854,8.290452 0,8.132119 H 11.439583 c -11.3604163,0.03333 -11.219791,-8.256077 0,-8.132119 z" + id="path2" + transform="scale(3.75)" + sodipodi:nodetypes="ccccc" /> + transform="matrix(0.93922526,0,0,0.93922526,79.006064,2.8192093)"> + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.536273" /> + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.536273" /> + transform="matrix(0.69882528,0,0,0.93922526,220.19065,2.8192093)"> + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.536273" /> + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.536273" /> + transform="matrix(0.69882528,0,0,0.93922526,-6.6284342,2.8192093)"> + style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.536273" /> + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.536273" /> diff --git a/assets/themes/surveillance/panorama.svg.license b/assets/themes/surveillance/panorama.svg.license index f725fff05..27a495574 100644 --- a/assets/themes/surveillance/panorama.svg.license +++ b/assets/themes/surveillance/panorama.svg.license @@ -1,2 +1,2 @@ SPDX-FileCopyrightText: Martin Bodin -SPDX-License-Identifier: CC0-1.0 +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file From 0f2e8486cd723c68701a35f63437dcc804be4ea7 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 27 Aug 2025 02:23:06 +0200 Subject: [PATCH 23/34] UI: change tablist to reflect fixmycity-design, see #2107 --- public/css/index-tailwind-output.css | 40 ++++++++++++++++++++++++ src/UI/Base/TabbedGroup.svelte | 35 ++++++--------------- src/UI/Wikipedia/WikipediaPanel.svelte | 13 ++------ src/index.css | 42 ++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 37 deletions(-) diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index 34744bb80..eff0449cf 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -5444,6 +5444,46 @@ button.unstyled, .button-unstyled button { padding: 0; } +/****** Tablist elements *****/ + +.tablist { + margin: 0.25rem; + padding: 0.5rem; + border: 2px dashed var(--button-background-hover); + border-radius: 0.5rem; + display: flex; + justify-content: center; +} + +.tab { + border: unset; + border-radius: 0; + transition: all; + color: var(--foreground-color); + border-bottom: 2px solid var(--foreground-color); + font-weight: bold; + margin: 0.25rem; + padding: 0.25rem; + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.tab-selected { + opacity: 100%; + background: var(--interactive-background); +} + +/* Actually used, don't remove*/ + +.tab-unselected { + background: #00000000 !important; + opacity: 60%; +} + +.tab-unselected:hover { + background: var(--interactive-background); +} + /******* Other input elements ******/ .hover-alert:hover { diff --git a/src/UI/Base/TabbedGroup.svelte b/src/UI/Base/TabbedGroup.svelte index 586f7daf8..8c692740a 100644 --- a/src/UI/Base/TabbedGroup.svelte +++ b/src/UI/Base/TabbedGroup.svelte @@ -43,11 +43,11 @@ } }} > -
- +
+ {#if $$slots.title0} twJoin("tab", selected && "primary", !$condition0 && "hidden")} + class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition0 && "hidden")} >
Tab 0 @@ -56,7 +56,7 @@ {/if} {#if $$slots.title1} twJoin("tab", selected && "primary", !$condition1 && "hidden")} + class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition1 && "hidden")} >
@@ -65,7 +65,7 @@ {/if} {#if $$slots.title2} twJoin("tab", selected && "primary", !$condition2 && "hidden")} + class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition2 && "hidden")} >
@@ -74,7 +74,7 @@ {/if} {#if $$slots.title3} twJoin("tab", selected && "primary", !$condition3 && "hidden")} + class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition3 && "hidden")} >
@@ -83,7 +83,7 @@ {/if} {#if $$slots.title4} twJoin("tab", selected && "primary", !$condition4 && "hidden")} + class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition4 && "hidden")} >
@@ -92,7 +92,7 @@ {/if} {#if $$slots.title5} twJoin("tab", selected && "primary", !$condition5 && "hidden")} + class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition5 && "hidden")} >
@@ -101,7 +101,7 @@ {/if} {#if $$slots.title6} twJoin("tab", selected && "primary", !$condition6 && "hidden")} + class={({ selected }) => twJoin("tab", selected ? "tab-selected": "tab-unselected", !$condition6 && "hidden")} >
@@ -167,19 +167,6 @@ height: calc(100% - 2rem); } - :global(.tab) { - margin: 0.25rem; - padding: 0.25rem; - padding-left: 0.75rem; - padding-right: 0.75rem; - border-radius: 1rem; - } - - :global(.tab .flex) { - align-items: center; - gap: 0.25rem; - } - :global(.tab span|div) { align-items: center; gap: 0.25rem; @@ -190,8 +177,4 @@ fill: var(--interactive-contrast); } - :global(.tab-unselected) { - background-color: var(--background-color) !important; - color: var(--foreground-color) !important; - } diff --git a/src/UI/Wikipedia/WikipediaPanel.svelte b/src/UI/Wikipedia/WikipediaPanel.svelte index ac9e710d5..43db3d34a 100644 --- a/src/UI/Wikipedia/WikipediaPanel.svelte +++ b/src/UI/Wikipedia/WikipediaPanel.svelte @@ -31,9 +31,9 @@ {:else} - + {#each _wikipediaStores as store (store.tag)} - (selected ? "tab-selected" : "tab-unselected")}> + ("tab "+ (selected ? "tab-selected" : "tab-unselected"))}> {/each} @@ -51,14 +51,5 @@ diff --git a/src/index.css b/src/index.css index d1dc9a284..0fd70652d 100644 --- a/src/index.css +++ b/src/index.css @@ -293,6 +293,48 @@ button.unstyled, .button-unstyled button { padding: 0; } +/****** Tablist elements *****/ + +.tablist { + margin: 0.25rem; + padding: 0.5rem; + border: 2px dashed var(--button-background-hover); + border-radius: 0.5rem; + display: flex; + justify-content: center; + +} + +.tab { + border: unset; + border-radius: 0; + transition: all; + color: var(--foreground-color); + border-bottom: 2px solid var(--foreground-color); + font-weight: bold; + + margin: 0.25rem; + padding: 0.25rem; + padding-left: 0.75rem; + padding-right: 0.75rem; + +} + +.tab-selected { + opacity: 100%; + background: var(--interactive-background); +} + +/* Actually used, don't remove*/ +.tab-unselected { + background: #00000000 !important; + opacity: 60%; +} + +.tab-unselected:hover { + background: var(--interactive-background); +} + /******* Other input elements ******/ .hover-alert:hover { From 0a48765f0f443939af01f21912598b65422dfb0a Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 27 Aug 2025 02:32:23 +0200 Subject: [PATCH 24/34] UI(menu): add link to app download page in the menu drawer --- langs/en.json | 1 + src/UI/BigComponents/MenuDrawerIndex.svelte | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/langs/en.json b/langs/en.json index 19daa1127..897eb4bf6 100644 --- a/langs/en.json +++ b/langs/en.json @@ -319,6 +319,7 @@ "menu": { "aboutCurrentThemeTitle": "About this map", "aboutMapComplete": "About MapComplete", + "downloadApp": "Download the app for Android", "filter": "Filter data", "legal": "Legal notices", "moreUtilsTitle": "Discover more", diff --git a/src/UI/BigComponents/MenuDrawerIndex.svelte b/src/UI/BigComponents/MenuDrawerIndex.svelte index cac998937..84f136b9c 100644 --- a/src/UI/BigComponents/MenuDrawerIndex.svelte +++ b/src/UI/BigComponents/MenuDrawerIndex.svelte @@ -59,6 +59,9 @@ import OfflineManagement from "./OfflineManagement.svelte" import { GlobeEuropeAfrica } from "@babeard/svelte-heroicons/solid/GlobeEuropeAfrica" import { onDestroy } from "svelte" + import { DevicePhoneMobileIcon } from "@babeard/svelte-heroicons/solid" + import If from "../Base/If.svelte" + import IfNot from "../Base/IfNot.svelte" export let state: { favourites: FavouritesFeatureSource @@ -229,6 +232,13 @@ + + + + + + + From 8ae09e25de57b514c51a3fa3e42aa3f1e1bf85f7 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 27 Aug 2025 02:51:33 +0200 Subject: [PATCH 25/34] UI(Inspector): update tabgroups, fix import --- src/UI/History/AggregateView.svelte | 2 +- src/UI/InspectorGUI.svelte | 166 +++++++++++++++------------- 2 files changed, 89 insertions(+), 79 deletions(-) diff --git a/src/UI/History/AggregateView.svelte b/src/UI/History/AggregateView.svelte index 35ce509cd..130e7c2dd 100644 --- a/src/UI/History/AggregateView.svelte +++ b/src/UI/History/AggregateView.svelte @@ -12,9 +12,9 @@ import Translations from "../i18n/Translations" import type { TagRenderingConfigJson } from "../../Models/ThemeConfig/Json/TagRenderingConfigJson" import { Or } from "../../Logic/Tags/Or" - import { Utils } from "../../Utils" import ChartJs from "../Base/ChartJs.svelte" import { ChartJsUtils } from "../Base/ChartJsUtils" + import { Lists } from "../../Utils/Lists" export let onlyShowUsername: string[] export let features: Feature[] diff --git a/src/UI/InspectorGUI.svelte b/src/UI/InspectorGUI.svelte index 5afae05be..32f0c8f5d 100644 --- a/src/UI/InspectorGUI.svelte +++ b/src/UI/InspectorGUI.svelte @@ -33,6 +33,9 @@ import GeocodeResults from "./Search/GeocodeResults.svelte" import MagnifyingGlassCircle from "@babeard/svelte-heroicons/mini/MagnifyingGlassCircle" import type { GeocodeResult } from "../Logic/Search/GeocodingProvider" + import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@rgossiaux/svelte-headlessui" + import WikipediaTitle from "./Wikipedia/WikipediaTitle.svelte" + import WikipediaArticle from "./Wikipedia/WikipediaArticle.svelte" console.log("Loading inspector GUI") let username = QueryParameters.GetQueryParameter("user", undefined, "Inspect this user") @@ -49,7 +52,7 @@ new CoordinateSearch(), new OpenLocationCodeSearch(), new PhotonSearch(true, 2), - new PhotonSearch() + new PhotonSearch(), ) let showSearchDrawer = new UIEventSource(true) let searchIsFocussed = new UIEventSource(false) @@ -138,7 +141,7 @@ const overpass = new Overpass( Constants.defaultOverpassUrls[0], undefined, - user.split(";").map((user) => 'nw(user_touched:"' + user + '");') + user.split(";").map((user) => "nw(user_touched:\"" + user + "\");"), ) if (!maplibremap.bounds.data) { return @@ -161,8 +164,6 @@ return true }) - let mode: "map" | "table" | "aggregate" | "images" = "map" - let showPreviouslyVisited = new UIEventSource(true) const t = Translations.t.inspector @@ -207,89 +208,98 @@
-
- - - - -
+ + + ("tab "+ (selected ? "tab-selected" : "tab-unselected"))}> - {#if mode === "map"} - {#if $selectedElement !== undefined} - - + + ("tab "+ (selected ? "tab-selected" : "tab-unselected"))}> + + + + ("tab "+ (selected ? "tab-selected" : "tab-unselected"))}> + + + + ("tab "+ (selected ? "tab-selected" : "tab-unselected"))}> + + + + + + + + + {#if $selectedElement !== undefined} + + - {/if} - -
- -
- search()} - isFocused={searchIsFocussed} - value={searchvalue} - on:focus={() => state.searchState.showSearchDrawer.set(true)} - /> - {#if $searchSuggestions?.length > 0 || $searchIsFocussed} - search(event.detail)} /> + + + {/if} -
-
- {:else if mode === "table"} -
- {#each $featuresStore as f} - - {/each} -
- {:else if mode === "aggregate"} -
- -
- {:else if mode === "images"} -
- -
- {/if} + +
+ +
+ search()} + isFocused={searchIsFocussed} + value={searchvalue} + on:focus={() => state.searchState.showSearchDrawer.set(true)} + /> + {#if $searchSuggestions?.length > 0 || $searchIsFocussed} + search(event.detail)} /> + {/if} +
+
+ +
+ + + {#each $featuresStore as f} + + {/each} + + + + + + + + +
+
From 247507802c7c5229ba5541eee19b30908ae76976 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 28 Aug 2025 22:41:13 +0200 Subject: [PATCH 26/34] UI(Inspector): tweak UI --- public/css/index-tailwind-output.css | 1 + src/UI/InspectorGUI.svelte | 2 +- src/index.css | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index eff0449cf..63aa24107 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -5453,6 +5453,7 @@ button.unstyled, .button-unstyled button { border-radius: 0.5rem; display: flex; justify-content: center; + flex-wrap: wrap; } .tab { diff --git a/src/UI/InspectorGUI.svelte b/src/UI/InspectorGUI.svelte index 32f0c8f5d..06c93d9bc 100644 --- a/src/UI/InspectorGUI.svelte +++ b/src/UI/InspectorGUI.svelte @@ -182,8 +182,8 @@
-

+

Date: Thu, 28 Aug 2025 22:41:31 +0200 Subject: [PATCH 27/34] Feature: add 'download app' item into the menu drawer (if not on Android) --- src/UI/BigComponents/MenuDrawerIndex.svelte | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/UI/BigComponents/MenuDrawerIndex.svelte b/src/UI/BigComponents/MenuDrawerIndex.svelte index 84f136b9c..463edc0fb 100644 --- a/src/UI/BigComponents/MenuDrawerIndex.svelte +++ b/src/UI/BigComponents/MenuDrawerIndex.svelte @@ -60,8 +60,6 @@ import { GlobeEuropeAfrica } from "@babeard/svelte-heroicons/solid/GlobeEuropeAfrica" import { onDestroy } from "svelte" import { DevicePhoneMobileIcon } from "@babeard/svelte-heroicons/solid" - import If from "../Base/If.svelte" - import IfNot from "../Base/IfNot.svelte" export let state: { favourites: FavouritesFeatureSource @@ -232,13 +230,12 @@ - - - - - - - + {#if !$isAndroid} + + + + + {/if} From 6cfc40009df3480a31b82a534b608e63fa472e47 Mon Sep 17 00:00:00 2001 From: Osmwithspace <> Date: Wed, 27 Aug 2025 15:52:33 +0200 Subject: [PATCH 28/34] remove duplication --- assets/layers/campsite/campsite.json | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/layers/campsite/campsite.json b/assets/layers/campsite/campsite.json index 5e9024b87..d29e848c4 100644 --- a/assets/layers/campsite/campsite.json +++ b/assets/layers/campsite/campsite.json @@ -280,7 +280,6 @@ ] } }, - "caravansites.caravansites-toilets", "toilet_at_amenity_lib.all", "questions", "mastodon" From 3ecfbcc3071c3e4831bd7bf219a4f01892064ee8 Mon Sep 17 00:00:00 2001 From: Osmwithspace <> Date: Wed, 27 Aug 2025 15:52:55 +0200 Subject: [PATCH 29/34] use builtin question --- assets/layers/caravansites/caravansites.json | 80 +------------------- 1 file changed, 1 insertion(+), 79 deletions(-) diff --git a/assets/layers/caravansites/caravansites.json b/assets/layers/caravansites/caravansites.json index 373de01a3..4240ceb99 100644 --- a/assets/layers/caravansites/caravansites.json +++ b/assets/layers/caravansites/caravansites.json @@ -666,85 +666,7 @@ ] } }, - { - "id": "caravansites-toilets", - "question": { - "en": "Does this place have toilets?", - "ca": "Aquest lloc té lavabos?", - "cs": "Má toto místo toalety?", - "da": "Har dette sted toiletter?", - "de": "Verfügt dieser Ort über Toiletten?", - "es": "¿Este lugar tiene baños?", - "fr": "Y-a-t’il des toilettes sur le site ?", - "hu": "Van-e itt WC?", - "it": "Questo posto ha servizi igienici?", - "ja": "ここにトイレはありますか?", - "nb_NO": "Har dette stedet toaletter?", - "nl": "Heeft deze plaats toiletten?", - "pl": "Czy to miejsce ma toalety?", - "pt": "Este lugar tem casas de banho?", - "pt_BR": "Este lugar tem banheiros?", - "ru": "Здесь есть туалеты?", - "zh_Hant": "這個地方有廁所嗎?" - }, - "mappings": [ - { - "if": { - "and": [ - "toilets=yes" - ] - }, - "then": { - "en": "This place has toilets", - "ca": "Aquest lloc té lavabos", - "cs": "Toto místo má toalety", - "da": "Dette sted har toiletter", - "de": "Dieser Ort verfügt über Toiletten", - "es": "Este lugar tiene baños", - "fr": "Ce site a des toilettes", - "hu": "Itt van WC", - "id": "Tempat sini ada tandas", - "it": "Questo posto ha servizi igienici", - "ja": "ここにはトイレがある", - "nb_NO": "Dette stedet har toalettfasiliteter", - "nl": "Deze plaats heeft toiletten", - "pl": "To miejsce ma toalety", - "pt": "Este lugar tem casa de banho", - "pt_BR": "Este lugar tem banheiros", - "ru": "В этом месте есть туалеты", - "zh_Hant": "這個地方有廁所" - } - }, - { - "if": { - "and": [ - "toilets=no" - ] - }, - "then": { - "en": "This place does not have toilets", - "ca": "Aquest lloc no té lavabos", - "cs": "Toto místo nemá toalety", - "da": "Dette sted har ikke toiletter", - "de": "Dieser Ort verfügt nicht über Toiletten", - "es": "Este lugar no tiene baños", - "eu": "Toki honek ez dauka komunik", - "fr": "Ce site n’a pas de toilettes", - "hu": "Itt nincs WC", - "id": "Tempat sini tiada tandas", - "it": "Questo posto non ha servizi igienici", - "ja": "ここにはトイレがない", - "nb_NO": "Dette stedet har ikke toalettfasiliteter", - "nl": "Deze plaats heeft geen toiletten", - "pl": "To miejsce nie ma toalet", - "pt": "Este lugar não tem casas de banho", - "pt_BR": "Este lugar não tem banheiros", - "ru": "В этом месте нет туалетов", - "zh_Hant": "這個地方並沒有廁所" - } - } - ] - }, + "has_toilets", { "id": "caravansites-website", "question": { From 7f206da86cd3daed65ada504d978e57c1f640a4a Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 28 Aug 2025 22:48:22 +0200 Subject: [PATCH 30/34] Chore: reset translations --- langs/layers/ca.json | 11 ----------- langs/layers/cs.json | 11 ----------- langs/layers/da.json | 11 ----------- langs/layers/de.json | 11 ----------- langs/layers/en.json | 11 ----------- langs/layers/es.json | 11 ----------- langs/layers/eu.json | 7 ------- langs/layers/fr.json | 11 ----------- langs/layers/hu.json | 13 ------------- langs/layers/id.json | 10 ---------- langs/layers/it.json | 11 ----------- langs/layers/ja.json | 11 ----------- langs/layers/nb_NO.json | 11 ----------- langs/layers/nl.json | 11 ----------- langs/layers/pl.json | 11 ----------- langs/layers/pt.json | 11 ----------- langs/layers/pt_BR.json | 11 ----------- langs/layers/ru.json | 11 ----------- langs/layers/zh_Hant.json | 11 ----------- 19 files changed, 206 deletions(-) diff --git a/langs/layers/ca.json b/langs/layers/ca.json index 20e392c32..a68397a07 100644 --- a/langs/layers/ca.json +++ b/langs/layers/ca.json @@ -2468,17 +2468,6 @@ }, "question": "Aquest lloc té una estació d'abocament sanitari?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "Aquest lloc té lavabos" - }, - "1": { - "then": "Aquest lloc no té lavabos" - } - }, - "question": "Aquest lloc té lavabos?" - }, "caravansites-website": { "question": "Aquest lloc té un lloc web?", "render": "Lloc web oficial: {website}" diff --git a/langs/layers/cs.json b/langs/layers/cs.json index 26b07d97c..348bf81cb 100644 --- a/langs/layers/cs.json +++ b/langs/layers/cs.json @@ -2663,17 +2663,6 @@ }, "question": "Má toto místo sanitární skládku?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "Toto místo má toalety" - }, - "1": { - "then": "Toto místo nemá toalety" - } - }, - "question": "Má toto místo toalety?" - }, "caravansites-website": { "question": "Má toto místo webové stránky?", "render": "Oficiální webové stránky: {website}" diff --git a/langs/layers/da.json b/langs/layers/da.json index 1133fb708..19ad241fb 100644 --- a/langs/layers/da.json +++ b/langs/layers/da.json @@ -1561,17 +1561,6 @@ }, "question": "Har dette sted en sanitær tømningsstation?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "Dette sted har toiletter" - }, - "1": { - "then": "Dette sted har ikke toiletter" - } - }, - "question": "Har dette sted toiletter?" - }, "caravansites-website": { "question": "Har dette sted et websted?", "render": "Officiel hjemmeside: {website}" diff --git a/langs/layers/de.json b/langs/layers/de.json index 93e921a23..da3f91f1d 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -2439,17 +2439,6 @@ }, "question": "Hat dieser Ort eine sanitäre Entsorgungsstation?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "Dieser Ort verfügt über Toiletten" - }, - "1": { - "then": "Dieser Ort verfügt nicht über Toiletten" - } - }, - "question": "Verfügt dieser Ort über Toiletten?" - }, "caravansites-website": { "question": "Hat dieser Ort eine Webseite?", "render": "Offizielle Webseite: {website}" diff --git a/langs/layers/en.json b/langs/layers/en.json index 01939caf2..8fa7d11c6 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -2697,17 +2697,6 @@ }, "question": "Does this place have a sanitary dump station?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "This place has toilets" - }, - "1": { - "then": "This place does not have toilets" - } - }, - "question": "Does this place have toilets?" - }, "caravansites-website": { "question": "Does this place have a website?", "render": "Official website: {website}" diff --git a/langs/layers/es.json b/langs/layers/es.json index 5ff7d5e91..076643f52 100644 --- a/langs/layers/es.json +++ b/langs/layers/es.json @@ -2270,17 +2270,6 @@ }, "question": "¿Este lugar tiene un punto de vaciado de aguas grises?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "Este lugar tiene baños" - }, - "1": { - "then": "Este lugar no tiene baños" - } - }, - "question": "¿Este lugar tiene baños?" - }, "caravansites-website": { "question": "¿Este lugar tiene una página web?", "render": "Página web oficial: {website}" diff --git a/langs/layers/eu.json b/langs/layers/eu.json index 30ad60b23..8473c20b9 100644 --- a/langs/layers/eu.json +++ b/langs/layers/eu.json @@ -289,13 +289,6 @@ } } }, - "caravansites-toilets": { - "mappings": { - "1": { - "then": "Toki honek ez dauka komunik" - } - } - }, "caravansites-website": { "question": "Toki honek webgunerik ba al du?" } diff --git a/langs/layers/fr.json b/langs/layers/fr.json index bd576b835..cceb1374f 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -1873,17 +1873,6 @@ }, "question": "Ce site possède-t’il un lieu de vidange ?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "Ce site a des toilettes" - }, - "1": { - "then": "Ce site n’a pas de toilettes" - } - }, - "question": "Y-a-t’il des toilettes sur le site ?" - }, "caravansites-website": { "question": "Ce lieu a-t’il un site internet ?", "render": "Site officiel : {website}" diff --git a/langs/layers/hu.json b/langs/layers/hu.json index d1e1287f3..c95ddb68a 100644 --- a/langs/layers/hu.json +++ b/langs/layers/hu.json @@ -466,19 +466,6 @@ "description": "Új hivatalos lakóautóhely hozzáadása. Ez arra vannak kijelölve, hogy lakóautóval ott éjszakázzunk. Lehet, hogy úgy néz ki, mint egy igazi kemping, de az is lehet, hogy csak olyan, mint egy parkoló. Előfordulhat, hogy egyáltalán nem jelzik őket, hanem csak egy önkormányzati határozatban vannak kijelölve. A lakóautósoknak szánt olyan hagyományos parkolók, ahol nem várhatóan nem fognak éjszakázni, -nem minősül- lakóautóhelynek. ", "title": "lakóautós megállóhely" } - }, - "tagRenderings": { - "caravansites-toilets": { - "mappings": { - "0": { - "then": "Itt van WC" - }, - "1": { - "then": "Itt nincs WC" - } - }, - "question": "Van-e itt WC?" - } } }, "charging_station": { diff --git a/langs/layers/id.json b/langs/layers/id.json index cc5cd707c..beb701796 100644 --- a/langs/layers/id.json +++ b/langs/layers/id.json @@ -196,16 +196,6 @@ }, "question": "Apakah tempat ini memiliki tempat pembuangan sanitasi?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "Tempat sini ada tandas" - }, - "1": { - "then": "Tempat sini tiada tandas" - } - } - }, "caravansites-website": { "question": "Tempat sini terada situs web?", "render": "Situs resmi: {website}" diff --git a/langs/layers/it.json b/langs/layers/it.json index 25accfbc2..990c15157 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -2632,17 +2632,6 @@ }, "question": "Questo posto ha una stazione di scarico sanitario?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "Questo posto ha servizi igienici" - }, - "1": { - "then": "Questo posto non ha servizi igienici" - } - }, - "question": "Questo posto ha servizi igienici?" - }, "caravansites-website": { "question": "Questo posto ha un sito web?", "render": "Sito web ufficiale: {website}" diff --git a/langs/layers/ja.json b/langs/layers/ja.json index b9808a0b2..7ee7a672c 100644 --- a/langs/layers/ja.json +++ b/langs/layers/ja.json @@ -217,17 +217,6 @@ }, "question": "この場所に衛生的なゴミ捨て場はありますか?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "ここにはトイレがある" - }, - "1": { - "then": "ここにはトイレがない" - } - }, - "question": "ここにトイレはありますか?" - }, "caravansites-website": { "question": "ここにはウェブサイトがありますか?", "render": "公式Webサイト: {website}" diff --git a/langs/layers/nb_NO.json b/langs/layers/nb_NO.json index 2879f7599..e85806b21 100644 --- a/langs/layers/nb_NO.json +++ b/langs/layers/nb_NO.json @@ -443,17 +443,6 @@ "question": "Hva heter dette stedet?", "render": "Dette stedet heter {name}" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "Dette stedet har toalettfasiliteter" - }, - "1": { - "then": "Dette stedet har ikke toalettfasiliteter" - } - }, - "question": "Har dette stedet toaletter?" - }, "caravansites-website": { "question": "Har dette stedet en nettside?", "render": "Offisiell nettside: {website}" diff --git a/langs/layers/nl.json b/langs/layers/nl.json index db5c3e361..865edb6e9 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -2546,17 +2546,6 @@ }, "question": "Heeft deze plaats een loosplaats?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "Deze plaats heeft toiletten" - }, - "1": { - "then": "Deze plaats heeft geen toiletten" - } - }, - "question": "Heeft deze plaats toiletten?" - }, "caravansites-website": { "question": "Heeft deze plaats een website?", "render": "Officiële website: : {website}" diff --git a/langs/layers/pl.json b/langs/layers/pl.json index decd96ce4..187e36d08 100644 --- a/langs/layers/pl.json +++ b/langs/layers/pl.json @@ -1143,17 +1143,6 @@ }, "question": "Czy w tym miejscu znajduje się stacja zrzutu ścieków sanitarnych?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "To miejsce ma toalety" - }, - "1": { - "then": "To miejsce nie ma toalet" - } - }, - "question": "Czy to miejsce ma toalety?" - }, "caravansites-website": { "question": "Czy to miejsce ma stronę internetową?", "render": "Official website: {website}" diff --git a/langs/layers/pt.json b/langs/layers/pt.json index 6e2b577c2..b1599bf54 100644 --- a/langs/layers/pt.json +++ b/langs/layers/pt.json @@ -1418,17 +1418,6 @@ }, "question": "Este local tem uma estação de aterro sanitário?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "Este lugar tem casa de banho" - }, - "1": { - "then": "Este lugar não tem casas de banho" - } - }, - "question": "Este lugar tem casas de banho?" - }, "caravansites-website": { "question": "Este lugar tem um website?", "render": "Site oficial: {website}" diff --git a/langs/layers/pt_BR.json b/langs/layers/pt_BR.json index 2834d9a0c..6da173f4d 100644 --- a/langs/layers/pt_BR.json +++ b/langs/layers/pt_BR.json @@ -1428,17 +1428,6 @@ }, "question": "Este local tem uma estação de aterro sanitário?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "Este lugar tem banheiros" - }, - "1": { - "then": "Este lugar não tem banheiros" - } - }, - "question": "Este lugar tem banheiros?" - }, "caravansites-website": { "question": "Este lugar tem um website?", "render": "Site oficial: {website}" diff --git a/langs/layers/ru.json b/langs/layers/ru.json index 4cda3d5e1..915e17453 100644 --- a/langs/layers/ru.json +++ b/langs/layers/ru.json @@ -710,17 +710,6 @@ }, "question": "В этом кемпинге есть место для слива отходов из туалетных резервуаров?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "В этом месте есть туалеты" - }, - "1": { - "then": "В этом месте нет туалетов" - } - }, - "question": "Здесь есть туалеты?" - }, "caravansites-website": { "question": "Есть ли у этого места веб-сайт?", "render": "Официальный сайт: {website}" diff --git a/langs/layers/zh_Hant.json b/langs/layers/zh_Hant.json index 44fd18acb..adf784206 100644 --- a/langs/layers/zh_Hant.json +++ b/langs/layers/zh_Hant.json @@ -594,17 +594,6 @@ }, "question": "這個地方有衛生設施嗎?" }, - "caravansites-toilets": { - "mappings": { - "0": { - "then": "這個地方有廁所" - }, - "1": { - "then": "這個地方並沒有廁所" - } - }, - "question": "這個地方有廁所嗎?" - }, "caravansites-website": { "question": "這個地方有網站嗎?", "render": "官方網站:{website}" From 3da62f8d708c73cd72868db0206ed90cea2b6b98 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 29 Aug 2025 02:10:49 +0200 Subject: [PATCH 31/34] Themes(hut): move 'hut' above shelter, steal shelter type question, also see #2515 --- assets/layers/hut/hut.json | 8 +++++++- assets/themes/nature/nature.json | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/assets/layers/hut/hut.json b/assets/layers/hut/hut.json index 2aac48426..7620f1bfa 100644 --- a/assets/layers/hut/hut.json +++ b/assets/layers/hut/hut.json @@ -148,6 +148,12 @@ { "id": "preset_type", "render": "{preset_type_select()}" + }, + { + "builtin": "shelter.shelter-type", + "override": { + "condition": "amenity=shelter" + } } ], "filter":[ @@ -157,4 +163,4 @@ "enableRelocation": false, "enableImproveAccuracy": true } -} \ No newline at end of file +} diff --git a/assets/themes/nature/nature.json b/assets/themes/nature/nature.json index 0973a354d..a91c126a1 100644 --- a/assets/themes/nature/nature.json +++ b/assets/themes/nature/nature.json @@ -60,8 +60,8 @@ "nature_reserve", { "builtin": [ - "shelter", - "hut" + "hut", + "shelter" ], "override": { "minzoom": 11 @@ -97,4 +97,4 @@ "observation_tower", "viewpoint" ] -} \ No newline at end of file +} From 152d93bf4bdc73e4d331e2718181599ccf9a18b1 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 29 Aug 2025 02:11:42 +0200 Subject: [PATCH 32/34] Themes(preset_type_select): preset type select now removes keys set by other presets --- src/UI/Popup/DataVisualisations.ts | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/UI/Popup/DataVisualisations.ts b/src/UI/Popup/DataVisualisations.ts index dcbe8c80a..1cc80257e 100644 --- a/src/UI/Popup/DataVisualisations.ts +++ b/src/UI/Popup/DataVisualisations.ts @@ -30,6 +30,8 @@ import Tr from "../Base/Tr.svelte" import Combine from "../Base/Combine" import Marker from "../Map/Marker.svelte" import { twJoin } from "tailwind-merge" +import { Tag } from "../../Logic/Tags/Tag" +import { Lists } from "../../Utils/Lists" class DirectionIndicatorVis extends SpecialVisualizationSvelte { funcName = "direction_indicator" @@ -243,17 +245,24 @@ class PresetTypeSelect extends SpecialVisualizationSvelte { console.warn("Trying to use the _original_ layer") layer = state.theme.layers.find((l) => l.id === layer._basedOn) ?? layer } + + const allKeys = Lists.dedup(layer.presets.flatMap(preset => preset.tags.flatMap(tag => tag.usedKeys()))) + const question: QuestionableTagRenderingConfigJson = { id: layer.id + "-type", question: t.question.translations, - mappings: layer.presets.map((pr) => ({ - if: new And(pr.tags).asJson(), - icon: "auto", - then: (pr.description ? t.typeDescription : t.typeTitle).Subs({ - title: pr.title, - description: pr.description, - }).translations, - })), + mappings: layer.presets.map((pr) => { + const presetKeys = new Set(pr.tags.flatMap(t => t.key)) + const keysToRemove = allKeys.filter(k => !presetKeys.has(k)).map(k => new Tag(k, "")) + return ({ + if: new And([...pr.tags, ...keysToRemove]).asJson(), + icon: "auto", + then: (pr.description ? t.typeDescription : t.typeTitle).Subs({ + title: pr.title, + description: pr.description, + }).translations, + }) + }), } if (question.mappings.length === 0) { console.error("No mappings for preset_type_select, something went wrong") From 81f98e62ceec6313256a7864822225efb0b772d1 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 29 Aug 2025 02:11:58 +0200 Subject: [PATCH 33/34] Themes(preset_type_select): fix 'auto-icon' display --- src/Logic/Tags/SubstitutingTag.ts | 2 +- src/Logic/Tags/TagUtils.ts | 22 ++++++++++++++++++- .../TagRendering/TagRenderingMapping.svelte | 3 ++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/Logic/Tags/SubstitutingTag.ts b/src/Logic/Tags/SubstitutingTag.ts index e44e28394..9930a1de1 100644 --- a/src/Logic/Tags/SubstitutingTag.ts +++ b/src/Logic/Tags/SubstitutingTag.ts @@ -120,7 +120,7 @@ export default class SubstitutingTag extends TagsFilter { } isNegative(): boolean { - return false + return this._value === "" } visit(f: (tagsFilter: TagsFilter) => void) { diff --git a/src/Logic/Tags/TagUtils.ts b/src/Logic/Tags/TagUtils.ts index c09a77617..e4aba8209 100644 --- a/src/Logic/Tags/TagUtils.ts +++ b/src/Logic/Tags/TagUtils.ts @@ -994,11 +994,31 @@ export class TagUtils { ].join("\n") } - static fromProperties(tags: Record): TagConfigJson | boolean { + public static fromProperties(tags: Record): TagConfigJson | boolean { const opt = new And(Object.keys(tags).map((k) => new Tag(k, tags[k]))).optimize() if (opt === true || opt === false) { return opt } return opt.asJson() } + + /** + * Returns a similarly structured tag, but all tags with an empty value are removed. + * Those are assumed to be all met (and thus true) + * + * new And([new Tag("a", "b"), new Tag("c", "")] // => new Tag("a","b") + * new And([new Tag("c", "")] // => true + */ + public static removeEmptyParts(tag: UploadableTag): UploadableTag | true { + if (tag["and"]) { + const tags = tag["and"] + const cleaned = tags.map(t => TagUtils.removeEmptyParts(t)) + const filtered = cleaned.filter(t => t !== true) + return new And(filtered) + } + if (tag.isNegative()) { + return true + } + return tag + } } diff --git a/src/UI/Popup/TagRendering/TagRenderingMapping.svelte b/src/UI/Popup/TagRendering/TagRenderingMapping.svelte index 44b99286f..f7e365f1c 100644 --- a/src/UI/Popup/TagRendering/TagRenderingMapping.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingMapping.svelte @@ -44,8 +44,9 @@ } function getAutoIcon(mapping: { readonly if?: TagsFilter }): Readonly> { + const ifTags = TagUtils.removeEmptyParts(mapping.if) for (const preset of layer.presets) { - if (!new And(preset.tags).shadows(mapping.if)) { + if (!new And(preset.tags).shadows(ifTags)) { continue } From 2b8f32fe3ab219656f7414df58b9ced13991d291 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 29 Aug 2025 00:13:26 +0000 Subject: [PATCH 34/34] Make branches compatible --- assets/layers/hut/hut.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/assets/layers/hut/hut.json b/assets/layers/hut/hut.json index 8ebb80836..27951671e 100644 --- a/assets/layers/hut/hut.json +++ b/assets/layers/hut/hut.json @@ -165,12 +165,7 @@ { "builtin": "shelter.shelter-type", "override": { - "condition": { - "and": [ - "amenity=shelter", - "shelter_type=basic_hut" - ] - } + "condition": "amenity=shelter" } } ],