From 67319fc8d9741b9e96e6182bdf04624e49c3f963 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 1 Jan 2022 00:03:08 +0100 Subject: [PATCH 01/43] =?UTF-8?q?Test=20with=20waste=20theme=20(Happy=20Ne?= =?UTF-8?q?w=20Year=F0=9F=8E=87=F0=9F=8E=89)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/layers/recycling/license_info.json | 12 +++++ assets/layers/recycling/recycling-14.svg | 40 +++++++++++++++++ assets/layers/recycling/recycling.json | 53 +++++++++++++++++++++++ assets/themes/waste/waste.json | 29 +++++++++++++ 4 files changed, 134 insertions(+) create mode 100644 assets/layers/recycling/license_info.json create mode 100644 assets/layers/recycling/recycling-14.svg create mode 100644 assets/layers/recycling/recycling.json create mode 100644 assets/themes/waste/waste.json diff --git a/assets/layers/recycling/license_info.json b/assets/layers/recycling/license_info.json new file mode 100644 index 000000000..035586de7 --- /dev/null +++ b/assets/layers/recycling/license_info.json @@ -0,0 +1,12 @@ +[ + { + "path": "recycling-14.svg", + "license": "CC0-1.0", + "authors": [ + "Michael Glanznig" + ], + "sources": [ + "https://github.com/gmgeo/osmic/blob/master/amenity/recycling-14.svg" + ] + } +] \ No newline at end of file diff --git a/assets/layers/recycling/recycling-14.svg b/assets/layers/recycling/recycling-14.svg new file mode 100644 index 000000000..6244f4124 --- /dev/null +++ b/assets/layers/recycling/recycling-14.svg @@ -0,0 +1,40 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json new file mode 100644 index 000000000..1fc67853a --- /dev/null +++ b/assets/layers/recycling/recycling.json @@ -0,0 +1,53 @@ +{ + "id": "recycling", + "name": { + "en": "Recycling", + "nl": "Recycling" + }, + "description": { + "en": "A layer with recycling containers and centres", + "nl": "Een laag met recyclagingcontainers en -centrums" + }, + "source": { + "osmTags": "amenity=recycling" + }, + "minzoom": 12, + "title": { + "render": { + "en": "Recycling facility", + "nl": "Recyclingfaciliteit" + } + }, + "mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": "./assets/layers/recycling/recycling-14.svg" + } + ], + "presets": [], + "tagRenderings": [ + { + "id": "recycling-type", + "mappings": [ + { + "if": "recycling-type=container", + "then": { + "en": "This is a recycling container", + "nl": "Dit is een recyclingcontainer" + } + }, + { + "if": "recycling-type=centre", + "then": { + "en": "This is a recycling centre", + "nl": "Dit is een recyclingcentrum" + } + } + ] + } + ], + "filter": [] +} \ No newline at end of file diff --git a/assets/themes/waste/waste.json b/assets/themes/waste/waste.json new file mode 100644 index 000000000..4e70754d2 --- /dev/null +++ b/assets/themes/waste/waste.json @@ -0,0 +1,29 @@ +{ + "id": "waste", + "maintainer": "", + "version": "2021-12-31", + "language": [ + "en", + "nl" + ], + "title": { + "en": "Waste", + "nl": "Afval" + }, + "description": { + "en": "Map showing waste baskets and recycling facilities.", + "nl": "Kaart met afvalbakken en recyclingfaciliteiten." + }, + "icon": "./assets/layers/recycling/recycling-14.svg", + "startZoom": 19, + "startLat": 53.24865, + "startLon": 6.60075, + "layers": [ + { + "builtin": "waste_basket", + "override": { + "minzoom": 12 + } + } + ] +} \ No newline at end of file From 7ee50a4c9d2c8b4926d820a24889e2c08fd5f2a1 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 1 Jan 2022 00:43:04 +0100 Subject: [PATCH 02/43] Include recycling layer --- assets/themes/waste/waste.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assets/themes/waste/waste.json b/assets/themes/waste/waste.json index 4e70754d2..74f481338 100644 --- a/assets/themes/waste/waste.json +++ b/assets/themes/waste/waste.json @@ -24,6 +24,9 @@ "override": { "minzoom": 12 } + }, + { + "builtin": "recycling" } ] } \ No newline at end of file From 5a6e566fe2033a31c5945969247629c7f9b6c258 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 1 Jan 2022 02:56:06 +0100 Subject: [PATCH 03/43] Added recycling types --- assets/layers/recycling/batteries.svg | 15 + assets/layers/recycling/beverage_cartons.svg | 20 + assets/layers/recycling/cans.svg | 41 ++ assets/layers/recycling/clothes.svg | 18 + assets/layers/recycling/cooking_oil.svg | 32 ++ assets/layers/recycling/engine_oil.svg | 19 + assets/layers/recycling/garden_waste.svg | 22 + assets/layers/recycling/glass.svg | 25 ++ assets/layers/recycling/glass_bottles.svg | 18 + assets/layers/recycling/license_info.json | 176 +++++++- assets/layers/recycling/newspaper.svg | 13 + assets/layers/recycling/paper.svg | 33 ++ assets/layers/recycling/plastic.svg | 78 ++++ assets/layers/recycling/plastic_bottles.svg | 21 + assets/layers/recycling/plastic_packaging.svg | 55 +++ assets/layers/recycling/recycling.json | 376 +++++++++++++++++- assets/layers/recycling/scrap_metal.svg | 19 + assets/layers/recycling/shoes.svg | 24 ++ .../recycling/small_electrical_appliances.svg | 44 ++ 19 files changed, 1044 insertions(+), 5 deletions(-) create mode 100644 assets/layers/recycling/batteries.svg create mode 100644 assets/layers/recycling/beverage_cartons.svg create mode 100644 assets/layers/recycling/cans.svg create mode 100644 assets/layers/recycling/clothes.svg create mode 100644 assets/layers/recycling/cooking_oil.svg create mode 100644 assets/layers/recycling/engine_oil.svg create mode 100644 assets/layers/recycling/garden_waste.svg create mode 100644 assets/layers/recycling/glass.svg create mode 100644 assets/layers/recycling/glass_bottles.svg create mode 100644 assets/layers/recycling/newspaper.svg create mode 100644 assets/layers/recycling/paper.svg create mode 100644 assets/layers/recycling/plastic.svg create mode 100644 assets/layers/recycling/plastic_bottles.svg create mode 100644 assets/layers/recycling/plastic_packaging.svg create mode 100644 assets/layers/recycling/scrap_metal.svg create mode 100644 assets/layers/recycling/shoes.svg create mode 100644 assets/layers/recycling/small_electrical_appliances.svg diff --git a/assets/layers/recycling/batteries.svg b/assets/layers/recycling/batteries.svg new file mode 100644 index 000000000..2ba547473 --- /dev/null +++ b/assets/layers/recycling/batteries.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/beverage_cartons.svg b/assets/layers/recycling/beverage_cartons.svg new file mode 100644 index 000000000..05909ef8f --- /dev/null +++ b/assets/layers/recycling/beverage_cartons.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/cans.svg b/assets/layers/recycling/cans.svg new file mode 100644 index 000000000..2aaff3f9a --- /dev/null +++ b/assets/layers/recycling/cans.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/clothes.svg b/assets/layers/recycling/clothes.svg new file mode 100644 index 000000000..f99861248 --- /dev/null +++ b/assets/layers/recycling/clothes.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/cooking_oil.svg b/assets/layers/recycling/cooking_oil.svg new file mode 100644 index 000000000..7a742beab --- /dev/null +++ b/assets/layers/recycling/cooking_oil.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/engine_oil.svg b/assets/layers/recycling/engine_oil.svg new file mode 100644 index 000000000..f92afd355 --- /dev/null +++ b/assets/layers/recycling/engine_oil.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/garden_waste.svg b/assets/layers/recycling/garden_waste.svg new file mode 100644 index 000000000..8788c1147 --- /dev/null +++ b/assets/layers/recycling/garden_waste.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/glass.svg b/assets/layers/recycling/glass.svg new file mode 100644 index 000000000..46488037e --- /dev/null +++ b/assets/layers/recycling/glass.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/glass_bottles.svg b/assets/layers/recycling/glass_bottles.svg new file mode 100644 index 000000000..7d234e991 --- /dev/null +++ b/assets/layers/recycling/glass_bottles.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/license_info.json b/assets/layers/recycling/license_info.json index 035586de7..ec788ff43 100644 --- a/assets/layers/recycling/license_info.json +++ b/assets/layers/recycling/license_info.json @@ -1,12 +1,186 @@ [ + { + "path": "batteries.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/batteries.svg" + ] + }, + { + "path": "beverage_cartons.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/beverage_cartons.svg" + ] + }, + { + "path": "cans.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/cans.svg" + ] + }, + { + "path": "clothes.svg", + "license": "CC-BY-SA", + "authors": [ + "Adrien Pavie", + "modified from EmojiOne 2: U+1F456, U+1F45" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/clothes.svg" + ] + }, + { + "path": "cooking_oil.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/cooking_oil.svg" + ] + }, + { + "path": "engine_oil.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/engine_oil.svg" + ] + }, + { + "path": "garden_waste.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick", + "modified from Twemoji: U+1F33F" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/garden_waste.svg" + ] + }, + { + "path": "glass.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/glass.svg" + ] + }, + { + "path": "glass_bottles.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/glass_bottles.svg" + ] + }, + { + "path": "newspaper.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick", + "modified from EmojiOne 2: U+1F4F0" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/newspaper.svg" + ] + }, + { + "path": "paper.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/paper.svg" + ] + }, + { + "path": "plastic.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/plastic.svg" + ] + }, + { + "path": "plastic_bottles.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/plastic_bottles.svg" + ] + }, + { + "path": "plastic_packaging.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/plastic_packaging.svg" + ] + }, { "path": "recycling-14.svg", - "license": "CC0-1.0", + "license": "CC0", "authors": [ "Michael Glanznig" ], "sources": [ "https://github.com/gmgeo/osmic/blob/master/amenity/recycling-14.svg" ] + }, + { + "path": "scrap_metal.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/scrap_metal.svg" + ] + }, + { + "path": "shoes.svg", + "license": "CC-BY-SA", + "authors": [ + "Tobias Zwick", + "modified from EmojiOne 2: U+1F45F" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/shoes.svg" + ] + }, + { + "path": "small_electrical_appliances.svg", + "license": "CC-BY-SA", + "authors": [ + "EmojiOne 2: U+1F50C" + ], + "sources": [ + "https://github.com/streetcomplete/StreetComplete/blob/master/res/graphics/recycling%20icons/small_electrical_appliances.svg" + ] } ] \ No newline at end of file diff --git a/assets/layers/recycling/newspaper.svg b/assets/layers/recycling/newspaper.svg new file mode 100644 index 000000000..19ffa9d54 --- /dev/null +++ b/assets/layers/recycling/newspaper.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/assets/layers/recycling/paper.svg b/assets/layers/recycling/paper.svg new file mode 100644 index 000000000..76cb42217 --- /dev/null +++ b/assets/layers/recycling/paper.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/plastic.svg b/assets/layers/recycling/plastic.svg new file mode 100644 index 000000000..755c2e865 --- /dev/null +++ b/assets/layers/recycling/plastic.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/plastic_bottles.svg b/assets/layers/recycling/plastic_bottles.svg new file mode 100644 index 000000000..669312972 --- /dev/null +++ b/assets/layers/recycling/plastic_bottles.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/plastic_packaging.svg b/assets/layers/recycling/plastic_packaging.svg new file mode 100644 index 000000000..7ae9d650f --- /dev/null +++ b/assets/layers/recycling/plastic_packaging.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index 1fc67853a..c5b8edbd8 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -27,27 +27,395 @@ "icon": "./assets/layers/recycling/recycling-14.svg" } ], - "presets": [], + "presets": [ + { + "title": { + "en": "recycling container", + "nl": "recycling container" + }, + "tags": [ + "amenity=recycling", + "recycling_type=container" + ] + }, + { + "title": { + "en": "recycling centre", + "nl": "recycling centre" + }, + "tags": [ + "amenity=recycling", + "recycling_type=centre" + ] + } + ], "tagRenderings": [ { "id": "recycling-type", + "question": { + "en": "What type of recycling is this?", + "nl": "Wat voor soort recycling is dit?" + }, "mappings": [ { - "if": "recycling-type=container", + "if": "recycling_type=container", "then": { "en": "This is a recycling container", "nl": "Dit is een recyclingcontainer" } }, { - "if": "recycling-type=centre", + "if": "recycling_type=centre", "then": { "en": "This is a recycling centre", "nl": "Dit is een recyclingcentrum" } } ] + }, + { + "id": "container-underground", + "question": { + "en": "Is this container underground?", + "nl": "Is deze container ondergrond?" + }, + "condition": "recycling_type=container", + "mappings": [ + { + "if": "location=underground", + "then": { + "en": "This is an underground container", + "nl": "Dit is een ondergrondse container" + } + }, + { + "if": "location=", + "then": { + "en": "This container is not underground", + "nl": "Deze container is niet ondergronds" + } + } + ] + }, + { + "id": "recycling-accepts", + "question": { + "en": "What can be recycled here?", + "nl": "Wat kan hier gerecycled worden?" + }, + "multiAnswer": true, + "mappings": [ + { + "if": "recycling:batteries=yes", + "ifnot": "recycling:batteries=", + "then": { + "en": "
Batteries can be recycled here
", + "nl": "
Batterijen kunnen hier gerecycled worden
" + } + }, + { + "if": "recycling:beverage_cartons=yes", + "ifnot": "recycling:beverage_cartons=", + "then": { + "en": "
Beverage cartons can be recycled here
", + "nl": "
Drankpakken kunnen hier gerecycled worden
" + } + }, + { + "if": "recycling:cans=yes", + "ifnot": "recycling:cans=", + "then": { + "en": "
Cans can be recycled here
", + "nl": "
Blikken kunnen hier gerecycled worden
" + } + }, + { + "if": "recycling:clothes=yes", + "ifnot": "recycling:clothes=", + "then": { + "en": "
Clothes can be recycled here
", + "nl": "
Kleren kunnen hier gerecycled worden
" + } + }, + { + "if": "recycling:cooking_oil=yes", + "ifnot": "recycling:cooking_oil=", + "then": { + "en": "
Cooking oil can be recycled here
", + "nl": "
Frituurvet kan hier gerecycled worden
" + } + }, + { + "if": "recycling:engine_oil=yes", + "ifnot": "recycling:engine_oil=", + "then": { + "en": "
Engine oil can be recycled here
", + "nl": "
Motorolie kan hier gerecycled worden
" + } + }, + { + "if": "recycling:green_waste=yes", + "ifnot": "recycling:green_waste=", + "then": { + "en": "
Green waste can be recycled here
", + "nl": "
Groen afval kan hier gerecycled worden
" + } + }, + { + "if": "recycling:glass_bottles=yes", + "ifnot": "recycling:glass_bottles=", + "then": { + "en": "
Glass bottles can be recycled here
", + "nl": "
Glazen flessen kunnen hier gerecycled worden
" + } + }, + { + "if": "recycling:glass=yes", + "ifnot": "recycling:glass=", + "then": { + "en": "
Glass can be recycled here
", + "nl": "
Glas kan hier gerecycled worden
" + } + }, + { + "if": "recycling:newspaper=yes", + "ifnot": "recycling:newspaper=", + "then": { + "en": "
Newspapers can be recycled here
", + "nl": "
Kranten kunnen hier gerecycled worden
" + } + }, + { + "if": "recycling:paper=yes", + "ifnot": "recycling:paper=", + "then": { + "en": "
Paper can be recycled here
", + "nl": "
Papier kan hier gerecycled worden
" + } + }, + { + "if": "recycling:plastic_bottles=yes", + "ifnot": "recycling:plastic_bottles=", + "then": { + "en": "
Plastic bottles can be recycled here
", + "nl": "
Plastic flessen kunnen hier gerecycled worden
" + } + }, + { + "if": "recycling:plastic_packaging=yes", + "ifnot": "recycling:plastic_packaging=", + "then": { + "en": "
Plastic packaging can be recycled here
", + "nl": "
Plastic verpakking kan hier gerecycled worden
" + } + }, + { + "if": "recylcing:plastic=yes", + "ifnot": "recycling:plastic=", + "then": { + "en": "
Plastic can be recycled here
", + "nl": "
Plastic kan hier gerecycled worden
" + } + }, + { + "if": "recycling:scrap_metal=yes", + "ifnot": "recycling:scrap_metal=", + "then": { + "en": "
Scrap metal can be recycled here
", + "nl": "
Oud metaal kan hier gerecycled worden
" + } + }, + { + "if": "recycling:shoes=yes", + "ifnot": "recycling:shoes=", + "then": { + "en": "
Shoes can be recycled here
", + "nl": "
Schoenen kunnen hier gerecycled worden
" + } + }, + { + "if": "recycling:small_electrical_appliances=yes", + "ifnot": "recycling:small_electrical_appliances=", + "then": { + "en": "
Small electrical appliances can be recycled here
", + "nl": "
Kleine elektronische apparaten kunnen hier gerecycled worden
" + } + } + ] + }, + { + "id": "operator", + "render": { + "en": "This recycling facility is operated by {operator}", + "nl": "Deze recyclingfaciliteit wordt beheerd door {operator}" + }, + "question": { + "en": "What company operates this recycling facility?", + "nl": "Wat is de beheerder van deze recyclingfaciliteit?" + }, + "freeform": { + "key": "operator", + "type": "string" + } + }, + { + "id": "opening_hours", + "render": "{opening_hours_table()}", + "question": { + "en": "What are the opening hours of this recycling facility?", + "nl": "Wat zijn de openingstijden van deze recyclingfaciliteit?" + }, + "freeform": { + "key": "opening_hours", + "type": "opening_hours" + }, + "mappings": [ + { + "if": "opening_hours=24/7", + "then": { + "en": "24/7", + "nl": "24/7" + } + } + ] } ], - "filter": [] + "filter": [ + { + "id": "isOpen", + "options": [ + { + "question": { + "en": "Currently open", + "nl": "Op dit moment open" + }, + "osmTags": "_isOpen=yes" + } + ] + }, + { + "id": "recyclingType", + "options": [ + { + "question": { + "en": "All recycling types", + "nl": "Alle recyclingtypes" + } + }, + { + "question": { + "en": "Recycling of batteries", + "nl": "Recycling van batterijen" + }, + "osmTags": "recycling:batteries=yes" + }, + { + "question": { + "en": "Recycling of beverage cartons", + "nl": "Recycling van drankpakken" + }, + "osmTags": "recycling:beverage_cartons=yes" + }, + { + "question": { + "en": "Recycling of cans", + "nl": "Recycling van blikken" + }, + "osmTags": "recycling:cans=yes" + }, + { + "question": { + "en": "Recycling of clothes", + "nl": "Recycling van kleding" + }, + "osmTags": "recycling:clothes=yes" + }, + { + "question": { + "en": "Recycling of cooking oil", + "nl": "Recycling van frituurvet" + }, + "osmTags": "recycling:cooking_oil=yes" + }, + { + "question": { + "en": "Recycling of engine oil", + "nl": "Recycling van motorolie" + }, + "osmTags": "recycling:engine_oil=yes" + }, + { + "question": { + "en": "Recycling of green waste", + "nl": "Recycling van groen afval" + }, + "osmTags": "recycling:green_waste=yes" + }, + { + "question": { + "en": "Recycling of glass bottles", + "nl": "Recycling van glazen flessen" + }, + "osmTags": "recycling:glass_bottles=yes" + }, + { + "question": { + "en": "Recycling of glass", + "nl": "Recycling van glas" + }, + "osmTags": "recycling:glass=yes" + }, + { + "question": { + "en": "Recycling of newspapers", + "nl": "Recycling van kranten" + }, + "osmTags": "recycling:newspaper=yes" + }, + { + "question": { + "en": "Recycling of paper", + "nl": "Recycling van papier" + }, + "osmTags": "recycling:paper=yes" + }, + { + "question": { + "en": "Recycling of plastic bottles", + "nl": "Recycling van plastic flessen" + }, + "osmTags": "recycling:plastic_bottles=yes" + }, + { + "question": { + "en": "Recycling of plastic packaging", + "nl": "Recycling van plastic verpakking" + }, + "osmTags": "recycling:plastic_packaging=yes" + }, + { + "question": { + "en": "Recycling of plastic", + "nl": "Recycling van plastic" + }, + "osmTags": "recycling:plastic=yes" + }, + { + "question": { + "en": "Recycling of scrap metal", + "nl": "Recycling van oud metaal" + }, + "osmTags": "recycling:scrap_metal=yes" + }, + { + "question": { + "en": "Recycling of small electrical appliances", + "nl": "Recycling van kleine elektrische apparaten" + }, + "osmTags": "recycling:small_electrical_appliances=yes" + } + + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/recycling/scrap_metal.svg b/assets/layers/recycling/scrap_metal.svg new file mode 100644 index 000000000..9ff4dfeeb --- /dev/null +++ b/assets/layers/recycling/scrap_metal.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/shoes.svg b/assets/layers/recycling/shoes.svg new file mode 100644 index 000000000..0a5bd834e --- /dev/null +++ b/assets/layers/recycling/shoes.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/small_electrical_appliances.svg b/assets/layers/recycling/small_electrical_appliances.svg new file mode 100644 index 000000000..b5529c91f --- /dev/null +++ b/assets/layers/recycling/small_electrical_appliances.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 775f52839a72b0a55d7fc5be012bcb194f835faa Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 1 Jan 2022 03:09:08 +0100 Subject: [PATCH 04/43] Added filters to waste_basket layer --- assets/layers/waste_basket/waste_basket.json | 66 ++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json index 49a365d2b..1ff292be5 100644 --- a/assets/layers/waste_basket/waste_basket.json +++ b/assets/layers/waste_basket/waste_basket.json @@ -159,6 +159,72 @@ } } ], + "filter": [ + { + "id": "waste-type", + "options": [ + { + "question": { + "en": "All types", + "nl": "Alle soorten", + "de": "Alle Typen" + } + }, + { + "question": { + "en": "Waste basket for cigarettes", + "nl": "Vuilnisbak voor sigarettenpeuken", + "de": "Mülleimer für Zigaretten" + }, + "osmTags": "waste=cigarettes" + }, + { + "question": { + "en": "Waste basket for drugs", + "nl": "Vuilnisbak voor (vervallen) medicatie en drugs", + "de": "Mülleimer für Drogen" + }, + "osmTags": "waste=drugs" + }, + { + "question": { + "en": "Waste basket for dog excrement", + "nl": "Vuilnisbak voor hondenuitwerpselen", + "de": "Mülleimer für Hundekot" + }, + "osmTags": "waste=dog_excrement" + }, + { + "question": { + "en": "Waste basket for trash", + "nl": "Vuilnisbak voor zwerfvuil", + "de": "Mülleimer für allgemeinen Müll" + }, + "osmTags": "waste=trash" + }, + { + "question": { + "en": "Waste basket for sharps", + "nl": "Vuilnisbak voor injectienaalden en andere scherpe voorwerpen", + "de": "Mülleimer für Nadeln und andere scharfe Gegenstände" + }, + "osmTags": "waste=sharps" + } + ] + }, + { + "id": "waste-vending", + "options": [ + { + "question": { + "en": "Waste basket with dispenser for (dog) excrement bags", + "nl": "Vuilnisbak met verdeler voor hondenpoepzakjes", + "de": "Abfalleimer mit Spender für (Hunde-)Kotbeutel" + } + } + ] + } + ], "deletion": { "softDeletionTags": { "and": [ From 8b0c07f898ba7a582f875534463fe02e8ccb7ab5 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 1 Jan 2022 14:00:24 +0100 Subject: [PATCH 05/43] Fix filter --- assets/layers/waste_basket/waste_basket.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json index 1ff292be5..0e3ea89ed 100644 --- a/assets/layers/waste_basket/waste_basket.json +++ b/assets/layers/waste_basket/waste_basket.json @@ -220,7 +220,8 @@ "en": "Waste basket with dispenser for (dog) excrement bags", "nl": "Vuilnisbak met verdeler voor hondenpoepzakjes", "de": "Abfalleimer mit Spender für (Hunde-)Kotbeutel" - } + }, + "osmTags": "vending=dog_excrement_bag" } ] } From fe539d72f9175f747f6460b11e378af77b17fc0d Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 1 Jan 2022 15:32:33 +0100 Subject: [PATCH 06/43] Add waste disposal layer --- assets/layers/recycling/recycling.json | 18 +- assets/layers/waste_basket/waste_basket.json | 2 +- .../layers/waste_disposal/license_info.json | 12 + .../layers/waste_disposal/waste_disposal.json | 88 +++++++ .../layers/waste_disposal/waste_disposal.svg | 31 +++ assets/themes/waste/waste.json | 3 + langs/layers/de.json | 31 +++ langs/layers/en.json | 244 ++++++++++++++++++ langs/layers/nl.json | 205 +++++++++++++++ langs/themes/en.json | 4 + langs/themes/nl.json | 4 + 11 files changed, 635 insertions(+), 7 deletions(-) create mode 100644 assets/layers/waste_disposal/license_info.json create mode 100644 assets/layers/waste_disposal/waste_disposal.json create mode 100644 assets/layers/waste_disposal/waste_disposal.svg diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index c5b8edbd8..f25a0dce0 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -74,10 +74,10 @@ ] }, { - "id": "container-underground", + "id": "container-location", "question": { - "en": "Is this container underground?", - "nl": "Is deze container ondergrond?" + "en": "Where is this container located?", + "nl": "Waar bevindt deze container zich?" }, "condition": "recycling_type=container", "mappings": [ @@ -88,11 +88,18 @@ "nl": "Dit is een ondergrondse container" } }, + { + "if": "location=indoor", + "then": { + "en": "This container is located indoors", + "nl": "Deze container bevindt binnen" + } + }, { "if": "location=", "then": { - "en": "This container is not underground", - "nl": "Deze container is niet ondergronds" + "en": "This container is located outdoors", + "nl": "Deze container is buiten" } } ] @@ -414,7 +421,6 @@ }, "osmTags": "recycling:small_electrical_appliances=yes" } - ] } ] diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json index 0e3ea89ed..26be51a28 100644 --- a/assets/layers/waste_basket/waste_basket.json +++ b/assets/layers/waste_basket/waste_basket.json @@ -208,7 +208,7 @@ "nl": "Vuilnisbak voor injectienaalden en andere scherpe voorwerpen", "de": "Mülleimer für Nadeln und andere scharfe Gegenstände" }, - "osmTags": "waste=sharps" + "osmTags": "waste=sharps" } ] }, diff --git a/assets/layers/waste_disposal/license_info.json b/assets/layers/waste_disposal/license_info.json new file mode 100644 index 000000000..aeeec767e --- /dev/null +++ b/assets/layers/waste_disposal/license_info.json @@ -0,0 +1,12 @@ +[ + { + "path": "waste_disposal.svg", + "license": "CC0", + "authors": [ + "kocio-pl" + ], + "sources": [ + "https://github.com/gravitystorm/openstreetmap-carto/blob/master/symbols/amenity/waste_disposal.svg" + ] + } +] \ No newline at end of file diff --git a/assets/layers/waste_disposal/waste_disposal.json b/assets/layers/waste_disposal/waste_disposal.json new file mode 100644 index 000000000..5bf4faf8b --- /dev/null +++ b/assets/layers/waste_disposal/waste_disposal.json @@ -0,0 +1,88 @@ +{ + "id": "waste_disposal", + "name": { + "en": "Waste Disposal Bins" + }, + "description": { + "en": "Waste Disposal Bin, medium to large bin for disposal of (household) waste" + }, + "source": { + "osmTags": "amenity=waste_disposal" + }, + "minZoom": 12, + "title": { + "render": { + "en": "Waste Disposal" + } + }, + "mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": "./assets/layers/waste_disposal/waste_disposal.svg" + } + ], + "presets": [ + { + "title": { + "en": "Waste Disposal Bin" + }, + "tags": [ + "amenity=waste_disposal" + ], + "description": { + "en": "Medium to large bin for disposal of (household) waste" + } + } + ], + "tagRenderings": [ + { + "id": "access", + "render": { + "en": "Access: {access}" + }, + "question": { + "en": "Who can use this waste disposal bin?" + }, + "freeform": { + "key": "access", + "type": "string" + }, + "mappings": [ + { + "if": "access=yes", + "then": { + "en": "This bin can be used by anyone" + } + }, + { + "if": "access=no", + "then": { + "en": "This bin is private" + } + }, + { + "if": "access=residents", + "then": { + "en": "This bin is only for residents" + } + } + ] + } + ], + "filter": [ + { + "id": "public-access", + "options": [ + { + "question": { + "en": "Only public access" + }, + "osmTags": "access=yes" + } + ] + } + ] +} \ No newline at end of file diff --git a/assets/layers/waste_disposal/waste_disposal.svg b/assets/layers/waste_disposal/waste_disposal.svg new file mode 100644 index 000000000..db755349c --- /dev/null +++ b/assets/layers/waste_disposal/waste_disposal.svg @@ -0,0 +1,31 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/assets/themes/waste/waste.json b/assets/themes/waste/waste.json index 74f481338..0ae398543 100644 --- a/assets/themes/waste/waste.json +++ b/assets/themes/waste/waste.json @@ -27,6 +27,9 @@ }, { "builtin": "recycling" + }, + { + "builtin": "waste_disposal" } ] } \ No newline at end of file diff --git a/langs/layers/de.json b/langs/layers/de.json index dc35e9bcb..7155c02c7 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -3092,6 +3092,37 @@ }, "waste_basket": { "description": "Dies ist ein öffentlicher Abfalleimer, in den Sie Ihren Müll entsorgen können.", + "filter": { + "0": { + "options": { + "0": { + "question": "Alle Typen" + }, + "1": { + "question": "Mülleimer für Zigaretten" + }, + "2": { + "question": "Mülleimer für Drogen" + }, + "3": { + "question": "Mülleimer für Hundekot" + }, + "4": { + "question": "Mülleimer für allgemeinen Müll" + }, + "5": { + "question": "Mülleimer für Nadeln und andere scharfe Gegenstände" + } + } + }, + "1": { + "options": { + "0": { + "question": "Abfalleimer mit Spender für (Hunde-)Kotbeutel" + } + } + } + }, "mapRendering": { "0": { "iconSize": { diff --git a/langs/layers/en.json b/langs/layers/en.json index 59d75cc71..c9a347aa7 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -3620,6 +3620,180 @@ "render": "Bookcase" } }, + "recycling": { + "description": "A layer with recycling containers and centres", + "filter": { + "0": { + "options": { + "0": { + "question": "Currently open" + } + } + }, + "1": { + "options": { + "0": { + "question": "All recycling types" + }, + "1": { + "question": "Recycling of batteries" + }, + "2": { + "question": "Recycling of beverage cartons" + }, + "3": { + "question": "Recycling of cans" + }, + "4": { + "question": "Recycling of clothes" + }, + "5": { + "question": "Recycling of cooking oil" + }, + "6": { + "question": "Recycling of engine oil" + }, + "7": { + "question": "Recycling of green waste" + }, + "8": { + "question": "Recycling of glass bottles" + }, + "9": { + "question": "Recycling of glass" + }, + "10": { + "question": "Recycling of newspapers" + }, + "11": { + "question": "Recycling of paper" + }, + "12": { + "question": "Recycling of plastic bottles" + }, + "13": { + "question": "Recycling of plastic packaging" + }, + "14": { + "question": "Recycling of plastic" + }, + "15": { + "question": "Recycling of scrap metal" + }, + "16": { + "question": "Recycling of small electrical appliances" + } + } + } + }, + "name": "Recycling", + "presets": { + "0": { + "title": "recycling container" + }, + "1": { + "title": "recycling centre" + } + }, + "tagRenderings": { + "container-location": { + "mappings": { + "0": { + "then": "This is an underground container" + }, + "1": { + "then": "This container is located indoors" + }, + "2": { + "then": "This container is located outdoors" + } + }, + "question": "Where is this container located?" + }, + "opening_hours": { + "mappings": { + "0": { + "then": "24/7" + } + }, + "question": "What are the opening hours of this recycling facility?" + }, + "operator": { + "question": "What company operates this recycling facility?", + "render": "This recycling facility is operated by {operator}" + }, + "recycling-accepts": { + "mappings": { + "0": { + "then": "
Batteries can be recycled here
" + }, + "1": { + "then": "
Beverage cartons can be recycled here
" + }, + "2": { + "then": "
Cans can be recycled here
" + }, + "3": { + "then": "
Clothes can be recycled here
" + }, + "4": { + "then": "
Cooking oil can be recycled here
" + }, + "5": { + "then": "
Engine oil can be recycled here
" + }, + "6": { + "then": "
Green waste can be recycled here
" + }, + "7": { + "then": "
Glass bottles can be recycled here
" + }, + "8": { + "then": "
Glass can be recycled here
" + }, + "9": { + "then": "
Newspapers can be recycled here
" + }, + "10": { + "then": "
Paper can be recycled here
" + }, + "11": { + "then": "
Plastic bottles can be recycled here
" + }, + "12": { + "then": "
Plastic packaging can be recycled here
" + }, + "13": { + "then": "
Plastic can be recycled here
" + }, + "14": { + "then": "
Scrap metal can be recycled here
" + }, + "15": { + "then": "
Shoes can be recycled here
" + }, + "16": { + "then": "
Small electrical appliances can be recycled here
" + } + }, + "question": "What can be recycled here?" + }, + "recycling-type": { + "mappings": { + "0": { + "then": "This is a recycling container" + }, + "1": { + "then": "This is a recycling centre" + } + }, + "question": "What type of recycling is this?" + } + }, + "title": { + "render": "Recycling facility" + } + }, "shops": { "description": "A shop", "name": "Shop", @@ -4462,6 +4636,37 @@ }, "waste_basket": { "description": "This is a public waste basket, thrash can, where you can throw away your thrash.", + "filter": { + "0": { + "options": { + "0": { + "question": "All types" + }, + "1": { + "question": "Waste basket for cigarettes" + }, + "2": { + "question": "Waste basket for drugs" + }, + "3": { + "question": "Waste basket for dog excrement" + }, + "4": { + "question": "Waste basket for trash" + }, + "5": { + "question": "Waste basket for sharps" + } + } + }, + "1": { + "options": { + "0": { + "question": "Waste basket with dispenser for (dog) excrement bags" + } + } + } + }, "mapRendering": { "0": { "iconSize": { @@ -4522,6 +4727,45 @@ "render": "Waste Basket" } }, + "waste_disposal": { + "description": "Waste Disposal Bin, medium to large bin for disposal of (household) waste", + "filter": { + "0": { + "options": { + "0": { + "question": "Only public access" + } + } + } + }, + "name": "Waste Disposal Bins", + "presets": { + "0": { + "description": "Medium to large bin for disposal of (household) waste", + "title": "Waste Disposal Bin" + } + }, + "tagRenderings": { + "access": { + "mappings": { + "0": { + "then": "This bin can be used by anyone" + }, + "1": { + "then": "This bin is private" + }, + "2": { + "then": "This bin is only for residents" + } + }, + "question": "Who can use this waste disposal bin?", + "render": "Access: {access}" + } + }, + "title": { + "render": "Waste Disposal" + } + }, "watermill": { "name": "Watermill" } diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 03a8e7a83..50ea195a9 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3606,6 +3606,180 @@ "render": "Boekenruilkast" } }, + "recycling": { + "description": "Een laag met recyclagingcontainers en -centrums", + "filter": { + "0": { + "options": { + "0": { + "question": "Op dit moment open" + } + } + }, + "1": { + "options": { + "0": { + "question": "Alle recyclingtypes" + }, + "1": { + "question": "Recycling van batterijen" + }, + "2": { + "question": "Recycling van drankpakken" + }, + "3": { + "question": "Recycling van blikken" + }, + "4": { + "question": "Recycling van kleding" + }, + "5": { + "question": "Recycling van frituurvet" + }, + "6": { + "question": "Recycling van motorolie" + }, + "7": { + "question": "Recycling van groen afval" + }, + "8": { + "question": "Recycling van glazen flessen" + }, + "9": { + "question": "Recycling van glas" + }, + "10": { + "question": "Recycling van kranten" + }, + "11": { + "question": "Recycling van papier" + }, + "12": { + "question": "Recycling van plastic flessen" + }, + "13": { + "question": "Recycling van plastic verpakking" + }, + "14": { + "question": "Recycling van plastic" + }, + "15": { + "question": "Recycling van oud metaal" + }, + "16": { + "question": "Recycling van kleine elektrische apparaten" + } + } + } + }, + "name": "Recycling", + "presets": { + "0": { + "title": "recycling container" + }, + "1": { + "title": "recycling centre" + } + }, + "tagRenderings": { + "container-location": { + "mappings": { + "0": { + "then": "Dit is een ondergrondse container" + }, + "1": { + "then": "Deze container bevindt binnen" + }, + "2": { + "then": "Deze container is buiten" + } + }, + "question": "Waar bevindt deze container zich?" + }, + "opening_hours": { + "mappings": { + "0": { + "then": "24/7" + } + }, + "question": "Wat zijn de openingstijden van deze recyclingfaciliteit?" + }, + "operator": { + "question": "Wat is de beheerder van deze recyclingfaciliteit?", + "render": "Deze recyclingfaciliteit wordt beheerd door {operator}" + }, + "recycling-accepts": { + "mappings": { + "0": { + "then": "
Batterijen kunnen hier gerecycled worden
" + }, + "1": { + "then": "
Drankpakken kunnen hier gerecycled worden
" + }, + "2": { + "then": "
Blikken kunnen hier gerecycled worden
" + }, + "3": { + "then": "
Kleren kunnen hier gerecycled worden
" + }, + "4": { + "then": "
Frituurvet kan hier gerecycled worden
" + }, + "5": { + "then": "
Motorolie kan hier gerecycled worden
" + }, + "6": { + "then": "
Groen afval kan hier gerecycled worden
" + }, + "7": { + "then": "
Glazen flessen kunnen hier gerecycled worden
" + }, + "8": { + "then": "
Glas kan hier gerecycled worden
" + }, + "9": { + "then": "
Kranten kunnen hier gerecycled worden
" + }, + "10": { + "then": "
Papier kan hier gerecycled worden
" + }, + "11": { + "then": "
Plastic flessen kunnen hier gerecycled worden
" + }, + "12": { + "then": "
Plastic verpakking kan hier gerecycled worden
" + }, + "13": { + "then": "
Plastic kan hier gerecycled worden
" + }, + "14": { + "then": "
Oud metaal kan hier gerecycled worden
" + }, + "15": { + "then": "
Schoenen kunnen hier gerecycled worden
" + }, + "16": { + "then": "
Kleine elektronische apparaten kunnen hier gerecycled worden
" + } + }, + "question": "Wat kan hier gerecycled worden?" + }, + "recycling-type": { + "mappings": { + "0": { + "then": "Dit is een recyclingcontainer" + }, + "1": { + "then": "Dit is een recyclingcentrum" + } + }, + "question": "Wat voor soort recycling is dit?" + } + }, + "title": { + "render": "Recyclingfaciliteit" + } + }, "shops": { "description": "Een winkel", "name": "Winkel", @@ -4512,6 +4686,37 @@ }, "waste_basket": { "description": "Dit is een publieke vuilnisbak waar je je afval kan weggooien.", + "filter": { + "0": { + "options": { + "0": { + "question": "Alle soorten" + }, + "1": { + "question": "Vuilnisbak voor sigarettenpeuken" + }, + "2": { + "question": "Vuilnisbak voor (vervallen) medicatie en drugs" + }, + "3": { + "question": "Vuilnisbak voor hondenuitwerpselen" + }, + "4": { + "question": "Vuilnisbak voor zwerfvuil" + }, + "5": { + "question": "Vuilnisbak voor injectienaalden en andere scherpe voorwerpen" + } + } + }, + "1": { + "options": { + "0": { + "question": "Vuilnisbak met verdeler voor hondenpoepzakjes" + } + } + } + }, "mapRendering": { "0": { "iconSize": { diff --git a/langs/themes/en.json b/langs/themes/en.json index cd69694b4..54d9eadd7 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1264,6 +1264,10 @@ }, "title": "UK Addresses" }, + "waste": { + "description": "Map showing waste baskets and recycling facilities.", + "title": "Waste" + }, "waste_basket": { "description": "On this map, you'll find waste baskets near you. If a waste basket is missing on this map, you can add it yourself", "shortDescription": "A map with waste baskets", diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 78f9277d0..5a12cc039 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -1057,6 +1057,10 @@ } } }, + "waste": { + "description": "Kaart met afvalbakken en recyclingfaciliteiten.", + "title": "Afval" + }, "waste_basket": { "description": "Op deze kaart vind je vuilnisbakken waar je afval in kan smijten. Ontbreekt er een vuilnisbak? Dan kan je die zelf toevoegen", "shortDescription": "Een kaart met vuilnisbakken", From 1acf72cc704b73b2f01d5c7b8278c88cb1b829b1 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 1 Jan 2022 16:01:33 +0100 Subject: [PATCH 07/43] Fix typo --- assets/layers/recycling/recycling.json | 2 +- langs/layers/nl.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index f25a0dce0..1bebe85be 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -92,7 +92,7 @@ "if": "location=indoor", "then": { "en": "This container is located indoors", - "nl": "Deze container bevindt binnen" + "nl": "Deze container bevindt zich binnen" } }, { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 50ea195a9..9138f969a 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3688,7 +3688,7 @@ "then": "Dit is een ondergrondse container" }, "1": { - "then": "Deze container bevindt binnen" + "then": "Deze container bevindt zich binnen" }, "2": { "then": "Deze container is buiten" From abb1e39612cc154df067f0e8d0620573ac1cd60e Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 2 Jan 2022 20:54:00 +0100 Subject: [PATCH 08/43] Added names and iconBadges --- assets/layers/recycling/recycling.json | 92 +++++++++++++++++++++++++- langs/layers/en.json | 8 +++ langs/layers/nl.json | 8 +++ 3 files changed, 105 insertions(+), 3 deletions(-) diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index 1bebe85be..58e1d28fd 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -16,7 +16,23 @@ "render": { "en": "Recycling facility", "nl": "Recyclingfaciliteit" - } + }, + "mappings": [ + { + "if": "recycling_type=centre", + "then": { + "en": "Recycling centre", + "nl": "Recyclingcentrum" + } + }, + { + "if": "recycling_type=container", + "then": { + "en": "Recycling container", + "nl": "Recyclingcontainer" + } + } + ] }, "mapRendering": [ { @@ -24,7 +40,77 @@ "point", "centroid" ], - "icon": "./assets/layers/recycling/recycling-14.svg" + "icon": "./assets/layers/recycling/recycling-14.svg", + "iconBadges": [ + { + "if": "recycling:batteries=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/batteries.svg" + }, + { + "if": "recycling:beverage_cartons=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/beverage_cartons.svg" + }, + { + "if": "recycling:cans=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/cans.svg" + }, + { + "if": "recycling:clothes=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/clothes.svg" + }, + { + "if": "recycling:cooking_oil=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/cooking_oil.svg" + }, + { + "if": "recycling:engine_oil=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/engine_oil.svg" + }, + { + "if": "recycling:glass=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/glass.svg" + }, + { + "if": "recycling:glass_bottles=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/glass_bottles.svg" + }, + { + "if": "recycling:green_waste=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/garden_waste.svg" + }, + { + "if": "recycling:newspaper=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/newspaper.svg" + }, + { + "if": "recycling:paper=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/paper.svg" + }, + { + "if": "recycling:plastic_bottles=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/plastic_bottles.svg" + }, + { + "if": "recycling:plastic_packaging=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/plastic_packaging.svg" + }, + { + "if": "recycling:plastic=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/plastic.svg" + }, + { + "if": "recycling:scrap_metal=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/scrap_metal.svg" + }, + { + "if": "recyling:shoes=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/shoes.svg" + }, + { + "if": "recycling:small_electrical_appliances=yes", + "then": "circle:#FFFFFF;./assets/layers/recycling/small_electrical_appliances.svg" + } + ] } ], "presets": [ @@ -217,7 +303,7 @@ } }, { - "if": "recylcing:plastic=yes", + "if": "recycling:plastic=yes", "ifnot": "recycling:plastic=", "then": { "en": "
Plastic can be recycled here
", diff --git a/langs/layers/en.json b/langs/layers/en.json index c9a347aa7..dec19cc04 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -3791,6 +3791,14 @@ } }, "title": { + "mappings": { + "0": { + "then": "Recycling centre" + }, + "1": { + "then": "Recycling container" + } + }, "render": "Recycling facility" } }, diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 9138f969a..eded2ac50 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3777,6 +3777,14 @@ } }, "title": { + "mappings": { + "0": { + "then": "Recyclingcentrum" + }, + "1": { + "then": "Recyclingcontainer" + } + }, "render": "Recyclingfaciliteit" } }, From b59f3ca5c846b8d55e94eebb1bd615c968e5101a Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 9 Jan 2022 14:28:14 +0100 Subject: [PATCH 09/43] Added waste to recycling theme --- assets/layers/recycling/recycling.json | 19 +++++++++++++++++++ langs/layers/en.json | 6 ++++++ langs/layers/nl.json | 6 ++++++ 3 files changed, 31 insertions(+) diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index 58e1d28fd..e4d640f21 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -109,6 +109,10 @@ { "if": "recycling:small_electrical_appliances=yes", "then": "circle:#FFFFFF;./assets/layers/recycling/small_electrical_appliances.svg" + }, + { + "if": "recycling:waste=yes", + "then": "circle:#FFFFFF;./assets/layers/waste_disposal/waste_disposal.svg" } ] } @@ -333,6 +337,14 @@ "en": "
Small electrical appliances can be recycled here
", "nl": "
Kleine elektronische apparaten kunnen hier gerecycled worden
" } + }, + { + "if": "recycling:waste=yes", + "ifnot": "recycling:waste=", + "then": { + "en": "
Residual waste can be recycled here
", + "nl": "
Restafval kan hier gerecycled worden
" + } } ] }, @@ -506,6 +518,13 @@ "nl": "Recycling van kleine elektrische apparaten" }, "osmTags": "recycling:small_electrical_appliances=yes" + }, + { + "question": { + "en": "Recycling of residual waste", + "nl": "Recycling van restafval" + }, + "osmTags": "recycling:waste=yes" } ] } diff --git a/langs/layers/en.json b/langs/layers/en.json index f6f2c8e2d..a08fbbb59 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -3692,6 +3692,9 @@ }, "16": { "question": "Recycling of small electrical appliances" + }, + "17": { + "question": "Recycling of residual waste" } } } @@ -3784,6 +3787,9 @@ }, "16": { "then": "
Small electrical appliances can be recycled here
" + }, + "17": { + "then": "
Residual waste can be recycled here
" } }, "question": "What can be recycled here?" diff --git a/langs/layers/nl.json b/langs/layers/nl.json index b9d8c781b..31967af2d 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3675,6 +3675,9 @@ }, "16": { "question": "Recycling van kleine elektrische apparaten" + }, + "17": { + "question": "Recycling van restafval" } } } @@ -3767,6 +3770,9 @@ }, "16": { "then": "
Kleine elektronische apparaten kunnen hier gerecycled worden
" + }, + "17": { + "then": "
Restafval kan hier gerecycled worden
" } }, "question": "Wat kan hier gerecycled worden?" From b5bfd6558b50c99319b498cb119d444e4229cd81 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 9 Jan 2022 17:39:51 +0100 Subject: [PATCH 10/43] Add waste_assen, add location for disposal --- .../layers/waste_disposal/waste_disposal.json | 30 +++++ .../street_lighting_assen.json | 2 +- assets/themes/waste/waste.json | 2 +- assets/themes/waste/waste_assen.json | 113 ++++++++++++++++++ langs/layers/en.json | 14 +++ langs/layers/nl.json | 18 +++ langs/themes/nl.json | 4 + 7 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 assets/themes/waste/waste_assen.json diff --git a/assets/layers/waste_disposal/waste_disposal.json b/assets/layers/waste_disposal/waste_disposal.json index 5bf4faf8b..b74479672 100644 --- a/assets/layers/waste_disposal/waste_disposal.json +++ b/assets/layers/waste_disposal/waste_disposal.json @@ -70,6 +70,36 @@ } } ] + }, + { + "id": "disposal-location", + "question": { + "en": "Where is this container located?", + "nl": "Waar bevindt deze container zich?" + }, + "mappings": [ + { + "if": "location=underground", + "then": { + "en": "This is an underground container", + "nl": "Dit is een ondergrondse container" + } + }, + { + "if": "location=indoor", + "then": { + "en": "This container is located indoors", + "nl": "Deze container bevindt zich binnen" + } + }, + { + "if": "location=", + "then": { + "en": "This container is located outdoors", + "nl": "Deze container is buiten" + } + } + ] } ], "filter": [ diff --git a/assets/themes/street_lighting/street_lighting_assen.json b/assets/themes/street_lighting/street_lighting_assen.json index fe515dba0..49de1e8af 100644 --- a/assets/themes/street_lighting/street_lighting_assen.json +++ b/assets/themes/street_lighting/street_lighting_assen.json @@ -30,7 +30,7 @@ }, "calculatedTags": [ "_closest_osm_street_lamp=feat.closest('street_lamps')?.properties?.id", - "_closest_osm_street_lamp_distance=feat.distanceTo(feat.properties._closest_osm_street_lamp) * 1000", + "_closest_osm_street_lamp_distance=feat.distanceTo(feat.properties._closest_osm_street_lamp)", "_has_closeby_feature=Number(feat.properties._closest_osm_street_lamp_distance) < 5 ? 'yes' : 'no'" ], "title": "Straatlantaarn in dataset", diff --git a/assets/themes/waste/waste.json b/assets/themes/waste/waste.json index 0ae398543..61d65712c 100644 --- a/assets/themes/waste/waste.json +++ b/assets/themes/waste/waste.json @@ -1,7 +1,7 @@ { "id": "waste", "maintainer": "", - "version": "2021-12-31", + "version": "2022-01-19", "language": [ "en", "nl" diff --git a/assets/themes/waste/waste_assen.json b/assets/themes/waste/waste_assen.json new file mode 100644 index 000000000..fadb370fd --- /dev/null +++ b/assets/themes/waste/waste_assen.json @@ -0,0 +1,113 @@ +{ + "id": "waste_assen", + "maintainer": "Robin van der Linde", + "version": "2022-01-09", + "language": [ + "en", + "nl" + ], + "title": { + "nl": "Afval - Assen" + }, + "description": { + "nl": "Kaart met afvalbakken en recyclingfaciliteiten + een dataset voor Assen." + }, + "icon": "./assets/layers/recycling/recycling-14.svg", + "startZoom": 19, + "startLat": 53.24865, + "startLon": 6.60075, + "layers": [ + { + "builtin": "waste_basket", + "override": { + "minzoom": 12 + } + }, + { + "id": "waste_basket_assen", + "name": "Dataset Afvalbakken Assen", + "source": { + "osmTags": "OBJECTID~*", + "geoJson": "https://opendata.arcgis.com/datasets/5b6953ac5a9d4616a7dc75ab0beeac2f_0.geojson", + "isOsmCache": false + }, + "calculatedTags": [ + "_closest_osm_waste_basket=feat.closest('waste_basket')?.properties?.id", + "_closest_osm_waste_basket_distance=feat.distanceTo(feat.properties._closest_osm_waste_basket)", + "_has_closeby_feature=Number(feat.properties._closest_osm_waste_basket_distance) < 10 ? 'yes' : 'no'" + ], + "title": "Afvalbak in dataset", + "mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": { + "render": "circle:red", + "mappings": [ + { + "if": "_has_closeby_feature=yes", + "then": "circle:#008000aa" + } + ] + }, + "iconSize": "20,20,center" + } + ], + "tagRenderings": [ + "all_tags" + ] + }, + { + "builtin": "recycling" + }, + { + "id": "recycling_assen", + "name": "Dataset Recyclingcontainers Assen", + "source": { + "osmTags": "OBJECTID~*", + "geoJson": "https://opendata.arcgis.com/datasets/edb893998e27461b8ed82aad9854d27d_0.geojson", + "isOsmCache": false + }, + "calculatedTags": [ + "_closest_osm_recycling=feat.closest('recycling')?.properties?.id", + "_closest_osm_waste_disposal=feat.closest('waste_disposal')?.properties?.id", + "_closest_osm_recycling_distance=feat.distanceTo(feat.properties._closest_osm_recycling)", + "_closest_osm_waste_disposal_distance=feat.distanceTo(feat.properties._closest_osm_waste_disposal)", + "_has_closeby_recycling=Number(feat.properties._closest_osm_recycling_distance) < 10 ? 'yes' : 'no'", + "_has_closeby_waste_disposal=Number(feat.properties._closest_osm_waste_disposal_distance) < 10 ? 'yes' : 'no'" + ], + "title": "Recyclingcontainer in dataset", + "mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": { + "render": "circle:red", + "mappings": [ + { + "if": "_has_closeby_recycling=yes", + "then": "circle:#008000aa" + }, + { + "if": "_has_closeby_waste_disposal=yes", + "then": "circle:#008000aa" + } + ] + }, + "iconSize": "20,20,center" + } + ], + "tagRenderings": [ + "all_tags" + ] + }, + { + "builtin": "waste_disposal" + } + ], + "hideFromOverview": true +} \ No newline at end of file diff --git a/langs/layers/en.json b/langs/layers/en.json index a08fbbb59..4fa8c01ef 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4784,6 +4784,20 @@ }, "question": "Who can use this waste disposal bin?", "render": "Access: {access}" + }, + "disposal-location": { + "mappings": { + "0": { + "then": "This is an underground container" + }, + "1": { + "then": "This container is located indoors" + }, + "2": { + "then": "This container is located outdoors" + } + }, + "question": "Where is this container located?" } }, "title": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 31967af2d..805df00d8 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -4798,6 +4798,24 @@ "render": "Vuilnisbak" } }, + "waste_disposal": { + "tagRenderings": { + "disposal-location": { + "mappings": { + "0": { + "then": "Dit is een ondergrondse container" + }, + "1": { + "then": "Deze container bevindt zich binnen" + }, + "2": { + "then": "Deze container is buiten" + } + }, + "question": "Waar bevindt deze container zich?" + } + } + }, "watermill": { "description": "Watermolens", "name": "Watermolens", diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 5a12cc039..80cbc37c9 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -1061,6 +1061,10 @@ "description": "Kaart met afvalbakken en recyclingfaciliteiten.", "title": "Afval" }, + "waste_assen": { + "description": "Kaart met afvalbakken en recyclingfaciliteiten + een dataset voor Assen.", + "title": "Afval - Assen" + }, "waste_basket": { "description": "Op deze kaart vind je vuilnisbakken waar je afval in kan smijten. Ontbreekt er een vuilnisbak? Dan kan je die zelf toevoegen", "shortDescription": "Een kaart met vuilnisbakken", From 9a85fd96e194894d5a57861e39201adaff21baff Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 21 Jan 2022 02:39:06 +0100 Subject: [PATCH 11/43] Remove unneeded builtin invocation --- assets/themes/waste/waste.json | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/assets/themes/waste/waste.json b/assets/themes/waste/waste.json index 61d65712c..6ca4ccbc3 100644 --- a/assets/themes/waste/waste.json +++ b/assets/themes/waste/waste.json @@ -25,11 +25,7 @@ "minzoom": 12 } }, - { - "builtin": "recycling" - }, - { - "builtin": "waste_disposal" - } + "recycling", + "waste_disposal" ] } \ No newline at end of file From c85cecfdb586e91f536cd03089b35232bc35fe8e Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 21 Jan 2022 02:47:23 +0100 Subject: [PATCH 12/43] Remove unnecessary builtin --- assets/themes/waste/waste_assen.json | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/assets/themes/waste/waste_assen.json b/assets/themes/waste/waste_assen.json index fadb370fd..810c64a31 100644 --- a/assets/themes/waste/waste_assen.json +++ b/assets/themes/waste/waste_assen.json @@ -59,9 +59,7 @@ "all_tags" ] }, - { - "builtin": "recycling" - }, + "recycling", { "id": "recycling_assen", "name": "Dataset Recyclingcontainers Assen", @@ -105,9 +103,7 @@ "all_tags" ] }, - { - "builtin": "waste_disposal" - } + "waste_disposal" ], "hideFromOverview": true } \ No newline at end of file From 010bd7cdc1cb6cbef4de3fab9ca356b24aca79fa Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Thu, 17 Feb 2022 11:03:11 +0100 Subject: [PATCH 13/43] Fix filters, add plastic --- assets/layers/waste_basket/waste_basket.json | 26 +++- .../mapcomplete-changes.json | 8 ++ langs/layers/de.json | 6 + langs/layers/en.json | 121 ++++++++++++++++++ langs/layers/nl.json | 7 + langs/themes/en.json | 4 + langs/themes/nl.json | 8 ++ 7 files changed, 175 insertions(+), 5 deletions(-) diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json index 26be51a28..9df55c18b 100644 --- a/assets/layers/waste_basket/waste_basket.json +++ b/assets/layers/waste_basket/waste_basket.json @@ -86,6 +86,14 @@ "nl": "Een vuilnisbak voor injectienaalden en andere scherpe voorwerpen", "de": "Ein Abfalleimer für Nadeln und andere scharfe Gegenstände" } + }, + { + "if": "waste=plastic", + "then": { + "en": "A waste basket for plastic", + "nl": "Een vuilnisbak voor plastic", + "de": "Ein Abfalleimer für Plastik" + } } ] }, @@ -176,7 +184,7 @@ "nl": "Vuilnisbak voor sigarettenpeuken", "de": "Mülleimer für Zigaretten" }, - "osmTags": "waste=cigarettes" + "osmTags": "waste~.*cigarettes.*" }, { "question": { @@ -184,7 +192,7 @@ "nl": "Vuilnisbak voor (vervallen) medicatie en drugs", "de": "Mülleimer für Drogen" }, - "osmTags": "waste=drugs" + "osmTags": "waste~.*drugs.*" }, { "question": { @@ -192,7 +200,7 @@ "nl": "Vuilnisbak voor hondenuitwerpselen", "de": "Mülleimer für Hundekot" }, - "osmTags": "waste=dog_excrement" + "osmTags": "waste~.*dog_excrement.*" }, { "question": { @@ -200,7 +208,7 @@ "nl": "Vuilnisbak voor zwerfvuil", "de": "Mülleimer für allgemeinen Müll" }, - "osmTags": "waste=trash" + "osmTags": "waste~.*trash.*" }, { "question": { @@ -208,7 +216,15 @@ "nl": "Vuilnisbak voor injectienaalden en andere scherpe voorwerpen", "de": "Mülleimer für Nadeln und andere scharfe Gegenstände" }, - "osmTags": "waste=sharps" + "osmTags": "waste~.*sharps.*" + }, + { + "question": { + "en": "Waste basket for plastic", + "nl": "Vuilnisbak voor plastic", + "de": "Mülleimer für Plastik" + }, + "osmTags": "waste~.*plastic.*" } ] }, diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index d0108bdbf..8653e1842 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -299,6 +299,14 @@ "if": "theme=uk_addresses", "then": "./assets/themes/uk_addresses/housenumber_unknown.svg" }, + { + "if": "theme=waste", + "then": "./assets/layers/recycling/recycling-14.svg" + }, + { + "if": "theme=waste_assen", + "then": "./assets/layers/recycling/recycling-14.svg" + }, { "if": "theme=waste_basket", "then": "./assets/themes/waste_basket/waste_basket.svg" diff --git a/langs/layers/de.json b/langs/layers/de.json index ae8fdc2ce..f865c9cf1 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -3214,6 +3214,9 @@ }, "5": { "question": "Mülleimer für Nadeln und andere scharfe Gegenstände" + }, + "6": { + "question": "Mülleimer für Plastik" } } }, @@ -3276,6 +3279,9 @@ }, "5": { "then": "Ein Abfalleimer für Nadeln und andere scharfe Gegenstände" + }, + "6": { + "then": "Ein Abfalleimer für Plastik" } }, "question": "Um was für einen Abfalleimer handelt es sich?" diff --git a/langs/layers/en.json b/langs/layers/en.json index fbc713e37..d32c623af 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -1123,6 +1123,7 @@ } }, "birdhide": { + "description": "A birdhide", "filter": { "0": { "options": { @@ -1130,7 +1131,84 @@ "question": "Wheelchair accessible" } } + }, + "1": { + "options": { + "0": { + "question": "Only covered birdhides" + } + } } + }, + "name": "Bird watching places", + "presets": { + "0": { + "description": "A covered shelter where one can watch birds comfortably", + "title": "Birdhide" + }, + "1": { + "description": "A screen or wall with openings to watch birds", + "title": "Bird blind" + } + }, + "tagRenderings": { + "bird-hide-shelter-or-wall": { + "mappings": { + "0": { + "then": "Bird blind" + }, + "1": { + "then": "Bird hide" + }, + "2": { + "then": "Bird tower hide" + }, + "3": { + "then": "Bird hide shelter" + } + }, + "question": "Is this a bird blind or a bird watching shelter?" + }, + "bird-hide-wheelchair": { + "mappings": { + "0": { + "then": "There are special provisions for wheelchair users" + }, + "1": { + "then": "A wheelchair can easily use this birdhide" + }, + "2": { + "then": "This birdhide is reachable by wheelchair, but it is not easy" + }, + "3": { + "then": "Not accessible to wheelchair users" + } + }, + "question": "Is this bird hide accessible to wheelchair users?" + }, + "birdhide-operator": { + "mappings": { + "0": { + "then": "Operated by Natuurpunt" + }, + "1": { + "then": "Operated by the Agency for Nature and Forests" + } + }, + "question": "Who operates this birdhide?", + "render": "Operated by {operator}" + } + }, + "title": { + "mappings": { + "1": { + "then": "Bird hide {name}" + }, + "2": { + "then": "Bird blind {name}" + } + }, + "render": "Bird watching place" } }, "cafe_pub": { @@ -3515,6 +3593,35 @@ }, "nature_reserve": { "description": "A nature reserve is an area where nature can take its course", + "filter": { + "0": { + "options": { + "0": { + "question": "Freely accesible" + } + } + }, + "1": { + "options": { + "0": { + "question": "All nature reserves" + }, + "1": { + "question": "Dogs are allowed to roam freely" + }, + "2": { + "question": "Dogs are allowed if they are leashed" + } + } + } + }, + "name": "Nature reserve", + "presets": { + "0": { + "description": "Add a missing nature reserve", + "title": "nature reserve" + } + }, "tagRenderings": { "Access tag": { "mappings": { @@ -3558,6 +3665,10 @@ }, "question": "Are dogs allowed in this nature reserve?" }, + "Editable description": { + "question": "Is there some extra info?", + "render": "Extra info: {description:0}" + }, "Email": { "question": "What email adress can one send to with questions and problems with this nature reserve?
Respect privacy - only fill out a personal email address if this is widely published", "render": "{email}" @@ -3571,6 +3682,9 @@ "question": "What is the name of this area?", "render": "This area is named {name}" }, + "Non-editable description": { + "render": "Extra information: {description}" + }, "Operator tag": { "mappings": { "0": { @@ -3764,6 +3878,7 @@ }, "parking": { "description": "A layer showing car parkings", + "name": "Parking", "presets": { "0": { "title": "car parking" @@ -5088,6 +5203,9 @@ }, "5": { "question": "Waste basket for sharps" + }, + "6": { + "question": "Waste basket for plastic" } } }, @@ -5150,6 +5268,9 @@ }, "5": { "then": "A waste basket for needles and other sharp objects" + }, + "6": { + "then": "A waste basket for plastic" } }, "question": "What kind of waste basket is this?" diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 2c6d1c12d..d9673fce0 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3473,6 +3473,7 @@ "question": "Zijn honden toegelaten in dit gebied?" }, "Editable description": { + "question": "Is er extra info die je kwijt wil?", "render": "Extra info: {description:0}" }, "Email": { @@ -4999,6 +5000,9 @@ }, "5": { "question": "Vuilnisbak voor injectienaalden en andere scherpe voorwerpen" + }, + "6": { + "question": "Vuilnisbak voor plastic" } } }, @@ -5061,6 +5065,9 @@ }, "5": { "then": "Een vuilnisbak voor injectienaalden en andere scherpe voorwerpen" + }, + "6": { + "then": "Een vuilnisbak voor plastic" } }, "question": "Wat voor soort vuilnisbak is dit?" diff --git a/langs/themes/en.json b/langs/themes/en.json index ccdc4f080..fb0db2db4 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -1286,6 +1286,10 @@ "shortDescription": "Map all the trees", "title": "Trees" }, + "waste": { + "description": "Map showing waste baskets and recycling facilities.", + "title": "Waste" + }, "waste_basket": { "description": "On this map, you'll find waste baskets near you. If a waste basket is missing on this map, you can add it yourself", "shortDescription": "A map with waste baskets", diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 4e77e8d09..e0cd02382 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -1050,6 +1050,14 @@ "shortDescription": "Breng bomen in kaart", "title": "Bomen" }, + "waste": { + "description": "Kaart met afvalbakken en recyclingfaciliteiten.", + "title": "Afval" + }, + "waste_assen": { + "description": "Kaart met afvalbakken en recyclingfaciliteiten + een dataset voor Assen.", + "title": "Afval - Assen" + }, "waste_basket": { "description": "Op deze kaart vind je vuilnisbakken waar je afval in kan smijten. Ontbreekt er een vuilnisbak? Dan kan je die zelf toevoegen", "shortDescription": "Een kaart met vuilnisbakken", From bb30c353b415f3a4121c5d29efb1bf59fe087f03 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Thu, 17 Feb 2022 13:47:59 +0100 Subject: [PATCH 14/43] Fix typo --- assets/layers/waste_basket/waste_basket.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/layers/waste_basket/waste_basket.json b/assets/layers/waste_basket/waste_basket.json index 9df55c18b..cf59c3f29 100644 --- a/assets/layers/waste_basket/waste_basket.json +++ b/assets/layers/waste_basket/waste_basket.json @@ -145,7 +145,7 @@ "nl": "Deze vuilnisbaak heeft waarschijnlijk geen verdeler voor hondenpoepzakjes", "de": "Dieser Abfalleimer hat keinen Spender für (Hunde-)Kotbeutel" }, - "hideInAnwer": true + "hideInAnswer": true } ] } From 41009abe5deae4b014b22a0e5557aab96d8e58b3 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Thu, 17 Feb 2022 13:52:16 +0100 Subject: [PATCH 15/43] Change icon --- assets/layers/recycling/recycling.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index e4d640f21..13fedf0d9 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -40,7 +40,7 @@ "point", "centroid" ], - "icon": "./assets/layers/recycling/recycling-14.svg", + "icon": "circle:white;./assets/layers/recycling/recycling-14.svg", "iconBadges": [ { "if": "recycling:batteries=yes", From 879536c823b4f2ee5d8be44675197c64414524a9 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Thu, 17 Feb 2022 14:34:43 +0100 Subject: [PATCH 16/43] Changed zoom levels, other details --- assets/layers/recycling/recycling.json | 58 ++++++++++++------- .../layers/waste_disposal/waste_disposal.json | 3 +- assets/themes/waste/waste.json | 3 +- langs/layers/en.json | 6 ++ langs/layers/nl.json | 3 + 5 files changed, 49 insertions(+), 24 deletions(-) diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index 13fedf0d9..0e8a1e86b 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -75,7 +75,12 @@ "then": "circle:#FFFFFF;./assets/layers/recycling/glass_bottles.svg" }, { - "if": "recycling:green_waste=yes", + "if": { + "or": [ + "recycling:green_waste=yes", + "recycling:organic=yes" + ] + }, "then": "circle:#FFFFFF;./assets/layers/recycling/garden_waste.svg" }, { @@ -259,85 +264,94 @@ } }, { - "if": "recycling:glass_bottles=yes", - "ifnot": "recycling:glass_bottles=", + "if": "recycling:organic=yes", + "ifnot": "recycling:organic=", "then": { "en": "
Glass bottles can be recycled here
", "nl": "
Glazen flessen kunnen hier gerecycled worden
" - } + }, + "hideInAnswer": true }, { - "if": "recycling:glass=yes", - "ifnot": "recycling:glass=", + "if": "recycling:glass_bottles=yes", + "ifnot": "recycling:glass_bottles=", "then": { "en": "
Glass can be recycled here
", "nl": "
Glas kan hier gerecycled worden
" } }, { - "if": "recycling:newspaper=yes", - "ifnot": "recycling:newspaper=", + "if": "recycling:glass=yes", + "ifnot": "recycling:glass=", "then": { "en": "
Newspapers can be recycled here
", "nl": "
Kranten kunnen hier gerecycled worden
" } }, { - "if": "recycling:paper=yes", - "ifnot": "recycling:paper=", + "if": "recycling:newspaper=yes", + "ifnot": "recycling:newspaper=", "then": { "en": "
Paper can be recycled here
", "nl": "
Papier kan hier gerecycled worden
" } }, { - "if": "recycling:plastic_bottles=yes", - "ifnot": "recycling:plastic_bottles=", + "if": "recycling:paper=yes", + "ifnot": "recycling:paper=", "then": { "en": "
Plastic bottles can be recycled here
", "nl": "
Plastic flessen kunnen hier gerecycled worden
" } }, { - "if": "recycling:plastic_packaging=yes", - "ifnot": "recycling:plastic_packaging=", + "if": "recycling:plastic_bottles=yes", + "ifnot": "recycling:plastic_bottles=", "then": { "en": "
Plastic packaging can be recycled here
", "nl": "
Plastic verpakking kan hier gerecycled worden
" } }, { - "if": "recycling:plastic=yes", - "ifnot": "recycling:plastic=", + "if": "recycling:plastic_packaging=yes", + "ifnot": "recycling:plastic_packaging=", "then": { "en": "
Plastic can be recycled here
", "nl": "
Plastic kan hier gerecycled worden
" } }, { - "if": "recycling:scrap_metal=yes", - "ifnot": "recycling:scrap_metal=", + "if": "recycling:plastic=yes", + "ifnot": "recycling:plastic=", "then": { "en": "
Scrap metal can be recycled here
", "nl": "
Oud metaal kan hier gerecycled worden
" } }, { - "if": "recycling:shoes=yes", - "ifnot": "recycling:shoes=", + "if": "recycling:scrap_metal=yes", + "ifnot": "recycling:scrap_metal=", "then": { "en": "
Shoes can be recycled here
", "nl": "
Schoenen kunnen hier gerecycled worden
" } }, { - "if": "recycling:small_electrical_appliances=yes", - "ifnot": "recycling:small_electrical_appliances=", + "if": "recycling:shoes=yes", + "ifnot": "recycling:shoes=", "then": { "en": "
Small electrical appliances can be recycled here
", "nl": "
Kleine elektronische apparaten kunnen hier gerecycled worden
" } }, + { + "if": "recycling:small_electrical_appliances=yes", + "ifnot": "recycling:small_electrical_appliances=", + "then": { + "en": "
Residual waste can be recycled here
", + "nl": "
Restafval kan hier gerecycled worden
" + } + }, { "if": "recycling:waste=yes", "ifnot": "recycling:waste=", diff --git a/assets/layers/waste_disposal/waste_disposal.json b/assets/layers/waste_disposal/waste_disposal.json index b74479672..dc5c2e334 100644 --- a/assets/layers/waste_disposal/waste_disposal.json +++ b/assets/layers/waste_disposal/waste_disposal.json @@ -9,7 +9,8 @@ "source": { "osmTags": "amenity=waste_disposal" }, - "minZoom": 12, + "minzoom": 18, + "minzoomVisible": 18, "title": { "render": { "en": "Waste Disposal" diff --git a/assets/themes/waste/waste.json b/assets/themes/waste/waste.json index 6ca4ccbc3..1998b893a 100644 --- a/assets/themes/waste/waste.json +++ b/assets/themes/waste/waste.json @@ -22,7 +22,8 @@ { "builtin": "waste_basket", "override": { - "minzoom": 12 + "minzoom": 18, + "minzoomVisible": 18 } }, "recycling", diff --git a/langs/layers/en.json b/langs/layers/en.json index d32c623af..fdb214cf1 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4313,6 +4313,9 @@ }, "17": { "then": "
Residual waste can be recycled here
" + }, + "18": { + "then": "
Residual waste can be recycled here
" } }, "question": "What can be recycled here?" @@ -4336,6 +4339,9 @@ }, "1": { "then": "Recycling container" + }, + "2": { + "then": "Waste disposal bin" } }, "render": "Recycling facility" diff --git a/langs/layers/nl.json b/langs/layers/nl.json index d9673fce0..0b313e648 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -4046,6 +4046,9 @@ }, "17": { "then": "
Restafval kan hier gerecycled worden
" + }, + "18": { + "then": "
Restafval kan hier gerecycled worden
" } }, "question": "Wat kan hier gerecycled worden?" From e2dc9e1490afc5b1fff69e3e9640fdbfda78b21a Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Thu, 17 Feb 2022 20:31:21 +0100 Subject: [PATCH 17/43] Fix translations --- assets/layers/recycling/recycling.json | 44 +++++++++++++------------- langs/layers/en.json | 25 +++++++-------- langs/layers/nl.json | 22 ++++++------- 3 files changed, 44 insertions(+), 47 deletions(-) diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index 0e8a1e86b..928b71ebd 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -267,8 +267,8 @@ "if": "recycling:organic=yes", "ifnot": "recycling:organic=", "then": { - "en": "
Glass bottles can be recycled here
", - "nl": "
Glazen flessen kunnen hier gerecycled worden
" + "en": "
Organic waste can be recycled here
", + "nl": "
Organisch afval kan hier gerecycled worden
" }, "hideInAnswer": true }, @@ -276,80 +276,80 @@ "if": "recycling:glass_bottles=yes", "ifnot": "recycling:glass_bottles=", "then": { - "en": "
Glass can be recycled here
", - "nl": "
Glas kan hier gerecycled worden
" + "en": "
Glass bottles can be recycled here
", + "nl": "
Glazen flessen kunnen hier gerecycled worden
" } }, { "if": "recycling:glass=yes", "ifnot": "recycling:glass=", "then": { - "en": "
Newspapers can be recycled here
", - "nl": "
Kranten kunnen hier gerecycled worden
" + "en": "
Glass can be recycled here
", + "nl": "
Glas kan hier gerecycled worden
" } }, { "if": "recycling:newspaper=yes", "ifnot": "recycling:newspaper=", "then": { - "en": "
Paper can be recycled here
", - "nl": "
Papier kan hier gerecycled worden
" + "en": "
Newspapers can be recycled here
", + "nl": "
Kranten kunnen hier gerecycled worden
" } }, { "if": "recycling:paper=yes", "ifnot": "recycling:paper=", "then": { - "en": "
Plastic bottles can be recycled here
", - "nl": "
Plastic flessen kunnen hier gerecycled worden
" + "en": "
Paper can be recycled here
", + "nl": "
Papier kan hier gerecycled worden
" } }, { "if": "recycling:plastic_bottles=yes", "ifnot": "recycling:plastic_bottles=", "then": { - "en": "
Plastic packaging can be recycled here
", - "nl": "
Plastic verpakking kan hier gerecycled worden
" + "en": "
Plastic bottles can be recycled here
", + "nl": "
Plastic flessen kunnen hier gerecycled worden
" } }, { "if": "recycling:plastic_packaging=yes", "ifnot": "recycling:plastic_packaging=", "then": { - "en": "
Plastic can be recycled here
", - "nl": "
Plastic kan hier gerecycled worden
" + "en": "
Plastic packaging can be recycled here
", + "nl": "
Plastic verpakking kan hier gerecycled worden
" } }, { "if": "recycling:plastic=yes", "ifnot": "recycling:plastic=", "then": { - "en": "
Scrap metal can be recycled here
", - "nl": "
Oud metaal kan hier gerecycled worden
" + "en": "
Plastic can be recycled here
", + "nl": "
Plastic kan hier gerecycled worden
" } }, { "if": "recycling:scrap_metal=yes", "ifnot": "recycling:scrap_metal=", "then": { - "en": "
Shoes can be recycled here
", - "nl": "
Schoenen kunnen hier gerecycled worden
" + "en": "
Scrap metal can be recycled here
", + "nl": "
Oud metaal kan hier gerecycled worden
" } }, { "if": "recycling:shoes=yes", "ifnot": "recycling:shoes=", "then": { - "en": "
Small electrical appliances can be recycled here
", - "nl": "
Kleine elektronische apparaten kunnen hier gerecycled worden
" + "en": "
Shoes can be recycled here
", + "nl": "
Schoenen kunnen hier gerecycled worden
" } }, { "if": "recycling:small_electrical_appliances=yes", "ifnot": "recycling:small_electrical_appliances=", "then": { - "en": "
Residual waste can be recycled here
", - "nl": "
Restafval kan hier gerecycled worden
" + "en": "
Small electrical appliances can be recycled here
", + "nl": "
Kleine elektronische apparaten kunnen hier gerecycled worden
" } }, { diff --git a/langs/layers/en.json b/langs/layers/en.json index fdb214cf1..0f14fb194 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4282,37 +4282,37 @@ "then": "
Green waste can be recycled here
" }, "7": { - "then": "
Glass bottles can be recycled here
" + "then": "
Organic waste can be recycled here
" }, "8": { - "then": "
Glass can be recycled here
" + "then": "
Glass bottles can be recycled here
" }, "9": { - "then": "
Newspapers can be recycled here
" + "then": "
Glass can be recycled here
" }, "10": { - "then": "
Paper can be recycled here
" + "then": "
Newspapers can be recycled here
" }, "11": { - "then": "
Plastic bottles can be recycled here
" + "then": "
Paper can be recycled here
" }, "12": { - "then": "
Plastic packaging can be recycled here
" + "then": "
Plastic bottles can be recycled here
" }, "13": { - "then": "
Plastic can be recycled here
" + "then": "
Plastic packaging can be recycled here
" }, "14": { - "then": "
Scrap metal can be recycled here
" + "then": "
Plastic can be recycled here
" }, "15": { - "then": "
Shoes can be recycled here
" + "then": "
Scrap metal can be recycled here
" }, "16": { - "then": "
Small electrical appliances can be recycled here
" + "then": "
Shoes can be recycled here
" }, "17": { - "then": "
Residual waste can be recycled here
" + "then": "
Small electrical appliances can be recycled here
" }, "18": { "then": "
Residual waste can be recycled here
" @@ -4339,9 +4339,6 @@ }, "1": { "then": "Recycling container" - }, - "2": { - "then": "Waste disposal bin" } }, "render": "Recycling facility" diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 0b313e648..0b44ce9f2 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -4015,37 +4015,37 @@ "then": "
Groen afval kan hier gerecycled worden
" }, "7": { - "then": "
Glazen flessen kunnen hier gerecycled worden
" + "then": "
Organisch afval kan hier gerecycled worden
" }, "8": { - "then": "
Glas kan hier gerecycled worden
" + "then": "
Glazen flessen kunnen hier gerecycled worden
" }, "9": { - "then": "
Kranten kunnen hier gerecycled worden
" + "then": "
Glas kan hier gerecycled worden
" }, "10": { - "then": "
Papier kan hier gerecycled worden
" + "then": "
Kranten kunnen hier gerecycled worden
" }, "11": { - "then": "
Plastic flessen kunnen hier gerecycled worden
" + "then": "
Papier kan hier gerecycled worden
" }, "12": { - "then": "
Plastic verpakking kan hier gerecycled worden
" + "then": "
Plastic flessen kunnen hier gerecycled worden
" }, "13": { - "then": "
Plastic kan hier gerecycled worden
" + "then": "
Plastic verpakking kan hier gerecycled worden
" }, "14": { - "then": "
Oud metaal kan hier gerecycled worden
" + "then": "
Plastic kan hier gerecycled worden
" }, "15": { - "then": "
Schoenen kunnen hier gerecycled worden
" + "then": "
Oud metaal kan hier gerecycled worden
" }, "16": { - "then": "
Kleine elektronische apparaten kunnen hier gerecycled worden
" + "then": "
Schoenen kunnen hier gerecycled worden
" }, "17": { - "then": "
Restafval kan hier gerecycled worden
" + "then": "
Kleine elektronische apparaten kunnen hier gerecycled worden
" }, "18": { "then": "
Restafval kan hier gerecycled worden
" From 4801d1df495ad7f802804ee2a63aec94e7b069db Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 18 Feb 2022 03:15:37 +0100 Subject: [PATCH 18/43] Add small script to autoconvert images to use tags instead --- UI/i18n/Translation.ts | 7 +++- scripts/fixImagesInTagRenderings.ts | 65 +++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 scripts/fixImagesInTagRenderings.ts diff --git a/UI/i18n/Translation.ts b/UI/i18n/Translation.ts index a068be199..30a0f74cb 100644 --- a/UI/i18n/Translation.ts +++ b/UI/i18n/Translation.ts @@ -130,13 +130,16 @@ export class Translation extends BaseUIElement { } public Subs(text: any): Translation { + return this.OnEveryLanguage((template, lang) => Utils.SubstituteKeys(template, text, lang)) + } + + public OnEveryLanguage(f: (s: string, language: string) => string): Translation { const newTranslations = {}; for (const lang in this.translations) { if (!this.translations.hasOwnProperty(lang)) { continue; } - let template: string = this.translations[lang]; - newTranslations[lang] = Utils.SubstituteKeys(template, text, lang); + newTranslations[lang] = f(this.translations[lang], lang); } return new Translation(newTranslations); diff --git a/scripts/fixImagesInTagRenderings.ts b/scripts/fixImagesInTagRenderings.ts new file mode 100644 index 000000000..be5e58e43 --- /dev/null +++ b/scripts/fixImagesInTagRenderings.ts @@ -0,0 +1,65 @@ +import {readFileSync, writeFileSync} from "fs"; +import {DesugaringStep} from "../Models/ThemeConfig/Conversion/Conversion"; +import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson"; +import {Utils} from "../Utils"; +import Translations from "../UI/i18n/Translations"; + +class ConvertImagesToIcon extends DesugaringStep { + private _iconClass: string; + + constructor(iconClass: string) { + super("Searches for images in the 'then' path, removes the block and extracts the image itself a 'icon'", + [], "ConvertImagesToIcon") + this._iconClass = iconClass; + } + + convert(json: LayerConfigJson, context: string): { result: LayerConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } { + const information = [] + const errors = [] + json = Utils.Clone(json) + Utils.WalkPath( + ["tagRenderings", "mappings"], + json, + mapping => { + const then = Translations.T(mapping.then) + const images = Utils.Dedup(then.ExtractImages()) + if (images.length == 0) { + return mapping + } + if (images.length > 1) { + errors.push("The mapping " + mapping.then + " has multiple images: " + images.join(", ")) + } + information.push("Replaced image " + images[0]) + const replaced = then.OnEveryLanguage((s) => { + return s.replace(/(
]*>)?]*> ?/, "").replace(/<\/div>$/, "") + }) + + mapping.then = replaced.translations + mapping.icon = {path: images[0], class: this._iconClass} + return mapping + } + ) + + return { + information, + result: json + }; + } +} + +/** + * One-of script to load one layer.json-file and rewrite all tagrenderings + */ +function main() { + let args = [...process.argv] + args.splice(0, 2) + const path = args[0] + const iconClass = args[1] ?? "small" + console.log("Fixing images in " + path) + const parsed = JSON.parse(readFileSync(path, "UTF8")) + const converted = new ConvertImagesToIcon(iconClass).convertStrict(parsed, "While running the fixImagesInTagRenderings-script") + writeFileSync(path + ".autoconverted.json", JSON.stringify(converted, null, " ")) + console.log("Written fixed version to " + path + ".autoconverted.json") +} + +main(); \ No newline at end of file From f2d022664a275c2cfe48cfe02af202546fd5cd6a Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 18 Feb 2022 03:16:05 +0100 Subject: [PATCH 19/43] Convert tagrendering-mappings with images to tagrendering-mapping with an icon --- assets/layers/recycling/recycling.json | 154 ++++++++++++++++++------- 1 file changed, 115 insertions(+), 39 deletions(-) diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index 928b71ebd..3d1d23ffb 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -211,153 +211,229 @@ "if": "recycling:batteries=yes", "ifnot": "recycling:batteries=", "then": { - "en": "
Batteries can be recycled here
", - "nl": "
Batterijen kunnen hier gerecycled worden
" + "en": "Batteries can be recycled here", + "nl": "Batterijen kunnen hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/batteries.svg", + "class": "medium" } }, { "if": "recycling:beverage_cartons=yes", "ifnot": "recycling:beverage_cartons=", "then": { - "en": "
Beverage cartons can be recycled here
", - "nl": "
Drankpakken kunnen hier gerecycled worden
" + "en": "Beverage cartons can be recycled here", + "nl": "Drankpakken kunnen hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/beverage_cartons.svg", + "class": "medium" } }, { "if": "recycling:cans=yes", "ifnot": "recycling:cans=", "then": { - "en": "
Cans can be recycled here
", - "nl": "
Blikken kunnen hier gerecycled worden
" + "en": "Cans can be recycled here", + "nl": "Blikken kunnen hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/cans.svg", + "class": "medium" } }, { "if": "recycling:clothes=yes", "ifnot": "recycling:clothes=", "then": { - "en": "
Clothes can be recycled here
", - "nl": "
Kleren kunnen hier gerecycled worden
" + "en": "Clothes can be recycled here", + "nl": "Kleren kunnen hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/clothes.svg", + "class": "medium" } }, { "if": "recycling:cooking_oil=yes", "ifnot": "recycling:cooking_oil=", "then": { - "en": "
Cooking oil can be recycled here
", - "nl": "
Frituurvet kan hier gerecycled worden
" + "en": "Cooking oil can be recycled here", + "nl": "Frituurvet kan hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/cooking_oil.svg", + "class": "medium" } }, { "if": "recycling:engine_oil=yes", "ifnot": "recycling:engine_oil=", "then": { - "en": "
Engine oil can be recycled here
", - "nl": "
Motorolie kan hier gerecycled worden
" + "en": "Engine oil can be recycled here", + "nl": "Motorolie kan hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/engine_oil.svg", + "class": "medium" } }, { "if": "recycling:green_waste=yes", "ifnot": "recycling:green_waste=", "then": { - "en": "
Green waste can be recycled here
", - "nl": "
Groen afval kan hier gerecycled worden
" + "en": "Green waste can be recycled here", + "nl": "Groen afval kan hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/garden_waste.svg", + "class": "medium" } }, { "if": "recycling:organic=yes", "ifnot": "recycling:organic=", "then": { - "en": "
Organic waste can be recycled here
", - "nl": "
Organisch afval kan hier gerecycled worden
" + "en": "Organic waste can be recycled here", + "nl": "Organisch afval kan hier gerecycled worden" }, - "hideInAnswer": true + "hideInAnswer": true, + "icon": { + "path": "./assets/layers/recycling/garden_waste.svg", + "class": "medium" + } }, { "if": "recycling:glass_bottles=yes", "ifnot": "recycling:glass_bottles=", "then": { - "en": "
Glass bottles can be recycled here
", - "nl": "
Glazen flessen kunnen hier gerecycled worden
" + "en": "Glass bottles can be recycled here", + "nl": "Glazen flessen kunnen hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/glass_bottles.svg", + "class": "medium" } }, { "if": "recycling:glass=yes", "ifnot": "recycling:glass=", "then": { - "en": "
Glass can be recycled here
", - "nl": "
Glas kan hier gerecycled worden
" + "en": "Glass can be recycled here", + "nl": "Glas kan hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/glass.svg", + "class": "medium" } }, { "if": "recycling:newspaper=yes", "ifnot": "recycling:newspaper=", "then": { - "en": "
Newspapers can be recycled here
", - "nl": "
Kranten kunnen hier gerecycled worden
" + "en": "Newspapers can be recycled here", + "nl": "Kranten kunnen hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/newspaper.svg", + "class": "medium" } }, { "if": "recycling:paper=yes", "ifnot": "recycling:paper=", "then": { - "en": "
Paper can be recycled here
", - "nl": "
Papier kan hier gerecycled worden
" + "en": "Paper can be recycled here", + "nl": "Papier kan hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/paper.svg", + "class": "medium" } }, { "if": "recycling:plastic_bottles=yes", "ifnot": "recycling:plastic_bottles=", "then": { - "en": "
Plastic bottles can be recycled here
", - "nl": "
Plastic flessen kunnen hier gerecycled worden
" + "en": "Plastic bottles can be recycled here", + "nl": "Plastic flessen kunnen hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/plastic_bottles.svg", + "class": "medium" } }, { "if": "recycling:plastic_packaging=yes", "ifnot": "recycling:plastic_packaging=", "then": { - "en": "
Plastic packaging can be recycled here
", - "nl": "
Plastic verpakking kan hier gerecycled worden
" + "en": "Plastic packaging can be recycled here", + "nl": "Plastic verpakking kan hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/plastic_packaging.svg", + "class": "medium" } }, { "if": "recycling:plastic=yes", "ifnot": "recycling:plastic=", "then": { - "en": "
Plastic can be recycled here
", - "nl": "
Plastic kan hier gerecycled worden
" + "en": "Plastic can be recycled here", + "nl": "Plastic kan hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/plastic.svg", + "class": "medium" } }, { "if": "recycling:scrap_metal=yes", "ifnot": "recycling:scrap_metal=", "then": { - "en": "
Scrap metal can be recycled here
", - "nl": "
Oud metaal kan hier gerecycled worden
" + "en": "Scrap metal can be recycled here", + "nl": "Oud metaal kan hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/scrap_metal.svg", + "class": "medium" } }, { "if": "recycling:shoes=yes", "ifnot": "recycling:shoes=", "then": { - "en": "
Shoes can be recycled here
", - "nl": "
Schoenen kunnen hier gerecycled worden
" + "en": "Shoes can be recycled here", + "nl": "Schoenen kunnen hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/shoes.svg", + "class": "medium" } }, { "if": "recycling:small_electrical_appliances=yes", "ifnot": "recycling:small_electrical_appliances=", "then": { - "en": "
Small electrical appliances can be recycled here
", - "nl": "
Kleine elektronische apparaten kunnen hier gerecycled worden
" + "en": "Small electrical appliances can be recycled here", + "nl": "Kleine elektronische apparaten kunnen hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/small_electrical_appliances.svg", + "class": "medium" } }, { "if": "recycling:waste=yes", "ifnot": "recycling:waste=", "then": { - "en": "
Residual waste can be recycled here
", - "nl": "
Restafval kan hier gerecycled worden
" + "en": "Residual waste can be recycled here", + "nl": "Restafval kan hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/waste_disposal/waste_disposal.svg", + "class": "medium" } } ] From 63327c883293a6dfaac2e3ad1c48a2119a652c18 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Fri, 18 Feb 2022 11:02:38 +0100 Subject: [PATCH 20/43] Reset translations (double images) --- langs/layers/en.json | 38 +++++++++++++++++++------------------- langs/layers/nl.json | 38 +++++++++++++++++++------------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/langs/layers/en.json b/langs/layers/en.json index 34b124697..faf063421 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4258,61 +4258,61 @@ "recycling-accepts": { "mappings": { "0": { - "then": "
Batteries can be recycled here
" + "then": "Batteries can be recycled here" }, "1": { - "then": "
Beverage cartons can be recycled here
" + "then": "Beverage cartons can be recycled here" }, "2": { - "then": "
Cans can be recycled here
" + "then": "Cans can be recycled here" }, "3": { - "then": "
Clothes can be recycled here
" + "then": "Clothes can be recycled here" }, "4": { - "then": "
Cooking oil can be recycled here
" + "then": "Cooking oil can be recycled here" }, "5": { - "then": "
Engine oil can be recycled here
" + "then": "Engine oil can be recycled here" }, "6": { - "then": "
Green waste can be recycled here
" + "then": "Green waste can be recycled here" }, "7": { - "then": "
Organic waste can be recycled here
" + "then": "Organic waste can be recycled here" }, "8": { - "then": "
Glass bottles can be recycled here
" + "then": "Glass bottles can be recycled here" }, "9": { - "then": "
Glass can be recycled here
" + "then": "Glass can be recycled here" }, "10": { - "then": "
Newspapers can be recycled here
" + "then": "Newspapers can be recycled here" }, "11": { - "then": "
Paper can be recycled here
" + "then": "Paper can be recycled here" }, "12": { - "then": "
Plastic bottles can be recycled here
" + "then": "Plastic bottles can be recycled here" }, "13": { - "then": "
Plastic packaging can be recycled here
" + "then": "Plastic packaging can be recycled here" }, "14": { - "then": "
Plastic can be recycled here
" + "then": "Plastic can be recycled here" }, "15": { - "then": "
Scrap metal can be recycled here
" + "then": "Scrap metal can be recycled here" }, "16": { - "then": "
Shoes can be recycled here
" + "then": "Shoes can be recycled here" }, "17": { - "then": "
Small electrical appliances can be recycled here
" + "then": "Small electrical appliances can be recycled here" }, "18": { - "then": "
Residual waste can be recycled here
" + "then": "Residual waste can be recycled here" } }, "question": "What can be recycled here?" diff --git a/langs/layers/nl.json b/langs/layers/nl.json index f7ab9beb0..9433aaf7b 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -4050,61 +4050,61 @@ "recycling-accepts": { "mappings": { "0": { - "then": "
Batterijen kunnen hier gerecycled worden
" + "then": "Batterijen kunnen hier gerecycled worden" }, "1": { - "then": "
Drankpakken kunnen hier gerecycled worden
" + "then": "Drankpakken kunnen hier gerecycled worden" }, "2": { - "then": "
Blikken kunnen hier gerecycled worden
" + "then": "Blikken kunnen hier gerecycled worden" }, "3": { - "then": "
Kleren kunnen hier gerecycled worden
" + "then": "Kleren kunnen hier gerecycled worden" }, "4": { - "then": "
Frituurvet kan hier gerecycled worden
" + "then": "Frituurvet kan hier gerecycled worden" }, "5": { - "then": "
Motorolie kan hier gerecycled worden
" + "then": "Motorolie kan hier gerecycled worden" }, "6": { - "then": "
Groen afval kan hier gerecycled worden
" + "then": "Groen afval kan hier gerecycled worden" }, "7": { - "then": "
Organisch afval kan hier gerecycled worden
" + "then": "Organisch afval kan hier gerecycled worden" }, "8": { - "then": "
Glazen flessen kunnen hier gerecycled worden
" + "then": "Glazen flessen kunnen hier gerecycled worden" }, "9": { - "then": "
Glas kan hier gerecycled worden
" + "then": "Glas kan hier gerecycled worden" }, "10": { - "then": "
Kranten kunnen hier gerecycled worden
" + "then": "Kranten kunnen hier gerecycled worden" }, "11": { - "then": "
Papier kan hier gerecycled worden
" + "then": "Papier kan hier gerecycled worden" }, "12": { - "then": "
Plastic flessen kunnen hier gerecycled worden
" + "then": "Plastic flessen kunnen hier gerecycled worden" }, "13": { - "then": "
Plastic verpakking kan hier gerecycled worden
" + "then": "Plastic verpakking kan hier gerecycled worden" }, "14": { - "then": "
Plastic kan hier gerecycled worden
" + "then": "Plastic kan hier gerecycled worden" }, "15": { - "then": "
Oud metaal kan hier gerecycled worden
" + "then": "Oud metaal kan hier gerecycled worden" }, "16": { - "then": "
Schoenen kunnen hier gerecycled worden
" + "then": "Schoenen kunnen hier gerecycled worden" }, "17": { - "then": "
Kleine elektronische apparaten kunnen hier gerecycled worden
" + "then": "Kleine elektronische apparaten kunnen hier gerecycled worden" }, "18": { - "then": "
Restafval kan hier gerecycled worden
" + "then": "Restafval kan hier gerecycled worden" } }, "question": "Wat kan hier gerecycled worden?" From 62153e0eb42530c68b2ba4d5068317aeea84c420 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Fri, 18 Feb 2022 11:52:44 +0100 Subject: [PATCH 21/43] Small fixes (organic and appliances) --- assets/layers/recycling/recycling.json | 36 +++++++++++++++++++++++--- langs/layers/en.json | 3 +++ langs/layers/nl.json | 5 +++- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index 3d1d23ffb..b0e3f9562 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -112,7 +112,12 @@ "then": "circle:#FFFFFF;./assets/layers/recycling/shoes.svg" }, { - "if": "recycling:small_electrical_appliances=yes", + "if": { + "or": [ + "recycling:small_appliances=yes", + "recycling:small_electrical_appliances=yes" + ] + }, "then": "circle:#FFFFFF;./assets/layers/recycling/small_electrical_appliances.svg" }, { @@ -412,13 +417,26 @@ "class": "medium" } }, + { + "if": "recycling:small_appliances=yes", + "ifnot": "recycling:small_appliances=", + "then": { + "en": "Small electrical appliances can be recycled here", + "nl": "Kleine elektrische apparaten kunnen hier gerecycled worden" + }, + "icon": { + "path": "./assets/layers/recycling/small_electrical_appliances.svg", + "class": "medium" + } + }, { "if": "recycling:small_electrical_appliances=yes", "ifnot": "recycling:small_electrical_appliances=", "then": { "en": "Small electrical appliances can be recycled here", - "nl": "Kleine elektronische apparaten kunnen hier gerecycled worden" + "nl": "Kleine elektrische apparaten kunnen hier gerecycled worden" }, + "hideInAnswer": true, "icon": { "path": "./assets/layers/recycling/small_electrical_appliances.svg", "class": "medium" @@ -544,7 +562,12 @@ "en": "Recycling of green waste", "nl": "Recycling van groen afval" }, - "osmTags": "recycling:green_waste=yes" + "osmTags": { + "or": [ + "recycling:green_waste=yes", + "recycling:organic=yes" + ] + } }, { "question": { @@ -607,7 +630,12 @@ "en": "Recycling of small electrical appliances", "nl": "Recycling van kleine elektrische apparaten" }, - "osmTags": "recycling:small_electrical_appliances=yes" + "osmTags": { + "or": [ + "recycling:small_appliances=yes", + "recycling:small_electrical_appliances=yes" + ] + } }, { "question": { diff --git a/langs/layers/en.json b/langs/layers/en.json index faf063421..a10d84461 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4312,6 +4312,9 @@ "then": "Small electrical appliances can be recycled here" }, "18": { + "then": "Small electrical appliances can be recycled here" + }, + "19": { "then": "Residual waste can be recycled here" } }, diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 9433aaf7b..23fda94ad 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -4101,9 +4101,12 @@ "then": "Schoenen kunnen hier gerecycled worden" }, "17": { - "then": "Kleine elektronische apparaten kunnen hier gerecycled worden" + "then": "Kleine elektrische apparaten kunnen hier gerecycled worden" }, "18": { + "then": "Kleine elektrische apparaten kunnen hier gerecycled worden" + }, + "19": { "then": "Restafval kan hier gerecycled worden" } }, From 682b68a094c3d729472e86707f7412ca89939822 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 18 Feb 2022 23:10:27 +0100 Subject: [PATCH 22/43] Regenerate schemas, more fixes to images and licenses --- Docs/Schemas/LayerConfigJson.schema.json | 33 +- Docs/Schemas/LayerConfigJsonJSC.ts | 33 +- Docs/Schemas/LayoutConfigJson.schema.json | 33 +- Docs/Schemas/LayoutConfigJsonJSC.ts | 33 +- .../LineRenderingConfigJson.schema.json | 23 +- Docs/Schemas/LineRenderingConfigJsonJSC.ts | 23 +- .../PointRenderingConfigJson.schema.json | 23 +- Docs/Schemas/PointRenderingConfigJsonJSC.ts | 23 +- .../TagRenderingConfigJson.schema.json | 23 +- Docs/Schemas/TagRenderingConfigJsonJSC.ts | 23 +- Docs/Schemas/TilesourceConfigJson.schema.json | 23 +- Docs/Schemas/TilesourceConfigJsonJSC.ts | 23 +- Models/ThemeConfig/Conversion/FixImages.ts | 24 +- Models/ThemeConfig/Conversion/PrepareLayer.ts | 16 +- Models/ThemeConfig/Conversion/PrepareTheme.ts | 4 + Models/ThemeConfig/Conversion/Validation.ts | 24 +- Models/ThemeConfig/LayoutConfig.ts | 2 +- Utils.ts | 16 +- .../bike_repair_station/license_info.json | 14 + .../charging_station/charging_station.json | 34 +- .../charging_station.protojson | 2 +- assets/layers/charging_station/csvToJson.ts | 2 +- assets/layoutconfigmeta.json | 749 ++++++++++++++++++ assets/tagrenderingconfigmeta.json | 38 + assets/themes/buurtnatuur/buurtnatuur.json | 9 +- assets/themes/campersite/campersite.json | 2 +- assets/themes/campersite/license_info.json | 20 +- ...ohnmobilstellplatz.jpg => social_image.jpg} | Bin assets/themes/cyclofix/cyclofix.json | 2 +- assets/themes/grb_import/grb.json | 8 +- langs/themes/icon.json | 21 + langs/themes/nl.json | 6 +- scripts/generateLayerOverview.ts | 7 +- test/LegacyThemeLoader.spec.ts | 100 ++- 34 files changed, 1298 insertions(+), 118 deletions(-) rename assets/themes/campersite/{Barßel_Wohnmobilstellplatz.jpg => social_image.jpg} (100%) create mode 100644 langs/themes/icon.json diff --git a/Docs/Schemas/LayerConfigJson.schema.json b/Docs/Schemas/LayerConfigJson.schema.json index 52f9f1e69..9e72ff1a3 100644 --- a/Docs/Schemas/LayerConfigJson.schema.json +++ b/Docs/Schemas/LayerConfigJson.schema.json @@ -426,6 +426,16 @@ "items": { "$ref": "#/definitions/default_2" } + }, + "syncSelection": { + "description": "If set, synchronizes wether or not this layer is selected.\n\nno: Do not sync at all, always revert to default\nlocal: keep selection on local storage\ntheme-only: sync via OSM, but this layer will only be toggled in this theme\nglobal: all layers with this ID will be synced accross all themes", + "enum": [ + "global", + "local", + "no", + "theme-only" + ], + "type": "string" } }, "required": [ @@ -606,7 +616,28 @@ }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", - "type": "string" + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] }, "hideInAnswer": { "description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", diff --git a/Docs/Schemas/LayerConfigJsonJSC.ts b/Docs/Schemas/LayerConfigJsonJSC.ts index 35dc011ac..0348fcb91 100644 --- a/Docs/Schemas/LayerConfigJsonJSC.ts +++ b/Docs/Schemas/LayerConfigJsonJSC.ts @@ -426,6 +426,16 @@ export default { "items": { "$ref": "#/definitions/default_2" } + }, + "syncSelection": { + "description": "If set, synchronizes wether or not this layer is selected.\n\nno: Do not sync at all, always revert to default\nlocal: keep selection on local storage\ntheme-only: sync via OSM, but this layer will only be toggled in this theme\nglobal: all layers with this ID will be synced accross all themes", + "enum": [ + "global", + "local", + "no", + "theme-only" + ], + "type": "string" } }, "required": [ @@ -604,7 +614,28 @@ export default { }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", - "type": "string" + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] }, "hideInAnswer": { "description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", diff --git a/Docs/Schemas/LayoutConfigJson.schema.json b/Docs/Schemas/LayoutConfigJson.schema.json index dcfda9918..3321c1eed 100644 --- a/Docs/Schemas/LayoutConfigJson.schema.json +++ b/Docs/Schemas/LayoutConfigJson.schema.json @@ -460,7 +460,28 @@ }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", - "type": "string" + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] }, "hideInAnswer": { "description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", @@ -1314,6 +1335,16 @@ "items": { "$ref": "#/definitions/default_2" } + }, + "syncSelection": { + "description": "If set, synchronizes wether or not this layer is selected.\n\nno: Do not sync at all, always revert to default\nlocal: keep selection on local storage\ntheme-only: sync via OSM, but this layer will only be toggled in this theme\nglobal: all layers with this ID will be synced accross all themes", + "enum": [ + "global", + "local", + "no", + "theme-only" + ], + "type": "string" } }, "required": [ diff --git a/Docs/Schemas/LayoutConfigJsonJSC.ts b/Docs/Schemas/LayoutConfigJsonJSC.ts index 14f441f16..49ca6c05e 100644 --- a/Docs/Schemas/LayoutConfigJsonJSC.ts +++ b/Docs/Schemas/LayoutConfigJsonJSC.ts @@ -458,7 +458,28 @@ export default { }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", - "type": "string" + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] }, "hideInAnswer": { "description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", @@ -1304,6 +1325,16 @@ export default { "items": { "$ref": "#/definitions/default_2" } + }, + "syncSelection": { + "description": "If set, synchronizes wether or not this layer is selected.\n\nno: Do not sync at all, always revert to default\nlocal: keep selection on local storage\ntheme-only: sync via OSM, but this layer will only be toggled in this theme\nglobal: all layers with this ID will be synced accross all themes", + "enum": [ + "global", + "local", + "no", + "theme-only" + ], + "type": "string" } }, "required": [ diff --git a/Docs/Schemas/LineRenderingConfigJson.schema.json b/Docs/Schemas/LineRenderingConfigJson.schema.json index a410d218d..278e857e4 100644 --- a/Docs/Schemas/LineRenderingConfigJson.schema.json +++ b/Docs/Schemas/LineRenderingConfigJson.schema.json @@ -260,7 +260,28 @@ }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", - "type": "string" + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] }, "hideInAnswer": { "description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", diff --git a/Docs/Schemas/LineRenderingConfigJsonJSC.ts b/Docs/Schemas/LineRenderingConfigJsonJSC.ts index 918f1b4c8..f4a2e76d3 100644 --- a/Docs/Schemas/LineRenderingConfigJsonJSC.ts +++ b/Docs/Schemas/LineRenderingConfigJsonJSC.ts @@ -258,7 +258,28 @@ export default { }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", - "type": "string" + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] }, "hideInAnswer": { "description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", diff --git a/Docs/Schemas/PointRenderingConfigJson.schema.json b/Docs/Schemas/PointRenderingConfigJson.schema.json index 5f14ee615..715988459 100644 --- a/Docs/Schemas/PointRenderingConfigJson.schema.json +++ b/Docs/Schemas/PointRenderingConfigJson.schema.json @@ -264,7 +264,28 @@ }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", - "type": "string" + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] }, "hideInAnswer": { "description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", diff --git a/Docs/Schemas/PointRenderingConfigJsonJSC.ts b/Docs/Schemas/PointRenderingConfigJsonJSC.ts index 30827308f..1f37ef54a 100644 --- a/Docs/Schemas/PointRenderingConfigJsonJSC.ts +++ b/Docs/Schemas/PointRenderingConfigJsonJSC.ts @@ -262,7 +262,28 @@ export default { }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", - "type": "string" + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] }, "hideInAnswer": { "description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", diff --git a/Docs/Schemas/TagRenderingConfigJson.schema.json b/Docs/Schemas/TagRenderingConfigJson.schema.json index 9b74df032..1c595208d 100644 --- a/Docs/Schemas/TagRenderingConfigJson.schema.json +++ b/Docs/Schemas/TagRenderingConfigJson.schema.json @@ -100,7 +100,28 @@ }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", - "type": "string" + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] }, "hideInAnswer": { "description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", diff --git a/Docs/Schemas/TagRenderingConfigJsonJSC.ts b/Docs/Schemas/TagRenderingConfigJsonJSC.ts index 2bc849c6e..172740e9c 100644 --- a/Docs/Schemas/TagRenderingConfigJsonJSC.ts +++ b/Docs/Schemas/TagRenderingConfigJsonJSC.ts @@ -100,7 +100,28 @@ export default { }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", - "type": "string" + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] }, "hideInAnswer": { "description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", diff --git a/Docs/Schemas/TilesourceConfigJson.schema.json b/Docs/Schemas/TilesourceConfigJson.schema.json index cef3d5386..a7ae626b5 100644 --- a/Docs/Schemas/TilesourceConfigJson.schema.json +++ b/Docs/Schemas/TilesourceConfigJson.schema.json @@ -208,7 +208,28 @@ }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", - "type": "string" + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] }, "hideInAnswer": { "description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", diff --git a/Docs/Schemas/TilesourceConfigJsonJSC.ts b/Docs/Schemas/TilesourceConfigJsonJSC.ts index 15178b758..21d48703c 100644 --- a/Docs/Schemas/TilesourceConfigJsonJSC.ts +++ b/Docs/Schemas/TilesourceConfigJsonJSC.ts @@ -206,7 +206,28 @@ export default { }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", - "type": "string" + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] }, "hideInAnswer": { "description": "In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n \n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", diff --git a/Models/ThemeConfig/Conversion/FixImages.ts b/Models/ThemeConfig/Conversion/FixImages.ts index ef315fd95..ebeb0223d 100644 --- a/Models/ThemeConfig/Conversion/FixImages.ts +++ b/Models/ThemeConfig/Conversion/FixImages.ts @@ -6,9 +6,11 @@ import * as tagrenderingmetapaths from "../../../assets/tagrenderingconfigmeta.j export class ExtractImages extends Conversion { private _isOfficial: boolean; - constructor(isOfficial: boolean) { + private _sharedTagRenderings: Map; + constructor(isOfficial: boolean, sharedTagRenderings: Map) { super("Extract all images from a layoutConfig using the meta paths",[],"ExctractImages"); this._isOfficial = isOfficial; + this._sharedTagRenderings = sharedTagRenderings; } convert(json: LayoutConfigJson, context: string): { result: string[], errors: string[], warnings: string[] } { @@ -29,8 +31,19 @@ export class ExtractImages extends Conversion { const found = Utils.CollectPath(metapath.path, json) if (mightBeTr) { // We might have tagRenderingConfigs containing icons here - for (const foundImage of found) { + for (const {path, leaf} of found) { + const foundImage = leaf; if (typeof foundImage === "string") { + + if(foundImage == ""){ + errors.push(context+"."+path.join(".")+" Found an empty image") + } + + if(this._sharedTagRenderings?.has(foundImage)){ + // This is not an image, but a shared tag rendering + continue + } + allFoundImages.push(foundImage) } else{ // This is a tagRendering where every rendered value might be an icon! @@ -45,6 +58,11 @@ export class ExtractImages extends Conversion { } } allFoundImages.push(...fromPath.filter(i => typeof i === "string")) + for (const pathAndImg of fromPath) { + if(pathAndImg.leaf === "" || pathAndImg.leaf["path"] == ""){ + errors.push(context+[...path,...pathAndImg.path].join(".")+": Found an empty image at ") + } + } } } @@ -108,7 +126,7 @@ export class FixImages extends DesugaringStep { continue } const mightBeTr = Array.isArray(metapath.type) && metapath.type.some(t => t["$ref"] == "#/definitions/TagRenderingConfigJson") - Utils.WalkPath(metapath.path, json, leaf => { + Utils.WalkPath(metapath.path, json, (leaf, path) => { if (typeof leaf === "string") { return replaceString(leaf) } diff --git a/Models/ThemeConfig/Conversion/PrepareLayer.ts b/Models/ThemeConfig/Conversion/PrepareLayer.ts index 70b9815d7..88fc0276e 100644 --- a/Models/ThemeConfig/Conversion/PrepareLayer.ts +++ b/Models/ThemeConfig/Conversion/PrepareLayer.ts @@ -75,14 +75,14 @@ class ExpandTagRendering extends Conversion { new PreparePersonalTheme(state), new OnEveryConcat("layers", new SubstituteLayer(state)), new SetDefault("socialImage", "assets/SocialImage.png", true), + // We expand all tagrenderings first... new OnEvery("layers", new PrepareLayer(state)), + // Then we apply the override all new ApplyOverrideAll(), + // And then we prepare all the layers _again_ in case that an override all contained unexpanded tagrenderings! + new OnEvery("layers", new PrepareLayer(state)), new AddDefaultLayers(state), new AddDependencyLayersToTheme(state), new AddImportLayers(), diff --git a/Models/ThemeConfig/Conversion/Validation.ts b/Models/ThemeConfig/Conversion/Validation.ts index 2e87d706c..571cc7d3a 100644 --- a/Models/ThemeConfig/Conversion/Validation.ts +++ b/Models/ThemeConfig/Conversion/Validation.ts @@ -12,6 +12,7 @@ import {ExtractImages} from "./FixImages"; import ScriptUtils from "../../../scripts/ScriptUtils"; import {And} from "../../../Logic/Tags/And"; import Translations from "../../../UI/i18n/Translations"; +import Svg from "../../../Svg"; class ValidateLanguageCompleteness extends DesugaringStep { @@ -50,12 +51,14 @@ class ValidateTheme extends DesugaringStep { private readonly _path?: string; private readonly knownImagePaths: Set; private readonly _isBuiltin: boolean; + private _sharedTagRenderings: Map; - constructor(knownImagePaths: Set, path: string, isBuiltin: boolean) { + constructor(knownImagePaths: Set, path: string, isBuiltin: boolean, sharedTagRenderings: Map) { super("Doesn't change anything, but emits warnings and errors", [], "ValidateTheme"); this.knownImagePaths = knownImagePaths; this._path = path; this._isBuiltin = isBuiltin; + this._sharedTagRenderings = sharedTagRenderings; } convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[], warnings: string[], information: string[] } { @@ -78,7 +81,7 @@ class ValidateTheme extends DesugaringStep { } { // Check images: are they local, are the licenses there, is the theme icon square, ... - const images = new ExtractImages(this._isBuiltin).convertStrict(json, "validation") + const images = new ExtractImages(this._isBuiltin, this._sharedTagRenderings).convertStrict(json, "validation") const remoteImages = images.filter(img => img.indexOf("http") == 0) for (const remoteImage of remoteImages) { errors.push("Found a remote image: " + remoteImage + " in theme " + json.id + ", please download it.") @@ -93,8 +96,11 @@ class ValidateTheme extends DesugaringStep { continue } if (image.match(/[a-z]*/)) { - // This is a builtin img, e.g. 'checkmark' or 'crosshair' - continue; + + if(Svg.All[image + ".svg"] !== undefined){ + // This is a builtin img, e.g. 'checkmark' or 'crosshair' + continue; + } } if (this.knownImagePaths !== undefined && !this.knownImagePaths.has(image)) { @@ -163,10 +169,10 @@ class ValidateTheme extends DesugaringStep { } export class ValidateThemeAndLayers extends Fuse { - constructor(knownImagePaths: Set, path: string, isBuiltin: boolean) { + constructor(knownImagePaths: Set, path: string, isBuiltin: boolean, sharedTagRenderings: Map) { super("Validates a theme and the contained layers", - new ValidateTheme(knownImagePaths, path, isBuiltin), - new OnEvery("layers", new ValidateLayer(knownImagePaths, undefined, false)) + new ValidateTheme(knownImagePaths, path, isBuiltin, sharedTagRenderings), + new OnEvery("layers", new ValidateLayer(undefined, false)) ); } } @@ -302,12 +308,10 @@ export class ValidateLayer extends DesugaringStep { * @private */ private readonly _path?: string; - private readonly knownImagePaths?: Set; private readonly _isBuiltin: boolean; - constructor(knownImagePaths: Set, path: string, isBuiltin: boolean) { + constructor(path: string, isBuiltin: boolean) { super("Doesn't change anything, but emits warnings and errors", [], "ValidateLayer"); - this.knownImagePaths = knownImagePaths; this._path = path; this._isBuiltin = isBuiltin; } diff --git a/Models/ThemeConfig/LayoutConfig.ts b/Models/ThemeConfig/LayoutConfig.ts index 1875b8d99..eb22b8443 100644 --- a/Models/ThemeConfig/LayoutConfig.ts +++ b/Models/ThemeConfig/LayoutConfig.ts @@ -71,7 +71,7 @@ export default class LayoutConfig { this.credits = json.credits; this.version = json.version; this.language = json.mustHaveLanguage ?? Array.from(Object.keys(json.title)); - this.usedImages = Array.from(new ExtractImages(official).convertStrict(json, "while extracting the images of " + json.id + " " + context ?? "")).sort() + this.usedImages = Array.from(new ExtractImages(official, undefined).convertStrict(json, "while extracting the images of " + json.id + " " + context ?? "")).sort() { if (typeof json.title === "string") { throw `The title of a theme should always be a translation, as it sets the corresponding languages (${context}.title). The themenID is ${this.id}; the offending object is ${JSON.stringify(json.title)} which is a ${typeof json.title})` diff --git a/Utils.ts b/Utils.ts index cdf0cb26b..9132347f4 100644 --- a/Utils.ts +++ b/Utils.ts @@ -358,16 +358,16 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be * * The leaf objects are replaced by the function */ - public static WalkPath(path: string[], object: any, replaceLeaf: ((leaf: any) => any)) { + public static WalkPath(path: string[], object: any, replaceLeaf: ((leaf: any, travelledPath: string[]) => any), travelledPath: string[] = []) { const head = path[0] if (path.length === 1) { // We have reached the leaf const leaf = object[head]; if (leaf !== undefined) { if(Array.isArray(leaf)){ - object[head] = leaf.map(replaceLeaf) + object[head] = leaf.map(o => replaceLeaf(o, travelledPath)) }else{ - object[head] = replaceLeaf(leaf) + object[head] = replaceLeaf(leaf, travelledPath) } } return @@ -381,10 +381,10 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be return; } if (Array.isArray(sub)) { - sub.forEach(el => Utils.WalkPath(path.slice(1), el, replaceLeaf)) + sub.forEach((el, i) => Utils.WalkPath(path.slice(1), el, replaceLeaf, [...travelledPath, head, ""+i])) return; } - Utils.WalkPath(path.slice(1), sub, replaceLeaf) + Utils.WalkPath(path.slice(1), sub, replaceLeaf, [...travelledPath,head]) } /** @@ -393,7 +393,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be * * The leaf objects are collected in the list */ - public static CollectPath(path: string[], object: any, collectedList = []): any[] { + public static CollectPath(path: string[], object: any, collectedList: {leaf: any, path: string[]}[] = [], travelledPath: string[] = []): {leaf: any, path: string[]}[] { if (object === undefined || object === null) { return collectedList; } @@ -417,13 +417,13 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be } if (Array.isArray(sub)) { - sub.forEach(el => Utils.CollectPath(path.slice(1), el, collectedList)) + sub.forEach((el, i) => Utils.CollectPath(path.slice(1), el, collectedList, [...travelledPath,head,""+i])) return collectedList; } if (typeof sub !== "object") { return collectedList; } - return Utils.CollectPath(path.slice(1), sub, collectedList) + return Utils.CollectPath(path.slice(1), sub, collectedList,[...travelledPath, head]) } /** diff --git a/assets/layers/bike_repair_station/license_info.json b/assets/layers/bike_repair_station/license_info.json index 4ad68baaf..fb56c0978 100644 --- a/assets/layers/bike_repair_station/license_info.json +++ b/assets/layers/bike_repair_station/license_info.json @@ -84,6 +84,20 @@ "https://osoc.be/editions/2020/cyclofix" ] }, + { + "path": "repair_station_broken_pump.svg", + "license": "CC-BY-SA", + "authors": [ + "Pieter Fiers", + "Thibault Declercq", + "Pierre Barban", + "Joost Schouppe", + "Pieter Vander Vennet" + ], + "sources": [ + "https://osoc.be/editions/2020/cyclofix" + ] + }, { "path": "repair_station_example.jpg", "license": "CC-BY-SA 4.0", diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 38926d2d9..e7c89c5d2 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -241,7 +241,7 @@ }, "hideInAnswer": true, "icon": { - "path": "CEE7_4F.svg", + "path": "./assets/layers/charging_station/CEE7_4F.svg", "class": "medium" } }, @@ -270,7 +270,7 @@ }, "hideInAnswer": true, "icon": { - "path": "TypeE.svg", + "path": "./assets/layers/charging_station/TypeE.svg", "class": "medium" } }, @@ -325,7 +325,7 @@ }, "hideInAnswer": true, "icon": { - "path": "Chademo_type4.svg", + "path": "./assets/layers/charging_station/Chademo_type4.svg", "class": "medium" } }, @@ -380,7 +380,7 @@ }, "hideInAnswer": true, "icon": { - "path": "Type1_J1772.svg", + "path": "./assets/layers/charging_station/Type1_J1772.svg", "class": "medium" } }, @@ -435,7 +435,7 @@ }, "hideInAnswer": true, "icon": { - "path": "Type1_J1772.svg", + "path": "./assets/layers/charging_station/Type1_J1772.svg", "class": "medium" } }, @@ -490,7 +490,7 @@ }, "hideInAnswer": true, "icon": { - "path": "Type1-ccs.svg", + "path": "./assets/layers/charging_station/Type1-ccs.svg", "class": "medium" } }, @@ -545,7 +545,7 @@ }, "hideInAnswer": true, "icon": { - "path": "Tesla-hpwc-model-s.svg", + "path": "./assets/layers/charging_station/Tesla-hpwc-model-s.svg", "class": "medium" } }, @@ -600,7 +600,7 @@ }, "hideInAnswer": true, "icon": { - "path": "Type2_socket.svg", + "path": "./assets/layers/charging_station/Type2_socket.svg", "class": "medium" } }, @@ -655,7 +655,7 @@ }, "hideInAnswer": true, "icon": { - "path": "Type2_CCS.svg", + "path": "./assets/layers/charging_station/Type2_CCS.svg", "class": "medium" } }, @@ -710,7 +710,7 @@ }, "hideInAnswer": true, "icon": { - "path": "Type2_tethered.svg", + "path": "./assets/layers/charging_station/Type2_tethered.svg", "class": "medium" } }, @@ -765,7 +765,7 @@ }, "hideInAnswer": true, "icon": { - "path": "Type2_CCS.svg", + "path": "./assets/layers/charging_station/Type2_CCS.svg", "class": "medium" } }, @@ -826,7 +826,7 @@ }, "hideInAnswer": true, "icon": { - "path": "Tesla-hpwc-model-s.svg", + "path": "./assets/layers/charging_station/Tesla-hpwc-model-s.svg", "class": "medium" } }, @@ -887,7 +887,7 @@ }, "hideInAnswer": true, "icon": { - "path": "Type2_tethered.svg", + "path": "./assets/layers/charging_station/Type2_tethered.svg", "class": "medium" } }, @@ -916,7 +916,7 @@ }, "hideInAnswer": true, "icon": { - "path": "usb_port.svg", + "path": "./assets/layers/charging_station/usb_port.svg", "class": "medium" } }, @@ -967,7 +967,7 @@ }, "hideInAnswer": true, "icon": { - "path": "bosch-3pin.svg", + "path": "./assets/layers/charging_station/bosch-3pin.svg", "class": "medium" } }, @@ -1018,7 +1018,7 @@ }, "hideInAnswer": true, "icon": { - "path": "bosch-5pin.svg", + "path": "./assets/layers/charging_station/bosch-5pin.svg", "class": "medium" } } @@ -3953,7 +3953,7 @@ "operational_status=broken" ] }, - "then": "cross:#c22;" + "then": "close:#c22;" }, { "if": { diff --git a/assets/layers/charging_station/charging_station.protojson b/assets/layers/charging_station/charging_station.protojson index 72fb64d1d..35a2fc40a 100644 --- a/assets/layers/charging_station/charging_station.protojson +++ b/assets/layers/charging_station/charging_station.protojson @@ -764,7 +764,7 @@ "operational_status=broken" ] }, - "then": "cross:#c22;" + "then": "close:#c22;" }, { "if": { diff --git a/assets/layers/charging_station/csvToJson.ts b/assets/layers/charging_station/csvToJson.ts index e138243f7..ea1d55ac9 100644 --- a/assets/layers/charging_station/csvToJson.ts +++ b/assets/layers/charging_station/csvToJson.ts @@ -131,7 +131,7 @@ function run(file, protojson) { then: txt, hideInAnswer: true, icon:{ - path: e.image, + path: `./assets/layers/charging_station/${e.image}`, class:"medium" } } diff --git a/assets/layoutconfigmeta.json b/assets/layoutconfigmeta.json index 40abedbc8..c596f78b9 100644 --- a/assets/layoutconfigmeta.json +++ b/assets/layoutconfigmeta.json @@ -485,6 +485,48 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "isShown", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "isShown", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -753,6 +795,48 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "title", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "title", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -1014,6 +1098,48 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "titleIcons", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "titleIcons", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -1291,6 +1417,50 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "mapRendering", + "icon", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "mapRendering", + "icon", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -1585,6 +1755,52 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "mapRendering", + "iconBadges", + "then", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "mapRendering", + "iconBadges", + "then", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -1852,6 +2068,50 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "mapRendering", + "iconSize", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "mapRendering", + "iconSize", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -2116,6 +2376,50 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "mapRendering", + "rotation", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "mapRendering", + "rotation", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -2380,6 +2684,50 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "mapRendering", + "label", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "mapRendering", + "label", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -2651,6 +2999,50 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "mapRendering", + "color", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "mapRendering", + "color", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -2918,6 +3310,50 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "mapRendering", + "width", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "mapRendering", + "width", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -3182,6 +3618,50 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "mapRendering", + "dashArray", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "mapRendering", + "dashArray", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -3446,6 +3926,50 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "mapRendering", + "lineCap", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "mapRendering", + "lineCap", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -3714,6 +4238,50 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "mapRendering", + "fill", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "mapRendering", + "fill", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -3978,6 +4546,50 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "mapRendering", + "fillColor", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "mapRendering", + "fillColor", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -4242,6 +4854,50 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "mapRendering", + "offset", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "mapRendering", + "offset", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -4625,6 +5281,48 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "tagRenderings", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "tagRenderings", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -4871,6 +5569,50 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "layers", + "tagRenderings", + "renderings", + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "layers", + "tagRenderings", + "renderings", + "mappings", + "icon", + "class" + ], "type": "string" }, { @@ -5160,6 +5902,13 @@ ], "type": "boolean" }, + { + "path": [ + "layers", + "syncSelection" + ], + "type": "string" + }, { "path": [ "layers", diff --git a/assets/tagrenderingconfigmeta.json b/assets/tagrenderingconfigmeta.json index fd9483b82..6f7b349c1 100644 --- a/assets/tagrenderingconfigmeta.json +++ b/assets/tagrenderingconfigmeta.json @@ -138,6 +138,44 @@ "icon" ], "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "mappings", + "icon", + "class" + ], "type": "string" }, { diff --git a/assets/themes/buurtnatuur/buurtnatuur.json b/assets/themes/buurtnatuur/buurtnatuur.json index 1ef2e4f1d..991654c46 100644 --- a/assets/themes/buurtnatuur/buurtnatuur.json +++ b/assets/themes/buurtnatuur/buurtnatuur.json @@ -504,7 +504,8 @@ ] }, "then": { - "nl": "Dit gebied wordt beheerd door Natuurpunt" + "nl": "Dit gebied wordt beheerd door Natuurpunt", + "icon": "./assets/themes/buurtnatuur/Natuurpunt.jpg" } }, { @@ -514,7 +515,8 @@ ] }, "then": { - "nl": "Dit gebied wordt beheerd door {operator}" + "nl": "Dit gebied wordt beheerd door {operator}", + "icon": "./assets/themes/buurtnatuur/Natuurpunt.jpg" }, "hideInAnswer": true }, @@ -525,7 +527,8 @@ ] }, "then": { - "nl": "Dit gebied wordt beheerd door het Agentschap Natuur en Bos" + "nl": "Dit gebied wordt beheerd door het Agentschap Natuur en Bos", + "icon": "./assets/themes/buurtnatuur/ANB.jpg" } }, { diff --git a/assets/themes/campersite/campersite.json b/assets/themes/campersite/campersite.json index 59acbe7c6..bc792011e 100644 --- a/assets/themes/campersite/campersite.json +++ b/assets/themes/campersite/campersite.json @@ -42,7 +42,7 @@ "startLon": 3.14, "startZoom": 14, "widenFactor": 1.5, - "socialImage": "./assets/themes/campersite/Bar%C3%9Fel_Wohnmobilstellplatz.jpg", + "socialImage": "./assets/themes/campersite/social_image.jpg", "layers": [ { "id": "caravansites", diff --git a/assets/themes/campersite/license_info.json b/assets/themes/campersite/license_info.json index 6e12c74c2..d1b076033 100644 --- a/assets/themes/campersite/license_info.json +++ b/assets/themes/campersite/license_info.json @@ -1,14 +1,4 @@ [ - { - "path": "Barßel_Wohnmobilstellplatz.jpg", - "license": "CC-BY-SA 3.0", - "authors": [ - "ES01" - ], - "sources": [ - "https://commons.wikimedia.org/wiki/File:Bar%C3%9Fel_Wohnmobilstellplatz.jpg" - ] - }, { "path": "caravan.svg", "license": "CC0", @@ -41,5 +31,15 @@ "https://github.com/osmandapp/Osmand/blob/master/LICENSE", "https://github.com/osmandapp/OsmAnd-resources/blob/16892d8b2fc00dd422abfb2fef967d5ccd05eeac/icons/svg/poi/sanitary_dump_station.svg" ] + }, + { + "path": "social_image.jpg", + "license": "CC-BY-SA 3.0", + "authors": [ + "ES01" + ], + "sources": [ + "https://commons.wikimedia.org/wiki/File:Bar%C3%9Fel_Wohnmobilstellplatz.jpg" + ] } ] \ No newline at end of file diff --git a/assets/themes/campersite/Barßel_Wohnmobilstellplatz.jpg b/assets/themes/campersite/social_image.jpg similarity index 100% rename from assets/themes/campersite/Barßel_Wohnmobilstellplatz.jpg rename to assets/themes/campersite/social_image.jpg diff --git a/assets/themes/cyclofix/cyclofix.json b/assets/themes/cyclofix/cyclofix.json index 99ec5635b..82a94047f 100644 --- a/assets/themes/cyclofix/cyclofix.json +++ b/assets/themes/cyclofix/cyclofix.json @@ -33,7 +33,7 @@ "startLon": 0, "startZoom": 1, "widenFactor": 2, - "socialImage": "assets/themes/cyclofix/logo.svg", + "socialImage": "./assets/themes/cyclofix/logo.svg", "layers": [ "bike_cafe", "bike_shop", diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index c184f8594..28b9c62a4 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -11,14 +11,13 @@ "en": "This theme is an attempt to help automating the GRB import.", "hu": "Ez a sablon a flandriai GRB épületimportálás automatizlását kívánja megkönnyíteni." }, - "maintainer": "", + "maintainer": "Pieter Vander Vennet", "icon": "./assets/themes/grb_import/logo.svg", "version": "0", "startLat": 51.0249, "startLon": 4.026489, "startZoom": 9, "widenFactor": 2, - "socialImage": "", "clustering": { "maxZoom": 15 }, @@ -605,15 +604,14 @@ }, "iconSize": "50,50,center", "icon": { - "render": "./assets/themes/grb_import/housenumber_blank.svg", "mappings": [ { "if": "_intersects_with_other_features~*", "then": "./assets/themes/grb_import/warning.svg" }, { - "if": "addr:housenumber=", - "then": "" + "if": "addr:housenumber~*", + "then": "./assets/themes/grb_import/housenumber_blank.svg" } ] }, diff --git a/langs/themes/icon.json b/langs/themes/icon.json new file mode 100644 index 000000000..9544aaac9 --- /dev/null +++ b/langs/themes/icon.json @@ -0,0 +1,21 @@ +{ + "buurtnatuur": { + "overrideAll": { + "tagRenderings+": { + "1": { + "mappings": { + "1": { + "then": "./assets/themes/buurtnatuur/Natuurpunt.jpg" + }, + "2": { + "then": "./assets/themes/buurtnatuur/Natuurpunt.jpg" + }, + "3": { + "then": "./assets/themes/buurtnatuur/ANB.jpg" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 4e77e8d09..660702e1a 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -134,13 +134,13 @@ "1": { "mappings": { "1": { - "then": "Dit gebied wordt beheerd door Natuurpunt" + "then": "Dit gebied wordt beheerd door Natuurpunt" }, "2": { - "then": "Dit gebied wordt beheerd door {operator}" + "then": "Dit gebied wordt beheerd door {operator}" }, "3": { - "then": "Dit gebied wordt beheerd door het Agentschap Natuur en Bos" + "then": "Dit gebied wordt beheerd door het Agentschap Natuur en Bos" } }, "question": "Wie beheert dit gebied?", diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index 4ee9aa129..a14a88623 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -169,7 +169,7 @@ class LayerOverviewUtils { for (const sharedLayerJson of layerFiles) { const context = "While building builtin layer " + sharedLayerJson.path const fixed = prepLayer.convertStrict(sharedLayerJson.parsed, context) - const validator = new ValidateLayer(knownImagePaths, sharedLayerJson.path, true); + const validator = new ValidateLayer(sharedLayerJson.path, true); validator.convertStrict(fixed, context) if (sharedLayers.has(fixed.id)) { @@ -200,7 +200,10 @@ class LayerOverviewUtils { new PrevalidateTheme().convertStrict(themeFile, themePath) themeFile = new PrepareTheme(convertState).convertStrict(themeFile, themePath) - new ValidateThemeAndLayers(knownImagePaths, themePath, true) + if(knownImagePaths === undefined){ + throw "Could not load known images/licenses" + } + new ValidateThemeAndLayers(knownImagePaths, themePath, true, convertState.tagRenderings) .convertStrict(themeFile, themePath) this.writeTheme(themeFile) diff --git a/test/LegacyThemeLoader.spec.ts b/test/LegacyThemeLoader.spec.ts index 83284c5be..85a3d5b17 100644 --- a/test/LegacyThemeLoader.spec.ts +++ b/test/LegacyThemeLoader.spec.ts @@ -5,7 +5,7 @@ import {TagRenderingConfigJson} from "../Models/ThemeConfig/Json/TagRenderingCon import {AddMiniMap} from "../Models/ThemeConfig/Conversion/PrepareTheme"; import {DetectMappingsWithImages, DetectShadowedMappings} from "../Models/ThemeConfig/Conversion/Validation"; import * as Assert from "assert"; -import {FixImages} from "../Models/ThemeConfig/Conversion/FixImages"; +import {ExtractImages, FixImages} from "../Models/ThemeConfig/Conversion/FixImages"; export default class LegacyThemeLoaderSpec extends T { @@ -144,7 +144,7 @@ export default class LegacyThemeLoaderSpec extends T { ] } - private static readonly verkeerde_borden ={ + private static readonly verkeerde_borden = { "id": "https://raw.githubusercontent.com/seppesantens/MapComplete-Themes/main/VerkeerdeBordenDatabank/VerkeerdeBordenDatabank.json", "title": { "nl": "VerkeerdeBordenDatabank", @@ -242,7 +242,7 @@ export default class LegacyThemeLoaderSpec extends T { "icon": "./TS_bolt.svg", iconBadges: [{ if: "id=yes", - then:{ + then: { mappings: [ { if: "id=yes", @@ -351,7 +351,6 @@ export default class LegacyThemeLoaderSpec extends T { } - constructor() { super([ ["Walking_node_theme", () => { @@ -423,9 +422,9 @@ export default class LegacyThemeLoaderSpec extends T { } ] }, "test"); - T.isTrue(r.errors.length > 0, "Failing case 0 is not detected") + T.isTrue(r.errors.length > 0, "Failing case 0 is not detected") - const r0 = new DetectShadowedMappings().convert( { + const r0 = new DetectShadowedMappings().convert({ mappings: [ { if: {or: ["key=value", "x=y"]}, @@ -440,34 +439,71 @@ export default class LegacyThemeLoaderSpec extends T { T.isTrue(r0.errors.length > 0, "Failing case 1 is not detected") } ], - ["Images are rewritten", () => { - const fixed = new FixImages(new Set()).convertStrict(LegacyThemeLoaderSpec.verkeerde_borden, "test") - const fixedValue = fixed.layers[0]["mapRendering"][0].icon - Assert.equal("https://raw.githubusercontent.com/seppesantens/MapComplete-Themes/main/VerkeerdeBordenDatabank/TS_bolt.svg", - fixedValue) + ["Images are rewritten", () => { + const fixed = new FixImages(new Set()).convertStrict(LegacyThemeLoaderSpec.verkeerde_borden, "test") + const fixedValue = fixed.layers[0]["mapRendering"][0].icon + Assert.equal("https://raw.githubusercontent.com/seppesantens/MapComplete-Themes/main/VerkeerdeBordenDatabank/TS_bolt.svg", + fixedValue) - const fixedMapping = fixed.layers[0]["mapRendering"][0].iconBadges[0].then.mappings[0].then - Assert.equal("https://raw.githubusercontent.com/seppesantens/MapComplete-Themes/main/VerkeerdeBordenDatabank/Something.svg", - fixedMapping) - } ], - ["Images in 'thens' are detected", () => { - const r = new DetectMappingsWithImages().convert({ + const fixedMapping = fixed.layers[0]["mapRendering"][0].iconBadges[0].then.mappings[0].then + Assert.equal("https://raw.githubusercontent.com/seppesantens/MapComplete-Themes/main/VerkeerdeBordenDatabank/Something.svg", + fixedMapping) + }], + ["Images in 'thens' are detected", () => { + const r = new DetectMappingsWithImages().convert({ "mappings": [ - { - "if": "bicycle_parking=stands", - "then": { - "en": "Staple racks ", - "nl": "Nietjes ", - "fr": "Arceaux ", - "gl": "De roda (Stands) ", - "de": "Fahrradbügel ", - "hu": "Korlát ", - "it": "Archetti ", - "zh_Hant": "單車架 " - } - }]}, "test"); - T.isTrue(r.warnings.length > 0, "No images found"); - }] + { + "if": "bicycle_parking=stands", + "then": { + "en": "Staple racks ", + "nl": "Nietjes ", + "fr": "Arceaux ", + "gl": "De roda (Stands) ", + "de": "Fahrradbügel ", + "hu": "Korlát ", + "it": "Archetti ", + "zh_Hant": "單車架 " + } + }] + }, "test"); + T.isTrue(r.warnings.length > 0, "No images found"); + T.isTrue(r.warnings.some(msg => msg.indexOf("./assets/layers/bike_parking/staple.svg") >= 0), "staple.svg not mentioned"); + }], + ["Images in 'thens' icons are detected", () => { + const r = new ExtractImages(true).convert({ + "layers": [ + { + tagRenderings: [ + { + "mappings": [ + { + "if": "bicycle_parking=stands", + "then": { + "en": "Staple racks", + }, + "icon": { + path: "./assets/layers/bike_parking/staple.svg", + class: "small" + } + }, + { + "if": "bicycle_parking=stands", + "then": { + "en": "Bollard", + }, + "icon": "./assets/layers/bike_parking/bollard.svg", + } + ] + } + ] + } + ] + }, "test"); + const images = r.result + T.isTrue(images.length > 0, "No images found"); + T.isTrue(images.findIndex(img => img =="./assets/layers/bike_parking/staple.svg") >= 0, "staple.svg not mentioned"); + T.isTrue(images.findIndex(img => img == "./assets/layers/bike_parking/bollard.svg") >= 0, "bollard.svg not mentioned"); + }] ] ); } From d9d6e96fed90026aa40eef61c008226c6ffc1eed Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 18 Feb 2022 23:12:46 +0100 Subject: [PATCH 23/43] Extract images from crossings.json --- assets/layers/crossings/crossings.json | 24 ++++++++++++++++-------- langs/layers/de.json | 4 ++-- langs/layers/en.json | 4 ++-- langs/layers/nl.json | 4 ++-- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/assets/layers/crossings/crossings.json b/assets/layers/crossings/crossings.json index 2c1fb4b6a..dd6a4f7b5 100644 --- a/assets/layers/crossings/crossings.json +++ b/assets/layers/crossings/crossings.json @@ -306,11 +306,15 @@ { "if": "red_turn:right:bicycle=yes", "then": { - "en": "A cyclist can turn right if the light is red ", - "nl": "Een fietser mag wel rechtsaf slaan als het licht rood is ", - "de": "Ein Radfahrer kann bei roter Ampel rechts abbiegen " + "en": "A cyclist can turn right if the light is red", + "nl": "Een fietser mag wel rechtsaf slaan als het licht rood is", + "de": "Ein Radfahrer kann bei roter Ampel rechts abbiegen" }, - "hideInAnswer": "_country!=be" + "hideInAnswer": "_country!=be", + "icon": { + "path": "./assets/layers/crossings/Belgian_road_sign_B22.svg", + "class": "medium" + } }, { "if": "red_turn:right:bicycle=yes", @@ -343,11 +347,15 @@ { "if": "red_turn:straight:bicycle=yes", "then": { - "en": "A cyclist can go straight on if the light is red ", - "nl": "Een fietser mag wel rechtdoor gaan als het licht rood is ", - "de": "Ein Radfahrer kann bei roter Ampel geradeaus fahren " + "en": "A cyclist can go straight on if the light is red", + "nl": "Een fietser mag wel rechtdoor gaan als het licht rood is", + "de": "Ein Radfahrer kann bei roter Ampel geradeaus fahren" }, - "hideInAnswer": "_country!=be" + "hideInAnswer": "_country!=be", + "icon": { + "path": "./assets/layers/crossings/Belgian_road_sign_B23.svg", + "class": "medium" + } }, { "if": "red_turn:straight:bicycle=yes", diff --git a/langs/layers/de.json b/langs/layers/de.json index 38e3accf2..e3182578f 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -1242,7 +1242,7 @@ "crossing-continue-through-red": { "mappings": { "0": { - "then": "Ein Radfahrer kann bei roter Ampel geradeaus fahren " + "then": "Ein Radfahrer kann bei roter Ampel geradeaus fahren" }, "1": { "then": "Ein Radfahrer kann bei roter Ampel geradeaus fahren" @@ -1278,7 +1278,7 @@ "crossing-right-turn-through-red": { "mappings": { "0": { - "then": "Ein Radfahrer kann bei roter Ampel rechts abbiegen " + "then": "Ein Radfahrer kann bei roter Ampel rechts abbiegen" }, "1": { "then": "Ein Radfahrer kann bei roter Ampel rechts abbiegen" diff --git a/langs/layers/en.json b/langs/layers/en.json index 1fbb0a54e..e7b8d1d14 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -2305,7 +2305,7 @@ "crossing-continue-through-red": { "mappings": { "0": { - "then": "A cyclist can go straight on if the light is red " + "then": "A cyclist can go straight on if the light is red" }, "1": { "then": "A cyclist can go straight on if the light is red" @@ -2341,7 +2341,7 @@ "crossing-right-turn-through-red": { "mappings": { "0": { - "then": "A cyclist can turn right if the light is red " + "then": "A cyclist can turn right if the light is red" }, "1": { "then": "A cyclist can turn right if the light is red" diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 4ec425d43..7c2c17a36 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -2321,7 +2321,7 @@ "crossing-continue-through-red": { "mappings": { "0": { - "then": "Een fietser mag wel rechtdoor gaan als het licht rood is " + "then": "Een fietser mag wel rechtdoor gaan als het licht rood is" }, "1": { "then": "Een fietser mag wel rechtdoor gaan als het licht rood is" @@ -2357,7 +2357,7 @@ "crossing-right-turn-through-red": { "mappings": { "0": { - "then": "Een fietser mag wel rechtsaf slaan als het licht rood is " + "then": "Een fietser mag wel rechtsaf slaan als het licht rood is" }, "1": { "then": "Een fietser mag wel rechtsaf slaan als het licht rood is" From 04a2cc9bc796b99cda2c9ec8b1a90f5429c0ed58 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 19 Feb 2022 02:45:15 +0100 Subject: [PATCH 24/43] Fix tests --- Docs/Schemas/LayerConfigJson.schema.json | 2 +- Docs/Schemas/LayerConfigJsonJSC.ts | 2 +- Docs/Schemas/LayoutConfigJson.schema.json | 2 +- Docs/Schemas/LayoutConfigJsonJSC.ts | 2 +- .../LineRenderingConfigJson.schema.json | 2 +- Docs/Schemas/LineRenderingConfigJsonJSC.ts | 2 +- .../PointRenderingConfigJson.schema.json | 2 +- Docs/Schemas/PointRenderingConfigJsonJSC.ts | 2 +- .../TagRenderingConfigJson.schema.json | 2 +- Docs/Schemas/TagRenderingConfigJsonJSC.ts | 2 +- Docs/Schemas/TilesourceConfigJson.schema.json | 2 +- Docs/Schemas/TilesourceConfigJsonJSC.ts | 2 +- Models/ThemeConfig/Conversion/FixImages.ts | 36 ++++----- .../Json/TagRenderingConfigJson.ts | 2 +- Utils.ts | 74 ++++++++++--------- test/ImageAttribution.spec.ts | 2 +- test/LegacyThemeLoader.spec.ts | 2 +- test/TestAll.ts | 3 +- 18 files changed, 71 insertions(+), 72 deletions(-) diff --git a/Docs/Schemas/LayerConfigJson.schema.json b/Docs/Schemas/LayerConfigJson.schema.json index 9e72ff1a3..72a8a69e0 100644 --- a/Docs/Schemas/LayerConfigJson.schema.json +++ b/Docs/Schemas/LayerConfigJson.schema.json @@ -612,7 +612,7 @@ ] }, "then": { - "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\ntype: rendered" + "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered" }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", diff --git a/Docs/Schemas/LayerConfigJsonJSC.ts b/Docs/Schemas/LayerConfigJsonJSC.ts index 0348fcb91..481e12736 100644 --- a/Docs/Schemas/LayerConfigJsonJSC.ts +++ b/Docs/Schemas/LayerConfigJsonJSC.ts @@ -610,7 +610,7 @@ export default { ] }, "then": { - "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\ntype: rendered" + "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered" }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", diff --git a/Docs/Schemas/LayoutConfigJson.schema.json b/Docs/Schemas/LayoutConfigJson.schema.json index 3321c1eed..362a59e45 100644 --- a/Docs/Schemas/LayoutConfigJson.schema.json +++ b/Docs/Schemas/LayoutConfigJson.schema.json @@ -456,7 +456,7 @@ ] }, "then": { - "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\ntype: rendered" + "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered" }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", diff --git a/Docs/Schemas/LayoutConfigJsonJSC.ts b/Docs/Schemas/LayoutConfigJsonJSC.ts index 49ca6c05e..92e8e24fb 100644 --- a/Docs/Schemas/LayoutConfigJsonJSC.ts +++ b/Docs/Schemas/LayoutConfigJsonJSC.ts @@ -454,7 +454,7 @@ export default { ] }, "then": { - "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\ntype: rendered" + "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered" }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", diff --git a/Docs/Schemas/LineRenderingConfigJson.schema.json b/Docs/Schemas/LineRenderingConfigJson.schema.json index 278e857e4..2b8cac100 100644 --- a/Docs/Schemas/LineRenderingConfigJson.schema.json +++ b/Docs/Schemas/LineRenderingConfigJson.schema.json @@ -256,7 +256,7 @@ ] }, "then": { - "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\ntype: rendered" + "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered" }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", diff --git a/Docs/Schemas/LineRenderingConfigJsonJSC.ts b/Docs/Schemas/LineRenderingConfigJsonJSC.ts index f4a2e76d3..b0037110a 100644 --- a/Docs/Schemas/LineRenderingConfigJsonJSC.ts +++ b/Docs/Schemas/LineRenderingConfigJsonJSC.ts @@ -254,7 +254,7 @@ export default { ] }, "then": { - "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\ntype: rendered" + "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered" }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", diff --git a/Docs/Schemas/PointRenderingConfigJson.schema.json b/Docs/Schemas/PointRenderingConfigJson.schema.json index 715988459..89d2c3c41 100644 --- a/Docs/Schemas/PointRenderingConfigJson.schema.json +++ b/Docs/Schemas/PointRenderingConfigJson.schema.json @@ -260,7 +260,7 @@ ] }, "then": { - "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\ntype: rendered" + "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered" }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", diff --git a/Docs/Schemas/PointRenderingConfigJsonJSC.ts b/Docs/Schemas/PointRenderingConfigJsonJSC.ts index 1f37ef54a..599e6c5c1 100644 --- a/Docs/Schemas/PointRenderingConfigJsonJSC.ts +++ b/Docs/Schemas/PointRenderingConfigJsonJSC.ts @@ -258,7 +258,7 @@ export default { ] }, "then": { - "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\ntype: rendered" + "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered" }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", diff --git a/Docs/Schemas/TagRenderingConfigJson.schema.json b/Docs/Schemas/TagRenderingConfigJson.schema.json index 1c595208d..21ee45e1e 100644 --- a/Docs/Schemas/TagRenderingConfigJson.schema.json +++ b/Docs/Schemas/TagRenderingConfigJson.schema.json @@ -96,7 +96,7 @@ ] }, "then": { - "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\ntype: rendered" + "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered" }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", diff --git a/Docs/Schemas/TagRenderingConfigJsonJSC.ts b/Docs/Schemas/TagRenderingConfigJsonJSC.ts index 172740e9c..cf2358570 100644 --- a/Docs/Schemas/TagRenderingConfigJsonJSC.ts +++ b/Docs/Schemas/TagRenderingConfigJsonJSC.ts @@ -96,7 +96,7 @@ export default { ] }, "then": { - "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\ntype: rendered" + "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered" }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", diff --git a/Docs/Schemas/TilesourceConfigJson.schema.json b/Docs/Schemas/TilesourceConfigJson.schema.json index a7ae626b5..ebac53292 100644 --- a/Docs/Schemas/TilesourceConfigJson.schema.json +++ b/Docs/Schemas/TilesourceConfigJson.schema.json @@ -204,7 +204,7 @@ ] }, "then": { - "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\ntype: rendered" + "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered" }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", diff --git a/Docs/Schemas/TilesourceConfigJsonJSC.ts b/Docs/Schemas/TilesourceConfigJsonJSC.ts index 21d48703c..bc487ece2 100644 --- a/Docs/Schemas/TilesourceConfigJsonJSC.ts +++ b/Docs/Schemas/TilesourceConfigJsonJSC.ts @@ -202,7 +202,7 @@ export default { ] }, "then": { - "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\ntype: rendered" + "description": "If the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered" }, "icon": { "description": "An icon supporting this mapping; typically shown pretty small\nType: icon", diff --git a/Models/ThemeConfig/Conversion/FixImages.ts b/Models/ThemeConfig/Conversion/FixImages.ts index ebeb0223d..08318b4bc 100644 --- a/Models/ThemeConfig/Conversion/FixImages.ts +++ b/Models/ThemeConfig/Conversion/FixImages.ts @@ -7,6 +7,11 @@ import * as tagrenderingmetapaths from "../../../assets/tagrenderingconfigmeta.j export class ExtractImages extends Conversion { private _isOfficial: boolean; private _sharedTagRenderings: Map; + + private static readonly layoutMetaPaths = (metapaths["default"] ?? metapaths).filter(mp => mp.typeHint !== undefined && (mp.typeHint === "image" || mp.typeHint === "icon")) + private static readonly tagRenderingMetaPaths = (tagrenderingmetapaths["default"] ?? tagrenderingmetapaths).filter(trpath => trpath.typeHint !== "rendered") + + constructor(isOfficial: boolean, sharedTagRenderings: Map) { super("Extract all images from a layoutConfig using the meta paths",[],"ExctractImages"); this._isOfficial = isOfficial; @@ -14,25 +19,17 @@ export class ExtractImages extends Conversion { } convert(json: LayoutConfigJson, context: string): { result: string[], errors: string[], warnings: string[] } { - const paths = metapaths["default"] ?? metapaths - const trpaths = tagrenderingmetapaths["default"] ?? tagrenderingmetapaths - const allFoundImages = [] + const allFoundImages = [] const errors = [] const warnings = [] - for (const metapath of paths) { - if (metapath.typeHint === undefined) { - continue - } - if (metapath.typeHint !== "image" && metapath.typeHint !== "icon") { - continue - } - + for (const metapath of ExtractImages.layoutMetaPaths) { const mightBeTr = Array.isArray(metapath.type) && metapath.type.some(t => t["$ref"] == "#/definitions/TagRenderingConfigJson") const found = Utils.CollectPath(metapath.path, json) if (mightBeTr) { // We might have tagRenderingConfigs containing icons here - for (const {path, leaf} of found) { - const foundImage = leaf; + for (const el of found) { + const path = el.path + const foundImage = el.leaf; if (typeof foundImage === "string") { if(foundImage == ""){ @@ -47,17 +44,14 @@ export class ExtractImages extends Conversion { allFoundImages.push(foundImage) } else{ // This is a tagRendering where every rendered value might be an icon! - for (const trpath of trpaths) { - if (trpath.typeHint !== "rendered") { - continue - } + for (const trpath of ExtractImages.tagRenderingMetaPaths) { const fromPath = Utils.CollectPath(trpath.path, foundImage) for (const img of fromPath) { - if (typeof img !== "string") { - (this._isOfficial ? errors: warnings).push(context+": found an image path that is not a path at " + context + "." + metapath.path.join(".") + ": " + JSON.stringify(img)) + if (typeof img.leaf !== "string") { + (this._isOfficial ? errors: warnings).push(context+": found an image path that is not a path at " + context + "." + img.path.join(".") + ": " + JSON.stringify(img)) } } - allFoundImages.push(...fromPath.filter(i => typeof i === "string")) + allFoundImages.push(...fromPath.map(i => i.leaf).filter(i => typeof i=== "string")) for (const pathAndImg of fromPath) { if(pathAndImg.leaf === "" || pathAndImg.leaf["path"] == ""){ errors.push(context+[...path,...pathAndImg.path].join(".")+": Found an empty image at ") @@ -68,7 +62,7 @@ export class ExtractImages extends Conversion { } } } else { - allFoundImages.push(...found) + allFoundImages.push(...found.map(i => i.leaf)) } } diff --git a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts index 36ccbf3cc..0b08218b5 100644 --- a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts @@ -115,7 +115,7 @@ export interface TagRenderingConfigJson { /** * If the condition `if` is met, the text `then` will be rendered. * If not known yet, the user will be presented with `then` as an option - * type: rendered + * Type: rendered */ then: string | any, /** diff --git a/Utils.ts b/Utils.ts index 9132347f4..41e4d141e 100644 --- a/Utils.ts +++ b/Utils.ts @@ -304,7 +304,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be if (target === null) { return source } - + for (const key in source) { if (!source.hasOwnProperty(key)) { continue @@ -364,9 +364,9 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be // We have reached the leaf const leaf = object[head]; if (leaf !== undefined) { - if(Array.isArray(leaf)){ + if (Array.isArray(leaf)) { object[head] = leaf.map(o => replaceLeaf(o, travelledPath)) - }else{ + } else { object[head] = replaceLeaf(leaf, travelledPath) } } @@ -381,10 +381,10 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be return; } if (Array.isArray(sub)) { - sub.forEach((el, i) => Utils.WalkPath(path.slice(1), el, replaceLeaf, [...travelledPath, head, ""+i])) + sub.forEach((el, i) => Utils.WalkPath(path.slice(1), el, replaceLeaf, [...travelledPath, head, "" + i])) return; } - Utils.WalkPath(path.slice(1), sub, replaceLeaf, [...travelledPath,head]) + Utils.WalkPath(path.slice(1), sub, replaceLeaf, [...travelledPath, head]) } /** @@ -393,22 +393,26 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be * * The leaf objects are collected in the list */ - public static CollectPath(path: string[], object: any, collectedList: {leaf: any, path: string[]}[] = [], travelledPath: string[] = []): {leaf: any, path: string[]}[] { + public static CollectPath(path: string[], object: any, collectedList: { leaf: any, path: string[] }[] = [], travelledPath: string[] = []): { leaf: any, path: string[] }[] { if (object === undefined || object === null) { return collectedList; } const head = path[0] + travelledPath = [...travelledPath, head] if (path.length === 1) { // We have reached the leaf const leaf = object[head]; if (leaf === undefined || leaf === null) { return collectedList - } - if (Array.isArray(leaf)) { - collectedList.push(...leaf) - } else { - collectedList.push(leaf) + } + if (Array.isArray(leaf)) { + for (let i = 0; i < (leaf).length; i++){ + const l = (leaf)[i]; + collectedList.push({leaf: l, path: [...travelledPath, ""+i]}) } + } else { + collectedList.push({leaf, path: travelledPath}) + } return collectedList } const sub = object[head] @@ -417,13 +421,13 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be } if (Array.isArray(sub)) { - sub.forEach((el, i) => Utils.CollectPath(path.slice(1), el, collectedList, [...travelledPath,head,""+i])) + sub.forEach((el, i) => Utils.CollectPath(path.slice(1), el, collectedList, [...travelledPath, "" + i])) return collectedList; } if (typeof sub !== "object") { return collectedList; } - return Utils.CollectPath(path.slice(1), sub, collectedList,[...travelledPath, head]) + return Utils.CollectPath(path.slice(1), sub, collectedList, travelledPath) } /** @@ -725,6 +729,28 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be return new Date(str) } + public static levenshteinDistance(str1: string, str2: string) { + const track = Array(str2.length + 1).fill(null).map(() => + Array(str1.length + 1).fill(null)); + for (let i = 0; i <= str1.length; i += 1) { + track[0][i] = i; + } + for (let j = 0; j <= str2.length; j += 1) { + track[j][0] = j; + } + for (let j = 1; j <= str2.length; j += 1) { + for (let i = 1; i <= str1.length; i += 1) { + const indicator = str1[i - 1] === str2[j - 1] ? 0 : 1; + track[j][i] = Math.min( + track[j][i - 1] + 1, // deletion + track[j - 1][i] + 1, // insertion + track[j - 1][i - 1] + indicator, // substitution + ); + } + } + return track[str2.length][str1.length]; + } + private static colorDiff(c0: { r: number, g: number, b: number }, c1: { r: number, g: number, b: number }) { return Math.abs(c0.r - c1.r) + Math.abs(c0.g - c1.g) + Math.abs(c0.b - c1.b); } @@ -751,27 +777,5 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be b: parseInt(hex.substr(5, 2), 16), } } - - public static levenshteinDistance (str1: string, str2: string) { - const track = Array(str2.length + 1).fill(null).map(() => - Array(str1.length + 1).fill(null)); - for (let i = 0; i <= str1.length; i += 1) { - track[0][i] = i; - } - for (let j = 0; j <= str2.length; j += 1) { - track[j][0] = j; - } - for (let j = 1; j <= str2.length; j += 1) { - for (let i = 1; i <= str1.length; i += 1) { - const indicator = str1[i - 1] === str2[j - 1] ? 0 : 1; - track[j][i] = Math.min( - track[j][i - 1] + 1, // deletion - track[j - 1][i] + 1, // insertion - track[j - 1][i - 1] + indicator, // substitution - ); - } - } - return track[str2.length][str1.length]; - } } diff --git a/test/ImageAttribution.spec.ts b/test/ImageAttribution.spec.ts index 10883105a..86b394a79 100644 --- a/test/ImageAttribution.spec.ts +++ b/test/ImageAttribution.spec.ts @@ -10,7 +10,7 @@ export default class ImageAttributionSpec extends T { [ "Should find all the images", () => { - const images = new Set(new ExtractImages(true).convertStrict( cyclofix, "test")) + const images = new Set(new ExtractImages(true, new Map()).convertStrict( cyclofix, "test")) const expectedValues = [ './assets/layers/bike_repair_station/repair_station.svg', './assets/layers/bike_repair_station/repair_station_pump.svg', diff --git a/test/LegacyThemeLoader.spec.ts b/test/LegacyThemeLoader.spec.ts index 85a3d5b17..d310887f7 100644 --- a/test/LegacyThemeLoader.spec.ts +++ b/test/LegacyThemeLoader.spec.ts @@ -470,7 +470,7 @@ export default class LegacyThemeLoaderSpec extends T { T.isTrue(r.warnings.some(msg => msg.indexOf("./assets/layers/bike_parking/staple.svg") >= 0), "staple.svg not mentioned"); }], ["Images in 'thens' icons are detected", () => { - const r = new ExtractImages(true).convert({ + const r = new ExtractImages(true, new Map()).convert({ "layers": [ { tagRenderings: [ diff --git a/test/TestAll.ts b/test/TestAll.ts index de9211864..5d43853cc 100644 --- a/test/TestAll.ts +++ b/test/TestAll.ts @@ -59,7 +59,7 @@ async function main() { let args = [...process.argv] args.splice(0, 2) - args = args.map(a => a.toLowerCase()) + args = args.map(a => a.toLowerCase().replace(/"/g, "")) const allFailures: { testsuite: string, name: string, msg: string } [] = [] let testsToRun = allTests @@ -72,6 +72,7 @@ async function main() { } }) testsToRun = allTests.filter(t => args.indexOf(t.name.toLowerCase()) >= 0) + console.log("Only running test "+testsToRun.join(", ")) } if (testsToRun.length == 0) { From dcfac434e2a150d38a46e53ebc8f0d738a9aebb4 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 19 Feb 2022 02:50:54 +0100 Subject: [PATCH 25/43] Fix filter --- Models/ThemeConfig/Conversion/FixImages.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Models/ThemeConfig/Conversion/FixImages.ts b/Models/ThemeConfig/Conversion/FixImages.ts index 08318b4bc..9558e2f6e 100644 --- a/Models/ThemeConfig/Conversion/FixImages.ts +++ b/Models/ThemeConfig/Conversion/FixImages.ts @@ -9,7 +9,7 @@ export class ExtractImages extends Conversion { private _sharedTagRenderings: Map; private static readonly layoutMetaPaths = (metapaths["default"] ?? metapaths).filter(mp => mp.typeHint !== undefined && (mp.typeHint === "image" || mp.typeHint === "icon")) - private static readonly tagRenderingMetaPaths = (tagrenderingmetapaths["default"] ?? tagrenderingmetapaths).filter(trpath => trpath.typeHint !== "rendered") + private static readonly tagRenderingMetaPaths = (tagrenderingmetapaths["default"] ?? tagrenderingmetapaths).filter(trpath => trpath.typeHint === "rendered") constructor(isOfficial: boolean, sharedTagRenderings: Map) { @@ -48,7 +48,7 @@ export class ExtractImages extends Conversion { const fromPath = Utils.CollectPath(trpath.path, foundImage) for (const img of fromPath) { if (typeof img.leaf !== "string") { - (this._isOfficial ? errors: warnings).push(context+": found an image path that is not a path at " + context + "." + img.path.join(".") + ": " + JSON.stringify(img)) + (this._isOfficial ? errors: warnings).push(context+"."+img.path.join(".")+": found an image path that is not a string: " + JSON.stringify(img.leaf)) } } allFoundImages.push(...fromPath.map(i => i.leaf).filter(i => typeof i=== "string")) From 55f9ee605f78b788680e207ab8c02b3b035ce918 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 19 Feb 2022 17:39:16 +0100 Subject: [PATCH 26/43] More checks --- Models/ThemeConfig/Conversion/FixImages.ts | 16 ++++++++++++---- Models/ThemeConfig/Conversion/Validation.ts | 20 +++++++++++++++++++- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Models/ThemeConfig/Conversion/FixImages.ts b/Models/ThemeConfig/Conversion/FixImages.ts index 9558e2f6e..0b8bb1de0 100644 --- a/Models/ThemeConfig/Conversion/FixImages.ts +++ b/Models/ThemeConfig/Conversion/FixImages.ts @@ -19,7 +19,7 @@ export class ExtractImages extends Conversion { } convert(json: LayoutConfigJson, context: string): { result: string[], errors: string[], warnings: string[] } { - const allFoundImages = [] + const allFoundImages : string[] = [] const errors = [] const warnings = [] for (const metapath of ExtractImages.layoutMetaPaths) { @@ -33,7 +33,7 @@ export class ExtractImages extends Conversion { if (typeof foundImage === "string") { if(foundImage == ""){ - errors.push(context+"."+path.join(".")+" Found an empty image") + warnings.push(context+"."+path.join(".")+" Found an empty image") } if(this._sharedTagRenderings?.has(foundImage)){ @@ -54,7 +54,7 @@ export class ExtractImages extends Conversion { allFoundImages.push(...fromPath.map(i => i.leaf).filter(i => typeof i=== "string")) for (const pathAndImg of fromPath) { if(pathAndImg.leaf === "" || pathAndImg.leaf["path"] == ""){ - errors.push(context+[...path,...pathAndImg.path].join(".")+": Found an empty image at ") + warnings.push(context+[...path,...pathAndImg.path].join(".")+": Found an empty image at ") } } } @@ -62,7 +62,14 @@ export class ExtractImages extends Conversion { } } } else { - allFoundImages.push(...found.map(i => i.leaf)) + for (const foundElement of found) { + if(foundElement.leaf === ""){ + warnings.push(context+"."+foundElement.path.join(".")+" Found an empty image") + continue + } + allFoundImages.push(foundElement.leaf) + } + } } @@ -70,6 +77,7 @@ export class ExtractImages extends Conversion { .map(img => img["path"] ?? img) .map(img => img.split(";"))) .map(img => img.split(":")[0]) + .filter(img => img !== "") return {result: Utils.Dedup(splitParts), errors, warnings}; } diff --git a/Models/ThemeConfig/Conversion/Validation.ts b/Models/ThemeConfig/Conversion/Validation.ts index 571cc7d3a..bcdb3902d 100644 --- a/Models/ThemeConfig/Conversion/Validation.ts +++ b/Models/ThemeConfig/Conversion/Validation.ts @@ -208,11 +208,29 @@ class OverrideShadowingCheck extends DesugaringStep { } +class MiscThemeChecks extends DesugaringStep{ + constructor() { + super("Miscelleanous checks on the theme", [],"MiscThemesChecks"); + } + + convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } { + const warnings = [] + if(json.socialImage === ""){ + warnings.push("Social image for theme "+json.id+" is the emtpy string") + } + return { + result :json, + warnings + }; + } +} + export class PrevalidateTheme extends Fuse { constructor() { super("Various consistency checks on the raw JSON", - new OverrideShadowingCheck() + new OverrideShadowingCheck(), + new MiscThemeChecks() ); } From 2e586cedc4a8ab32da0753160625233cd13430c7 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 19 Feb 2022 17:40:51 +0100 Subject: [PATCH 27/43] Remove empty social images --- Models/ThemeConfig/Conversion/LegacyJsonConvert.ts | 5 +++++ assets/themes/benches/benches.json | 1 - assets/themes/bicycle_rental/bicycle_rental.json | 1 - assets/themes/binoculars/binoculars.json | 1 - assets/themes/cafes_and_pubs/cafes_and_pubs.json | 1 - assets/themes/charging_stations/charging_stations.json | 1 - assets/themes/climbing/climbing.json | 1 - assets/themes/etymology.json | 1 - assets/themes/facadegardens/facadegardens.json | 1 - assets/themes/food/food.json | 1 - assets/themes/fritures/fritures.json | 1 - assets/themes/fruit_trees/fruit_trees.json | 1 - assets/themes/grb_import/grb_fixme.json | 1 - assets/themes/grb_import/missing_streets.json | 1 - assets/themes/hackerspaces/hackerspaces.json | 1 - assets/themes/hailhydrant/hailhydrant.json | 1 - assets/themes/maps/maps.json | 1 - assets/themes/nature/nature.json | 1 - assets/themes/natuurpunt/natuurpunt.json | 1 - assets/themes/observation_towers/observation_towers.json | 1 - assets/themes/play_forests/play_forests.json | 1 - assets/themes/playgrounds/playgrounds.json | 1 - assets/themes/postal_codes/postal_codes.json | 1 - assets/themes/shops/shops.json | 1 - assets/themes/sidewalks/sidewalks.json | 1 - assets/themes/sport_pitches/sport_pitches.json | 1 - assets/themes/surveillance/surveillance.json | 1 - assets/themes/uk_addresses/uk_addresses.json | 1 - assets/themes/waste_basket/waste_basket.json | 1 - 29 files changed, 5 insertions(+), 28 deletions(-) diff --git a/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts b/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts index f5a5294fb..c1bbcec37 100644 --- a/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts +++ b/Models/ThemeConfig/Conversion/LegacyJsonConvert.ts @@ -126,6 +126,11 @@ class UpdateLegacyTheme extends DesugaringStep { convert(json: LayoutConfigJson, context: string): { result: LayoutConfigJson; errors: string[]; warnings: string[] } { const oldThemeConfig = {...json} + + if(oldThemeConfig.socialImage === ""){ + delete oldThemeConfig.socialImage + } + if (oldThemeConfig["roamingRenderings"] !== undefined) { if (oldThemeConfig["roamingRenderings"].length == 0) { diff --git a/assets/themes/benches/benches.json b/assets/themes/benches/benches.json index 193b38d40..7ed0d305e 100644 --- a/assets/themes/benches/benches.json +++ b/assets/themes/benches/benches.json @@ -45,7 +45,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 1.5, - "socialImage": "", "layers": [ "bench", "bench_at_pt", diff --git a/assets/themes/bicycle_rental/bicycle_rental.json b/assets/themes/bicycle_rental/bicycle_rental.json index 9f30efca3..b26d1bf16 100644 --- a/assets/themes/bicycle_rental/bicycle_rental.json +++ b/assets/themes/bicycle_rental/bicycle_rental.json @@ -19,7 +19,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 0.05, - "socialImage": "", "layers": [ "bicycle_rental" ] diff --git a/assets/themes/binoculars/binoculars.json b/assets/themes/binoculars/binoculars.json index 1e77b49c6..fed6bcb98 100644 --- a/assets/themes/binoculars/binoculars.json +++ b/assets/themes/binoculars/binoculars.json @@ -33,7 +33,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 1.5, - "socialImage": "", "layers": [ "binocular" ] diff --git a/assets/themes/cafes_and_pubs/cafes_and_pubs.json b/assets/themes/cafes_and_pubs/cafes_and_pubs.json index 9ab64a6c9..92256e06a 100644 --- a/assets/themes/cafes_and_pubs/cafes_and_pubs.json +++ b/assets/themes/cafes_and_pubs/cafes_and_pubs.json @@ -22,7 +22,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 1.5, - "socialImage": "", "layers": [ "cafe_pub" ] diff --git a/assets/themes/charging_stations/charging_stations.json b/assets/themes/charging_stations/charging_stations.json index 2b6fc04d2..6c0dbdc86 100644 --- a/assets/themes/charging_stations/charging_stations.json +++ b/assets/themes/charging_stations/charging_stations.json @@ -39,7 +39,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 1.5, - "socialImage": "", "defaultBackgroundId": "CartoDB.Voyager", "layers": [ "charging_station" diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json index fc948705d..e93273ebf 100644 --- a/assets/themes/climbing/climbing.json +++ b/assets/themes/climbing/climbing.json @@ -41,7 +41,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 1.5, - "socialImage": "", "layers": [ { "id": "climbing_club", diff --git a/assets/themes/etymology.json b/assets/themes/etymology.json index bd76bf8a3..1ed79cf96 100644 --- a/assets/themes/etymology.json +++ b/assets/themes/etymology.json @@ -32,7 +32,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 2, - "socialImage": "", "clustering": { "maxZoom": 14, "minNeededElements": 250 diff --git a/assets/themes/facadegardens/facadegardens.json b/assets/themes/facadegardens/facadegardens.json index a9d50b9c6..923346ff3 100644 --- a/assets/themes/facadegardens/facadegardens.json +++ b/assets/themes/facadegardens/facadegardens.json @@ -36,7 +36,6 @@ "startLon": 4.480705, "startZoom": 15, "widenFactor": 1.5, - "socialImage": "", "layers": [ { "id": "facadegardens", diff --git a/assets/themes/food/food.json b/assets/themes/food/food.json index 796aeefd4..05b4516ce 100644 --- a/assets/themes/food/food.json +++ b/assets/themes/food/food.json @@ -20,7 +20,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 3, - "socialImage": "", "layers": [ "food" ] diff --git a/assets/themes/fritures/fritures.json b/assets/themes/fritures/fritures.json index 22677da03..46d5a4e8e 100644 --- a/assets/themes/fritures/fritures.json +++ b/assets/themes/fritures/fritures.json @@ -16,7 +16,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 3, - "socialImage": "", "layers": [ { "builtin": "food", diff --git a/assets/themes/fruit_trees/fruit_trees.json b/assets/themes/fruit_trees/fruit_trees.json index 3b687bd89..7fe8c9323 100644 --- a/assets/themes/fruit_trees/fruit_trees.json +++ b/assets/themes/fruit_trees/fruit_trees.json @@ -16,7 +16,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 2, - "socialImage": "", "hideFromOverview": true, "layers": [ { diff --git a/assets/themes/grb_import/grb_fixme.json b/assets/themes/grb_import/grb_fixme.json index 313b5efb6..2ae057dfd 100644 --- a/assets/themes/grb_import/grb_fixme.json +++ b/assets/themes/grb_import/grb_fixme.json @@ -16,7 +16,6 @@ "startLon": 3.231, "startZoom": 14, "widenFactor": 2, - "socialImage": "", "clustering": { "maxZoom": 15 }, diff --git a/assets/themes/grb_import/missing_streets.json b/assets/themes/grb_import/missing_streets.json index 4bcd22c2a..d30e0a2cc 100644 --- a/assets/themes/grb_import/missing_streets.json +++ b/assets/themes/grb_import/missing_streets.json @@ -16,7 +16,6 @@ "startLon": 4.026489, "startZoom": 9, "widenFactor": 2, - "socialImage": "", "clustering": { "maxZoom": 15 }, diff --git a/assets/themes/hackerspaces/hackerspaces.json b/assets/themes/hackerspaces/hackerspaces.json index 4deafa356..c5728cb7e 100644 --- a/assets/themes/hackerspaces/hackerspaces.json +++ b/assets/themes/hackerspaces/hackerspaces.json @@ -29,7 +29,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 5, - "socialImage": "", "layers": [ { "id": "hackerspaces", diff --git a/assets/themes/hailhydrant/hailhydrant.json b/assets/themes/hailhydrant/hailhydrant.json index a135d0f4e..0d4007b45 100644 --- a/assets/themes/hailhydrant/hailhydrant.json +++ b/assets/themes/hailhydrant/hailhydrant.json @@ -37,7 +37,6 @@ "startLon": 121.6625, "startZoom": 6, "widenFactor": 3, - "socialImage": "", "layers": [ "hydrant", "extinguisher", diff --git a/assets/themes/maps/maps.json b/assets/themes/maps/maps.json index 305eb4a17..b18aecf4f 100644 --- a/assets/themes/maps/maps.json +++ b/assets/themes/maps/maps.json @@ -37,7 +37,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 5, - "socialImage": "", "layers": [ "map" ] diff --git a/assets/themes/nature/nature.json b/assets/themes/nature/nature.json index 8dc62a6df..294d6be52 100644 --- a/assets/themes/nature/nature.json +++ b/assets/themes/nature/nature.json @@ -19,7 +19,6 @@ "startLon": 3.22435, "startZoom": 12, "widenFactor": 2, - "socialImage": "", "layers": [ "drinking_water", "birdhide", diff --git a/assets/themes/natuurpunt/natuurpunt.json b/assets/themes/natuurpunt/natuurpunt.json index c66a67d92..3231ecbab 100644 --- a/assets/themes/natuurpunt/natuurpunt.json +++ b/assets/themes/natuurpunt/natuurpunt.json @@ -41,7 +41,6 @@ ] ], "widenFactor": 2, - "socialImage": "", "defaultBackgroundId": "CartoDB.Positron", "enablePdfDownload": true, "enableDownload": false, diff --git a/assets/themes/observation_towers/observation_towers.json b/assets/themes/observation_towers/observation_towers.json index 5a76cb753..f6bf823f9 100644 --- a/assets/themes/observation_towers/observation_towers.json +++ b/assets/themes/observation_towers/observation_towers.json @@ -32,7 +32,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 5, - "socialImage": "", "layers": [ "observation_tower" ] diff --git a/assets/themes/play_forests/play_forests.json b/assets/themes/play_forests/play_forests.json index 7c6950912..1a003edff 100644 --- a/assets/themes/play_forests/play_forests.json +++ b/assets/themes/play_forests/play_forests.json @@ -17,7 +17,6 @@ "startZoom": 1, "hideFromOverview": true, "widenFactor": 3, - "socialImage": "", "layers": [ "play_forest" ] diff --git a/assets/themes/playgrounds/playgrounds.json b/assets/themes/playgrounds/playgrounds.json index 696b7ff1b..8ab906ae8 100644 --- a/assets/themes/playgrounds/playgrounds.json +++ b/assets/themes/playgrounds/playgrounds.json @@ -45,7 +45,6 @@ "startLon": 4.399, "startZoom": 13, "widenFactor": 5, - "socialImage": "", "layers": [ "playground" ] diff --git a/assets/themes/postal_codes/postal_codes.json b/assets/themes/postal_codes/postal_codes.json index 6e14f97ec..247b4de33 100644 --- a/assets/themes/postal_codes/postal_codes.json +++ b/assets/themes/postal_codes/postal_codes.json @@ -22,7 +22,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 0.05, - "socialImage": "", "hideFromOverview": true, "clustering": false, "overpassTimeout": 180, diff --git a/assets/themes/shops/shops.json b/assets/themes/shops/shops.json index 6f07ffdf2..0c058075e 100644 --- a/assets/themes/shops/shops.json +++ b/assets/themes/shops/shops.json @@ -35,7 +35,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 3, - "socialImage": "", "layers": [ "shops" ] diff --git a/assets/themes/sidewalks/sidewalks.json b/assets/themes/sidewalks/sidewalks.json index 72ef76042..28cbe8ae8 100644 --- a/assets/themes/sidewalks/sidewalks.json +++ b/assets/themes/sidewalks/sidewalks.json @@ -18,7 +18,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 0.05, - "socialImage": "", "hideFromOverview": true, "layers": [ { diff --git a/assets/themes/sport_pitches/sport_pitches.json b/assets/themes/sport_pitches/sport_pitches.json index d2a88cd00..b0237a414 100644 --- a/assets/themes/sport_pitches/sport_pitches.json +++ b/assets/themes/sport_pitches/sport_pitches.json @@ -39,7 +39,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 2, - "socialImage": "", "layers": [ "sport_pitch" ] diff --git a/assets/themes/surveillance/surveillance.json b/assets/themes/surveillance/surveillance.json index fefeb406c..197362a86 100644 --- a/assets/themes/surveillance/surveillance.json +++ b/assets/themes/surveillance/surveillance.json @@ -39,7 +39,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 2, - "socialImage": "", "defaultBackgroundId": "osm", "layers": [ "direction", diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index febb712fe..6efb7ccdb 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -24,7 +24,6 @@ "startLon": 51.52224, "startZoom": 17, "widenFactor": 1.01, - "socialImage": "", "hideFromOverview": true, "clustering": { "minNeededFeatures": 25, diff --git a/assets/themes/waste_basket/waste_basket.json b/assets/themes/waste_basket/waste_basket.json index b0bca66e1..d312125a8 100644 --- a/assets/themes/waste_basket/waste_basket.json +++ b/assets/themes/waste_basket/waste_basket.json @@ -32,7 +32,6 @@ "startLon": 0, "startZoom": 1, "widenFactor": 2, - "socialImage": "", "layers": [ { "builtin": "waste_basket", From 812626456dbb85353e2a46662fbd8799e5b68bfc Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 19 Feb 2022 17:44:25 +0100 Subject: [PATCH 28/43] Fix dead image --- assets/layers/direction/direction.json | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/assets/layers/direction/direction.json b/assets/layers/direction/direction.json index c60322d11..4fa7f7ce6 100644 --- a/assets/layers/direction/direction.json +++ b/assets/layers/direction/direction.json @@ -32,16 +32,7 @@ "presets": [], "mapRendering": [ { - "icon": { - "render": "direction_gradient:var(--catch-detail-color)", - "#": "For some weird reason, showing the icon in the layer control panel breaks the svg-gradient (because the svg gradient has a global color or smthng) - so we use a different icon without gradient", - "mappings": [ - { - "if": "id=node/-1", - "then": "direction:var(--catch-detail-color)" - } - ] - }, + "icon": "direction_gradient:var(--catch-detail-color)", "iconSize": "200,200,center", "location": [ "point", From c06f8820a847b7dfc54993b67083b48c770110e7 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 19 Feb 2022 17:47:12 +0100 Subject: [PATCH 29/43] Extract images from slow_roads.json --- assets/layers/slow_roads/slow_roads.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/assets/layers/slow_roads/slow_roads.json b/assets/layers/slow_roads/slow_roads.json index 2964b7808..9984d916f 100644 --- a/assets/layers/slow_roads/slow_roads.json +++ b/assets/layers/slow_roads/slow_roads.json @@ -71,7 +71,11 @@ { "if": "highway=living_street", "then": { - "nl:": "
Dit is een woonerf:
  • Voetgangers mogen hier de volledige breedte van de straat gebruiken
  • Gemotoriseerd verkeer mag maximaal 20km/h rijden
" + "nl:": "
Dit is een woonerf:
  • Voetgangers mogen hier de volledige breedte van de straat gebruiken
  • Gemotoriseerd verkeer mag maximaal 20km/h rijden
" + }, + "icon": { + "path": "./assets/layers/slow_roads/woonerf.svg", + "class": "medium" } }, { From 798556f6d381fe6bbbca39d636403edf3511e08f Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 19 Feb 2022 17:50:06 +0100 Subject: [PATCH 30/43] Extract images from watermill.json and trail.json --- assets/layers/trail/trail.json | 14 +++++++++++--- assets/layers/watermill/watermill.json | 14 +++++++++++--- langs/layers/nl.json | 8 ++++---- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/assets/layers/trail/trail.json b/assets/layers/trail/trail.json index 84416e15f..0ec5f307b 100644 --- a/assets/layers/trail/trail.json +++ b/assets/layers/trail/trail.json @@ -77,7 +77,11 @@ ] }, "then": { - "nl": "Dit gebied wordt beheerd door Natuurpunt" + "nl": "Dit gebied wordt beheerd door Natuurpunt" + }, + "icon": { + "path": "./assets/themes/buurtnatuur/Natuurpunt.jpg", + "class": "small" } }, { @@ -87,9 +91,13 @@ ] }, "then": { - "nl": "Dit gebied wordt beheerd door {operator}" + "nl": "Dit gebied wordt beheerd door {operator}" }, - "hideInAnswer": true + "hideInAnswer": true, + "icon": { + "path": "./assets/themes/buurtnatuur/Natuurpunt.jpg", + "class": "small" + } } ], "id": "Operator tag" diff --git a/assets/layers/watermill/watermill.json b/assets/layers/watermill/watermill.json index 1e885ee8e..b69a4d4de 100644 --- a/assets/layers/watermill/watermill.json +++ b/assets/layers/watermill/watermill.json @@ -146,7 +146,11 @@ ] }, "then": { - "nl": "Dit gebied wordt beheerd door Natuurpunt" + "nl": "Dit gebied wordt beheerd door Natuurpunt" + }, + "icon": { + "path": "./assets/themes/buurtnatuur/Natuurpunt.jpg", + "class": "small" } }, { @@ -156,9 +160,13 @@ ] }, "then": { - "nl": "Dit gebied wordt beheerd door {operator}" + "nl": "Dit gebied wordt beheerd door {operator}" }, - "hideInAnswer": true + "hideInAnswer": true, + "icon": { + "path": "./assets/themes/buurtnatuur/Natuurpunt.jpg", + "class": "small" + } } ], "id": "Operator tag" diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 7c2c17a36..d11d5942b 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -4612,10 +4612,10 @@ "Operator tag": { "mappings": { "0": { - "then": "Dit gebied wordt beheerd door Natuurpunt" + "then": "Dit gebied wordt beheerd door Natuurpunt" }, "1": { - "then": "Dit gebied wordt beheerd door {operator}" + "then": "Dit gebied wordt beheerd door {operator}" } }, "question": "Wie beheert deze wandeltocht?", @@ -4908,10 +4908,10 @@ "Operator tag": { "mappings": { "0": { - "then": "Dit gebied wordt beheerd door Natuurpunt" + "then": "Dit gebied wordt beheerd door Natuurpunt" }, "1": { - "then": "Dit gebied wordt beheerd door {operator}" + "then": "Dit gebied wordt beheerd door {operator}" } }, "question": "Wie beheert dit pad?", From 4580ba0ce0812be17278233f98799282b3548bc3 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 19 Feb 2022 17:57:34 +0100 Subject: [PATCH 31/43] Add error suppression --- Models/ThemeConfig/Conversion/Validation.ts | 17 ++++++++++++++--- assets/themes/uk_addresses/uk_addresses.json | 1 + 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Models/ThemeConfig/Conversion/Validation.ts b/Models/ThemeConfig/Conversion/Validation.ts index bcdb3902d..e7624b0dc 100644 --- a/Models/ThemeConfig/Conversion/Validation.ts +++ b/Models/ThemeConfig/Conversion/Validation.ts @@ -289,22 +289,33 @@ export class DetectMappingsWithImages extends DesugaringStep=0 const images = Utils.Dedup(Translations.T(mapping.then).ExtractImages()) + const ctx = `${context}.mappings[${i}]` if (images.length > 0) { - warnings.push(context + ".mappings[" + i + "]: A mapping has an image in the 'then'-clause. Remove the image there and use `\"icon\": ` instead. The images found are "+images.join(", ")) + if(!ignore){ + errors.push(`${ctx}: A mapping has an image in the 'then'-clause. Remove the image there and use \`"icon": \` instead. The images found are ${images.join(", ")}. (Ignore this warning by adding "#": "${ignoreToken}" to the mapping`) + }else{ + information.push(`${ctx}: Ignored images in then`) + } + }else if (ignore){ + warnings.push(`${ctx}: unused '${ignoreToken}' - please remove this`) } } return { - warnings, + errors,warnings,information, result: json }; } diff --git a/assets/themes/uk_addresses/uk_addresses.json b/assets/themes/uk_addresses/uk_addresses.json index 6efb7ccdb..4a46793bf 100644 --- a/assets/themes/uk_addresses/uk_addresses.json +++ b/assets/themes/uk_addresses/uk_addresses.json @@ -239,6 +239,7 @@ }, "mappings": [ { + "#": "ignore-image-in-then", "if": "addr:substreet~*", "then": "
The envelope below shows the address that we have recorded. You can change this by answering any remaining questions above, or by clicking the pencil icons below. We do not need you to provide a recipient's name or any of the parts shown in [blue].
{addr:unit} {addr:housename}
{addr:housenumber} {addr:substreet}
{addr:street}
{addr:parentstreet}
[Suburb]
[Town]
[Postal code]
" } From cc086eaee1f8a81622f5970c0acb95a73aba4ab5 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 19 Feb 2022 21:37:54 +0100 Subject: [PATCH 32/43] Allow changing type --- assets/layers/recycling/recycling.json | 32 +++++++++++++++++++++++++- langs/layers/en.json | 3 +++ langs/layers/nl.json | 3 +++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index b0e3f9562..d42d1bf81 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -170,6 +170,35 @@ "en": "This is a recycling centre", "nl": "Dit is een recyclingcentrum" } + }, + { + "if": "amenity=waste_disposal", + "then": { + "en": "Waste disposal container for residual waste", + "nl": "Afvalcontainer voor restafval" + }, + "addExtraTags": [ + "recycling:batteries=", + "recycling:beverage_cartons=", + "recycling:cans=", + "recycling:clothes=", + "recycling:cooking_oil=", + "recycling:engine_oil=", + "recycling:green_waste=", + "recycling:organic=", + "recycling:glass_bottles=", + "recycling:glass=", + "recycling:newspaper=", + "recycling:paper=", + "recycling:plastic_bottles=", + "recycling:plastic_packaging=", + "recycling:plastic=", + "recycling:scrap_metal=", + "recycling:shoes=", + "recycling:small_appliances=", + "recycling:small_electrical_appliances=", + "recycling:waste=" + ] } ] }, @@ -452,7 +481,8 @@ "icon": { "path": "./assets/layers/waste_disposal/waste_disposal.svg", "class": "medium" - } + }, + "hideInAnswer": "recycling_type=container" } ] }, diff --git a/langs/layers/en.json b/langs/layers/en.json index a10d84461..54d43657b 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4327,6 +4327,9 @@ }, "1": { "then": "This is a recycling centre" + }, + "2": { + "then": "Waste disposal container for residual waste" } }, "question": "What type of recycling is this?" diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 23fda94ad..a4ce4c6a5 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -4119,6 +4119,9 @@ }, "1": { "then": "Dit is een recyclingcentrum" + }, + "2": { + "then": "Afvalcontainer voor restafval" } }, "question": "Wat voor soort recycling is dit?" From d49fd8df1f418458eeaf3824701cf5dc334b599a Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 19 Feb 2022 21:40:12 +0100 Subject: [PATCH 33/43] Add white background to icon --- assets/layers/waste_disposal/waste_disposal.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/layers/waste_disposal/waste_disposal.json b/assets/layers/waste_disposal/waste_disposal.json index dc5c2e334..a589d75d1 100644 --- a/assets/layers/waste_disposal/waste_disposal.json +++ b/assets/layers/waste_disposal/waste_disposal.json @@ -22,7 +22,7 @@ "point", "centroid" ], - "icon": "./assets/layers/waste_disposal/waste_disposal.svg" + "icon": "circle:white;./assets/layers/waste_disposal/waste_disposal.svg" } ], "presets": [ From 2e51a4566890f3cf467d5537b8f5c9c06f8d3c9c Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 19 Feb 2022 22:52:52 +0100 Subject: [PATCH 34/43] Use icon of waste if only one is set --- assets/layers/recycling/recycling.json | 407 ++++++++++++++++++++----- 1 file changed, 338 insertions(+), 69 deletions(-) diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index d42d1bf81..43942ac3d 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -11,6 +11,9 @@ "source": { "osmTags": "amenity=recycling" }, + "calculatedTags": [ + "_waste_amount=Object.values(Object.keys(feat.properties).filter((key) => key.startsWith('recycling:')).reduce((cur, key) => { return Object.assign(cur, { [key]: feat.properties[key] })}, {})).reduce((n, x) => n + (x == \"yes\"), 0);" + ], "minzoom": 12, "title": { "render": { @@ -40,89 +43,355 @@ "point", "centroid" ], - "icon": "circle:white;./assets/layers/recycling/recycling-14.svg", + "icon": { + "render": "circle:white;./assets/layers/recycling/recycling-14.svg", + "mappings": [ + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:batteries=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/batteries.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:beverage_cartons=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/beverage_cartons.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:cans=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/cans.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:clothes=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/clothes.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:cooking_oil=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/cooking_oil.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:engine_oil=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/engine_oil.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:glass=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/glass.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:glass_bottles=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/glass_bottles.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + { + "or": [ + "recycling:green_waste=yes", + "recycling:organic=yes" + ] + } + ] + }, + "then": "circle:white;./assets/layers/recycling/garden_waste.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:newspaper=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/newspaper.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:paper=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/paper.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:plastic_bottles=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/plastic_bottles.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:plastic_packaging=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/plastic_packaging.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:plastic=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/plastic.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:scrap_metal=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/scrap_metal.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:shoes=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/shoes.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + { + "or": [ + "recycling:small_appliances=yes", + "recycling:small_electric_appliances=yes" + ] + } + ] + }, + "then": "circle:white;./assets/layers/recycling/small_electrical_appliances.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + "recycling:waste=yes" + ] + }, + "then": "circle:white;./assets/layers/waste_disposal/waste_disposal.svg" + } + ] + }, "iconBadges": [ { - "if": "recycling:batteries=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/batteries.svg" - }, - { - "if": "recycling:beverage_cartons=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/beverage_cartons.svg" - }, - { - "if": "recycling:cans=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/cans.svg" - }, - { - "if": "recycling:clothes=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/clothes.svg" - }, - { - "if": "recycling:cooking_oil=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/cooking_oil.svg" - }, - { - "if": "recycling:engine_oil=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/engine_oil.svg" - }, - { - "if": "recycling:glass=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/glass.svg" - }, - { - "if": "recycling:glass_bottles=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/glass_bottles.svg" + "if": { + "and": [ + "_waste_amount>1", + "recycling:batteries=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/batteries.svg" }, { "if": { - "or": [ - "recycling:green_waste=yes", - "recycling:organic=yes" + "and": [ + "_waste_amount>1", + "recycling:beverage_cartons=yes" ] }, - "then": "circle:#FFFFFF;./assets/layers/recycling/garden_waste.svg" - }, - { - "if": "recycling:newspaper=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/newspaper.svg" - }, - { - "if": "recycling:paper=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/paper.svg" - }, - { - "if": "recycling:plastic_bottles=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/plastic_bottles.svg" - }, - { - "if": "recycling:plastic_packaging=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/plastic_packaging.svg" - }, - { - "if": "recycling:plastic=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/plastic.svg" - }, - { - "if": "recycling:scrap_metal=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/scrap_metal.svg" - }, - { - "if": "recyling:shoes=yes", - "then": "circle:#FFFFFF;./assets/layers/recycling/shoes.svg" + "then": "circle:white;./assets/layers/recycling/beverage_cartons.svg" }, { "if": { - "or": [ - "recycling:small_appliances=yes", - "recycling:small_electrical_appliances=yes" + "and": [ + "_waste_amount>1", + "recycling:cans=yes" ] }, - "then": "circle:#FFFFFF;./assets/layers/recycling/small_electrical_appliances.svg" + "then": "circle:white;./assets/layers/recycling/cans.svg" }, { - "if": "recycling:waste=yes", - "then": "circle:#FFFFFF;./assets/layers/waste_disposal/waste_disposal.svg" + "if": { + "and": [ + "_waste_amount>1", + "recycling:clothes=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/clothes.svg" + }, + { + "if": { + "and": [ + "_waste_amount>1", + "recycling:cooking_oil=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/cooking_oil.svg" + }, + { + "if": { + "and": [ + "_waste_amount>1", + "recycling:engine_oil=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/engine_oil.svg" + }, + { + "if": { + "and": [ + "_waste_amount>1", + "recycling:glass=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/glass.svg" + }, + { + "if": { + "and": [ + "_waste_amount>1", + "recycling:glass_bottles=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/glass_bottles.svg" + }, + { + "if": { + "and": [ + "_waste_amount=1", + { + "or": [ + "recycling:green_waste=yes", + "recycling:organic=yes" + ] + } + ] + }, + "then": "circle:white;./assets/layers/recycling/garden_waste.svg" + }, + { + "if": { + "and": [ + "_waste_amount>1", + "recycling:newspaper=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/newspaper.svg" + }, + { + "if": { + "and": [ + "_waste_amount>1", + "recycling:paper=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/paper.svg" + }, + { + "if": { + "and": [ + "_waste_amount>1", + "recycling:plastic_bottles=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/plastic_bottles.svg" + }, + { + "if": { + "and": [ + "_waste_amount>1", + "recycling:plastic_packaging=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/plastic_packaging.svg" + }, + { + "if": { + "and": [ + "_waste_amount>1", + "recycling:plastic=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/plastic.svg" + }, + { + "if": { + "and": [ + "_waste_amount>1", + "recycling:scrap_metal=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/scrap_metal.svg" + }, + { + "if": { + "and": [ + "_waste_amount>1", + "recycling:shoes=yes" + ] + }, + "then": "circle:white;./assets/layers/recycling/shoes.svg" + }, + { + "if": { + "and": [ + "_waste_amount>1", + { + "or": [ + "recycling:small_appliances=yes", + "recycling:small_electrical_appliances=yes" + ] + } + ] + }, + "then": "circle:white;./assets/layers/recycling/small_electrical_appliances.svg" + }, + { + "if": { + "and": [ + "_waste_amount>1", + "recycling:waste=yes" + ] + }, + "then": "circle:white;./assets/layers/waste_disposal/waste_disposal.svg" } ] } From 6a76865312e5e92cb368dd5c00cf2b1750312383 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 20 Feb 2022 00:30:28 +0100 Subject: [PATCH 35/43] Fix title() element --- UI/SpecialVisualizations.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 8a4d6a2cb..97b594375 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -45,6 +45,7 @@ import ImgurUploader from "../Logic/ImageProviders/ImgurUploader"; import FileSelectorButton from "./Input/FileSelectorButton"; import {LoginToggle} from "./Popup/LoginButton"; import {start} from "repl"; +import {SubstitutedTranslation} from "./SubstitutedTranslation"; export interface SpecialVisualization { funcName: string, @@ -865,13 +866,14 @@ export default class SpecialVisualizations { args: [], docs:"Shows the title of the popup. Useful for some cases, e.g. 'What is phone number of {title()}?'", example:"`What is the phone number of {title()}`, which might automatically become `What is the phone number of XYZ`.", - constr: (state, tags, args, guistate) => - new VariableUiElement(tags.map(tags => { + constr: (state, tagsSource, args, guistate) => + new VariableUiElement(tagsSource.map(tags => { const layer = state.layoutToUse.getMatchingLayer(tags) - console.log("Layer for tags", tags,"is", layer.id) const title = layer?.title?.GetRenderValue(tags) - console.log("Title became: ", title) - return title + if(title === undefined){ + return undefined + } + return new SubstitutedTranslation(title, tagsSource, state) })) } ] From 89c5cd3565ba0e51a175ed5374652444da8a8d40 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 20 Feb 2022 00:44:38 +0100 Subject: [PATCH 36/43] Fix tests --- test/LegacyThemeLoader.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/LegacyThemeLoader.spec.ts b/test/LegacyThemeLoader.spec.ts index d310887f7..e3b66ff65 100644 --- a/test/LegacyThemeLoader.spec.ts +++ b/test/LegacyThemeLoader.spec.ts @@ -466,8 +466,9 @@ export default class LegacyThemeLoaderSpec extends T { } }] }, "test"); - T.isTrue(r.warnings.length > 0, "No images found"); - T.isTrue(r.warnings.some(msg => msg.indexOf("./assets/layers/bike_parking/staple.svg") >= 0), "staple.svg not mentioned"); + const errors = r.errors; + T.isTrue(errors.length > 0, "No images found"); + T.isTrue(errors.some(msg => msg.indexOf("./assets/layers/bike_parking/staple.svg") >= 0), "staple.svg not mentioned"); }], ["Images in 'thens' icons are detected", () => { const r = new ExtractImages(true, new Map()).convert({ From 363803fc458c049cc3488e2b4a40cc67583b7eb2 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 20 Feb 2022 00:51:11 +0100 Subject: [PATCH 37/43] Improvement in logging --- Models/ThemeConfig/Conversion/Validation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Models/ThemeConfig/Conversion/Validation.ts b/Models/ThemeConfig/Conversion/Validation.ts index e7624b0dc..dc17d0d50 100644 --- a/Models/ThemeConfig/Conversion/Validation.ts +++ b/Models/ThemeConfig/Conversion/Validation.ts @@ -305,9 +305,9 @@ export class DetectMappingsWithImages extends DesugaringStep 0) { if(!ignore){ - errors.push(`${ctx}: A mapping has an image in the 'then'-clause. Remove the image there and use \`"icon": \` instead. The images found are ${images.join(", ")}. (Ignore this warning by adding "#": "${ignoreToken}" to the mapping`) + errors.push(`${ctx}: A mapping has an image in the 'then'-clause. Remove the image there and use \`"icon": \` instead. The images found are ${images.join(", ")}. (This check can be turned of by adding "#": "${ignoreToken}" in the mapping, but this is discouraged`) }else{ - information.push(`${ctx}: Ignored images in then`) + information.push(`${ctx}: Ignored image ${images.join(", ")} in 'then'-clause of a mapping as this check has been disabled`) } }else if (ignore){ warnings.push(`${ctx}: unused '${ignoreToken}' - please remove this`) From 125b63927dd0dbe40ae2c4745ced7520e548968c Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 20 Feb 2022 01:39:12 +0100 Subject: [PATCH 38/43] Improve shadowing detection of mappings --- Models/ThemeConfig/Conversion/Validation.ts | 32 ++++++++++++++------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/Models/ThemeConfig/Conversion/Validation.ts b/Models/ThemeConfig/Conversion/Validation.ts index dc17d0d50..5380360d3 100644 --- a/Models/ThemeConfig/Conversion/Validation.ts +++ b/Models/ThemeConfig/Conversion/Validation.ts @@ -248,9 +248,19 @@ export class DetectShadowedMappings extends DesugaringStep TagUtils.Tag(m.if)) + const parsedConditions = json.mappings.map(m => { + const ifTags = TagUtils.Tag(m.if); + if(m.hideInAnswer !== undefined && m.hideInAnswer !== false && m.hideInAnswer !== true){ + let conditionTags = TagUtils.Tag( m.hideInAnswer) + // Merge the condition too! + return new And([conditionTags, ifTags]) + } + return ifTags + }) for (let i = 0; i < json.mappings.length; i++) { - if(json.mappings[i].hideInAnswer === true){ + if(!parsedConditions[i].isUsableAsAnswer()){ + // There is no straightforward way to convert this mapping.if into a properties-object, so we simply skip this one + // Yes, it might be shadowed, but running this check is to difficult right now continue } const keyValues = parsedConditions[i].asChange({}); @@ -259,14 +269,11 @@ export class DetectShadowedMappings extends DesugaringStep { constructor() { super("Various validation on tagRenderingConfigs", - // TODO enable these checks again - // new DetectShadowedMappings(), + new DetectShadowedMappings(), new DetectMappingsWithImages() ); } From eddaca7767387ddde3129440ff233080e5f674b1 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 20 Feb 2022 01:46:31 +0100 Subject: [PATCH 39/43] Fix typo --- assets/layers/recycling/recycling.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index 43942ac3d..84b10f009 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -296,7 +296,7 @@ { "if": { "and": [ - "_waste_amount=1", + "_waste_amount>1", { "or": [ "recycling:green_waste=yes", From 30f4be183ee232d4f941c0987b24dbf0d2dd3b71 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 20 Feb 2022 02:02:09 +0100 Subject: [PATCH 40/43] Fix tests --- Models/ThemeConfig/Conversion/Validation.ts | 2 +- test/LegacyThemeLoader.spec.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Models/ThemeConfig/Conversion/Validation.ts b/Models/ThemeConfig/Conversion/Validation.ts index 5380360d3..f8174c5b0 100644 --- a/Models/ThemeConfig/Conversion/Validation.ts +++ b/Models/ThemeConfig/Conversion/Validation.ts @@ -264,7 +264,7 @@ export class DetectShadowedMappings extends DesugaringStep { properties[k] = v }) diff --git a/test/LegacyThemeLoader.spec.ts b/test/LegacyThemeLoader.spec.ts index e3b66ff65..8b1972120 100644 --- a/test/LegacyThemeLoader.spec.ts +++ b/test/LegacyThemeLoader.spec.ts @@ -422,8 +422,8 @@ export default class LegacyThemeLoaderSpec extends T { } ] }, "test"); - T.isTrue(r.errors.length > 0, "Failing case 0 is not detected") - + T.isTrue(r.warnings.length > 0, "Failing case 0 is not detected") + T.isTrue(r.warnings[0].indexOf("The mapping key=value is fully matched by a previous mapping (namely 0)") >= 0, "Error message does not contain tag and indices") const r0 = new DetectShadowedMappings().convert({ mappings: [ { @@ -436,7 +436,7 @@ export default class LegacyThemeLoaderSpec extends T { } ] }, "test"); - T.isTrue(r0.errors.length > 0, "Failing case 1 is not detected") + T.isTrue(r0.warnings.length > 0, "Failing case 1 is not detected") } ], ["Images are rewritten", () => { From d19791382f3d0798a72a28776eda99074447a0d1 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 21 Feb 2022 11:39:57 +0100 Subject: [PATCH 41/43] Add move and delete options --- assets/layers/recycling/recycling.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index 84b10f009..270620fd6 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -945,5 +945,10 @@ } ] } - ] + ], + "deletion": {"neededChangesets": 1}, + "allowMove": { + "enableRelocation": false, + "enableImproveAccuracy": true + } } \ No newline at end of file From 5284f198d8f72372b4b45dcb3799c41bacef43c6 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 22 Feb 2022 14:13:41 +0100 Subject: [PATCH 42/43] Fix various bugs --- Docs/Tools/GenerateSeries.ts | 2 +- .../AvailableBaseLayersImplementation.ts | 2 +- Logic/FeatureSource/FeaturePipeline.ts | 2 +- .../Sources/ChangeGeometryApplicator.ts | 2 +- .../TiledFeatureSource/OsmFeatureSource.ts | 2 +- Logic/Osm/Actions/ChangeDescription.ts | 2 +- .../CreateMultiPolygonWithPointReuseAction.ts | 4 +- Logic/Osm/Actions/CreateNewNodeAction.ts | 20 +- Logic/Osm/Actions/CreateNewWayAction.ts | 2 +- .../Actions/CreateWayWithPointReuseAction.ts | 4 +- Logic/Osm/Actions/ReplaceGeometryAction.ts | 3 +- Logic/Osm/Changes.ts | 4 +- Logic/Osm/OsmObject.ts | 25 +- Logic/State/FeaturePipelineState.ts | 2 +- Logic/State/MapState.ts | 40 ++-- Models/ThemeConfig/PointRenderingConfig.ts | 2 +- Models/ThemeConfig/TagRenderingConfig.ts | 3 +- UI/BigComponents/Histogram.ts | 2 +- UI/Input/ValidatedTextField.ts | 4 +- UI/Popup/ImportButton.ts | 42 +--- UI/Popup/LoginButton.ts | 1 - UI/Popup/SplitRoadWizard.ts | 2 +- Utils.ts | 1 + test/CodeQuality.spec.ts | 62 +++-- test/ImportMultiPolygon.spec.ts | 219 ++++++++++++++++++ test/TestAll.ts | 4 +- 26 files changed, 339 insertions(+), 119 deletions(-) create mode 100644 test/ImportMultiPolygon.spec.ts diff --git a/Docs/Tools/GenerateSeries.ts b/Docs/Tools/GenerateSeries.ts index 9f135c54d..b97d7399b 100644 --- a/Docs/Tools/GenerateSeries.ts +++ b/Docs/Tools/GenerateSeries.ts @@ -528,7 +528,7 @@ function stackHists(hists: [V, Histogram][]): [V, Histogram][] { runningTotals.bumpHist(hist) result.push([vhist[0], clone]) }) - result.reverse() + result.reverse(/* Changes in place, safe copy*/) return result } diff --git a/Logic/Actors/AvailableBaseLayersImplementation.ts b/Logic/Actors/AvailableBaseLayersImplementation.ts index 2ac7aa8d4..6b12013fe 100644 --- a/Logic/Actors/AvailableBaseLayersImplementation.ts +++ b/Logic/Actors/AvailableBaseLayersImplementation.ts @@ -239,7 +239,7 @@ export default class AvailableBaseLayersImplementation implements AvailableBaseL prefered = preferedCategory.data; } - prefered.reverse(); + prefered.reverse(/*New list, inplace reverse is fine*/); for (const category of prefered) { //Then sort all 'photo'-layers to the top. Stability of the sorting will force a 'best' photo layer on top available.sort((a, b) => { diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index bb2382aff..87d4aedda 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -75,7 +75,7 @@ export default class FeaturePipeline { this.state = state; const self = this - const expiryInSeconds = Math.min(...state.layoutToUse.layers.map(l => l.maxAgeOfCache)) + const expiryInSeconds = Math.min(...state.layoutToUse?.layers?.map(l => l.maxAgeOfCache) ?? []) this.oldestAllowedDate = new Date(new Date().getTime() - expiryInSeconds); this.osmSourceZoomLevel = state.osmApiTileSize.data; const useOsmApi = state.locationControl.map(l => l.zoom > (state.overpassMaxZoom.data ?? 12)) diff --git a/Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts b/Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts index 83ab174bd..d7d6c8b7a 100644 --- a/Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts +++ b/Logic/FeatureSource/Sources/ChangeGeometryApplicator.ts @@ -74,7 +74,7 @@ export default class ChangeGeometryApplicator implements FeatureSourceForLayer { // We only apply the last change as that one'll have the latest geometry const change = changesForFeature[changesForFeature.length - 1] copy.feature.geometry = ChangeDescriptionTools.getGeojsonGeometry(change) - console.log("Applying a geometry change onto ", feature, change, copy) + console.log("Applying a geometry change onto:", feature,"The change is:", change,"which becomes:", copy) newFeatures.push(copy) } this.features.setData(newFeatures) diff --git a/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts index 68190d909..a9bd149b5 100644 --- a/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts +++ b/Logic/FeatureSource/TiledFeatureSource/OsmFeatureSource.ts @@ -79,7 +79,7 @@ export default class OsmFeatureSource { }) - const neededLayers = options.state.layoutToUse.layers + const neededLayers = (options.state.layoutToUse?.layers ?? []) .filter(layer => !layer.doNotDownload) .filter(layer => layer.source.geojsonSource === undefined || layer.source.isOsmCacheLayer) this.allowedTags = new Or(neededLayers.map(l => l.source.osmTags)) diff --git a/Logic/Osm/Actions/ChangeDescription.ts b/Logic/Osm/Actions/ChangeDescription.ts index 0f03caf0b..42df3f4a0 100644 --- a/Logic/Osm/Actions/ChangeDescription.ts +++ b/Logic/Osm/Actions/ChangeDescription.ts @@ -81,7 +81,7 @@ export class ChangeDescriptionTools { case "way": const w = new OsmWay(change.id) w.nodes = change.changes["nodes"] - w.coordinates = change.changes["coordinates"].map(coor => coor.reverse()) + w.coordinates = change.changes["coordinates"].map(([lon, lat]) => [lat, lon]) return w.asGeoJson().geometry case "relation": const r = new OsmRelation(change.id) diff --git a/Logic/Osm/Actions/CreateMultiPolygonWithPointReuseAction.ts b/Logic/Osm/Actions/CreateMultiPolygonWithPointReuseAction.ts index d5e3f53ec..73eb1f3a1 100644 --- a/Logic/Osm/Actions/CreateMultiPolygonWithPointReuseAction.ts +++ b/Logic/Osm/Actions/CreateMultiPolygonWithPointReuseAction.ts @@ -33,12 +33,12 @@ export default class CreateMultiPolygonWithPointReuseAction extends OsmCreateAct super(null, true); this._tags = [...tags, new Tag("type", "multipolygon")]; this.changeType = changeType; - this.theme = state.layoutToUse.id + this.theme = state?.layoutToUse?.id ?? "" this.createOuterWay = new CreateWayWithPointReuseAction([], outerRingCoordinates, state, config) this.createInnerWays = innerRingsCoordinates.map(ringCoordinates => new CreateNewWayAction([], ringCoordinates.map(([lon, lat]) => ({lat, lon})), - {theme: state.layoutToUse.id})) + {theme: state?.layoutToUse?.id})) this.geojsonPreview = { type: "Feature", diff --git a/Logic/Osm/Actions/CreateNewNodeAction.ts b/Logic/Osm/Actions/CreateNewNodeAction.ts index 1c7d37989..844463ec1 100644 --- a/Logic/Osm/Actions/CreateNewNodeAction.ts +++ b/Logic/Osm/Actions/CreateNewNodeAction.ts @@ -112,16 +112,25 @@ export default class CreateNewNodeAction extends OsmCreateAction { const geojson = this._snapOnto.asGeoJson() const projected = GeoOperations.nearestPoint(geojson, [this._lon, this._lat]) + const projectedCoor= <[number, number]>projected.geometry.coordinates const index = projected.properties.index // We check that it isn't close to an already existing point let reusedPointId = undefined; - const prev = <[number, number]>geojson.geometry.coordinates[index] - if (GeoOperations.distanceBetween(prev, <[number, number]>projected.geometry.coordinates) < this._reusePointDistance) { + let outerring : [number,number][]; + + if(geojson.geometry.type === "LineString"){ + outerring = <[number, number][]> geojson.geometry.coordinates + }else if(geojson.geometry.type === "Polygon"){ + outerring =<[number, number][]> geojson.geometry.coordinates[0] + } + + const prev= outerring[index] + if (GeoOperations.distanceBetween(prev, projectedCoor) < this._reusePointDistance) { // We reuse this point instead! reusedPointId = this._snapOnto.nodes[index] } - const next = <[number, number]>geojson.geometry.coordinates[index + 1] - if (GeoOperations.distanceBetween(next, <[number, number]>projected.geometry.coordinates) < this._reusePointDistance) { + const next = outerring[index + 1] + if (GeoOperations.distanceBetween(next, projectedCoor) < this._reusePointDistance) { // We reuse this point instead! reusedPointId = this._snapOnto.nodes[index + 1] } @@ -135,8 +144,7 @@ export default class CreateNewNodeAction extends OsmCreateAction { }] } - const locations = [...this._snapOnto.coordinates] - locations.forEach(coor => coor.reverse()) + const locations = [...this._snapOnto.coordinates.map(([lat, lon]) =><[number,number]> [lon, lat])] const ids = [...this._snapOnto.nodes] locations.splice(index + 1, 0, [this._lon, this._lat]) diff --git a/Logic/Osm/Actions/CreateNewWayAction.ts b/Logic/Osm/Actions/CreateNewWayAction.ts index 68c521abc..edc6dd0bf 100644 --- a/Logic/Osm/Actions/CreateNewWayAction.ts +++ b/Logic/Osm/Actions/CreateNewWayAction.ts @@ -33,7 +33,7 @@ export default class CreateNewWayAction extends OsmCreateAction { We filter those here, as the CreateWayWithPointReuseAction delegates the actual creation to here. Filtering here also prevents similar bugs in other actions */ - if(this.coordinates.length > 0 && this.coordinates[this.coordinates.length - 1].nodeId === coordinate.nodeId){ + if(this.coordinates.length > 0 && coordinate.nodeId !== undefined && this.coordinates[this.coordinates.length - 1].nodeId === coordinate.nodeId){ // This is a duplicate id console.warn("Skipping a node in createWay to avoid a duplicate node:", coordinate,"\nThe previous coordinates are: ", this.coordinates) continue diff --git a/Logic/Osm/Actions/CreateWayWithPointReuseAction.ts b/Logic/Osm/Actions/CreateWayWithPointReuseAction.ts index afcdab4a1..95911f432 100644 --- a/Logic/Osm/Actions/CreateWayWithPointReuseAction.ts +++ b/Logic/Osm/Actions/CreateWayWithPointReuseAction.ts @@ -186,7 +186,7 @@ export default class CreateWayWithPointReuseAction extends OsmCreateAction { } public async CreateChangeDescriptions(changes: Changes): Promise { - const theme = this._state.layoutToUse.id + const theme = this._state?.layoutToUse?.id const allChanges: ChangeDescription[] = [] const nodeIdsToUse: { lat: number, lon: number, nodeId?: number }[] = [] for (let i = 0; i < this._coordinateInfo.length; i++) { @@ -251,7 +251,7 @@ export default class CreateWayWithPointReuseAction extends OsmCreateAction { const bbox = new BBox(coordinates) const state = this._state - const allNodes = [].concat(...state.featurePipeline.GetFeaturesWithin("type_node", bbox.pad(1.2))) + const allNodes = [].concat(...state?.featurePipeline?.GetFeaturesWithin("type_node", bbox.pad(1.2))??[]) const maxDistance = Math.max(...this._config.map(c => c.withinRangeOfM)) // Init coordianteinfo with undefined but the same length as coordinates diff --git a/Logic/Osm/Actions/ReplaceGeometryAction.ts b/Logic/Osm/Actions/ReplaceGeometryAction.ts index 5146e5574..f642b2d04 100644 --- a/Logic/Osm/Actions/ReplaceGeometryAction.ts +++ b/Logic/Osm/Actions/ReplaceGeometryAction.ts @@ -28,6 +28,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction { /** * The target coordinates that should end up in OpenStreetMap. * This is identical to either this.feature.geometry.coordinates or -in case of a polygon- feature.geometry.coordinates[0] + * Format: [lon, lat] */ private readonly targetCoordinates: [number, number][]; /** @@ -540,8 +541,6 @@ export default class ReplaceGeometryAction extends OsmChangeAction { id: nodeId, }) }) - - } return allChanges diff --git a/Logic/Osm/Changes.ts b/Logic/Osm/Changes.ts index 10640991f..c8cfb83bc 100644 --- a/Logic/Osm/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -55,8 +55,8 @@ export class Changes { // This doesn't matter however, as the '-1' is per piecewise upload, not global per changeset } - private static createChangesetFor(csId: string, - allChanges: { + static createChangesetFor(csId: string, + allChanges: { modifiedObjects: OsmObject[], newObjects: OsmObject[], deletedObjects: OsmObject[] diff --git a/Logic/Osm/OsmObject.ts b/Logic/Osm/OsmObject.ts index c73cf4e72..bb5a8e997 100644 --- a/Logic/Osm/OsmObject.ts +++ b/Logic/Osm/OsmObject.ts @@ -207,27 +207,36 @@ export abstract class OsmObject { return objects; } + /** + * Uses the list of polygon features to determine if the given tags are a polygon or not. + * */ protected static isPolygon(tags: any): boolean { for (const tagsKey in tags) { if (!tags.hasOwnProperty(tagsKey)) { continue } - const polyGuide = OsmObject.polygonFeatures.get(tagsKey) + const polyGuide : { values: Set; blacklist: boolean } = OsmObject.polygonFeatures.get(tagsKey) if (polyGuide === undefined) { continue } if ((polyGuide.values === null)) { - // We match all + // .values is null, thus merely _having_ this key is enough to be a polygon (or if blacklist, being a line) return !polyGuide.blacklist } - // is the key contained? - return polyGuide.values.has(tags[tagsKey]) + // is the key contained? Then we have a match if the value is contained + const doesMatch = polyGuide.values.has(tags[tagsKey]) + if(polyGuide.blacklist){ + return !doesMatch + } + return doesMatch } + + return false; } private static constructPolygonFeatures(): Map, blacklist: boolean }> { const result = new Map, blacklist: boolean }>(); - for (const polygonFeature of polygon_features) { + for (const polygonFeature of (polygon_features["default"] ?? polygon_features)) { const key = polygonFeature.key; if (polygonFeature.polygon === "all") { @@ -381,7 +390,7 @@ export class OsmWay extends OsmObject { } if (element.nodes === undefined) { - console.log("PANIC") + console.error("PANIC: no nodes!") } for (const nodeId of element.nodes) { @@ -417,7 +426,9 @@ export class OsmWay extends OsmObject { } private isPolygon(): boolean { - if (this.coordinates[0] !== this.coordinates[this.coordinates.length - 1]) { + // Compare lat and lon seperately, as the coordinate array might not be a reference to the same object + if (this.coordinates[0][0] !== this.coordinates[this.coordinates.length - 1][0] || + this.coordinates[0][1] !== this.coordinates[this.coordinates.length - 1][1] ) { return false; // Not closed } return OsmObject.isPolygon(this.tags) diff --git a/Logic/State/FeaturePipelineState.ts b/Logic/State/FeaturePipelineState.ts index 2c2c2a81f..62f461605 100644 --- a/Logic/State/FeaturePipelineState.ts +++ b/Logic/State/FeaturePipelineState.ts @@ -25,7 +25,7 @@ export default class FeaturePipelineState extends MapState { constructor(layoutToUse: LayoutConfig) { super(layoutToUse); - const clustering = layoutToUse.clustering + const clustering = layoutToUse?.clustering this.featureAggregator = TileHierarchyAggregator.createHierarchy(this); const clusterCounter = this.featureAggregator const self = this; diff --git a/Logic/State/MapState.ts b/Logic/State/MapState.ts index 387885a9c..32aba550d 100644 --- a/Logic/State/MapState.ts +++ b/Logic/State/MapState.ts @@ -117,10 +117,12 @@ export default class MapState extends UserRelatedState { }) - this.overlayToggles = this.layoutToUse.tileLayerSources.filter(c => c.name !== undefined).map(c => ({ + this.overlayToggles = this.layoutToUse?.tileLayerSources + ?.filter(c => c.name !== undefined) + ?.map(c => ({ config: c, isDisplayed: QueryParameters.GetBooleanQueryParameter("overlay-" + c.id, c.defaultState, "Wether or not the overlay " + c.id + " is shown") - })) + })) ?? [] this.filteredLayers = this.InitializeFilteredLayers() @@ -142,7 +144,7 @@ export default class MapState extends UserRelatedState { initialized.add(overlayToggle.config) } - for (const tileLayerSource of this.layoutToUse.tileLayerSources) { + for (const tileLayerSource of this.layoutToUse?.tileLayerSources ?? []) { if (initialized.has(tileLayerSource)) { continue } @@ -153,28 +155,14 @@ export default class MapState extends UserRelatedState { private lockBounds() { const layout = this.layoutToUse; - if (layout.lockLocation) { - if (layout.lockLocation === true) { - const tile = Tiles.embedded_tile( - layout.startLat, - layout.startLon, - layout.startZoom - 1 - ); - const bounds = Tiles.tile_bounds(tile.z, tile.x, tile.y); - // We use the bounds to get a sense of distance for this zoom level - const latDiff = bounds[0][0] - bounds[1][0]; - const lonDiff = bounds[0][1] - bounds[1][1]; - layout.lockLocation = [ - [layout.startLat - latDiff, layout.startLon - lonDiff], - [layout.startLat + latDiff, layout.startLon + lonDiff], - ]; - } - console.warn("Locking the bounds to ", layout.lockLocation); - this.mainMapObject.installBounds( - new BBox(layout.lockLocation), - this.featureSwitchIsTesting.data - ) + if (!layout?.lockLocation) { + return; } + console.warn("Locking the bounds to ", layout.lockLocation); + this.mainMapObject.installBounds( + new BBox(layout.lockLocation), + this.featureSwitchIsTesting.data + ) } private initCurrentView() { @@ -364,8 +352,10 @@ export default class MapState extends UserRelatedState { } private InitializeFilteredLayers() { - const layoutToUse = this.layoutToUse; + if(layoutToUse === undefined){ + return new UIEventSource([]) + } const flayers: FilteredLayer[] = []; for (const layer of layoutToUse.layers) { let isDisplayed: UIEventSource diff --git a/Models/ThemeConfig/PointRenderingConfig.ts b/Models/ThemeConfig/PointRenderingConfig.ts index 9bffc3d26..0a02149b8 100644 --- a/Models/ThemeConfig/PointRenderingConfig.ts +++ b/Models/ThemeConfig/PointRenderingConfig.ts @@ -127,7 +127,7 @@ export default class PointRenderingConfig extends WithContextLoader { public GetBaseIcon(tags?: any): BaseUIElement { tags = tags ?? {id: "node/-1"} const rotation = Utils.SubstituteKeys(this.rotation?.GetRenderValue(tags)?.txt ?? "0deg", tags) - const htmlDefs = Utils.SubstituteKeys(this.icon.GetRenderValue(tags)?.txt, tags) + const htmlDefs = Utils.SubstituteKeys(this.icon?.GetRenderValue(tags)?.txt, tags) let defaultPin: BaseUIElement = undefined if (this.label === undefined) { defaultPin = Svg.teardrop_with_hole_green_svg() diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index 5c1c9b28c..f0e797aae 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -338,7 +338,8 @@ export default class TagRenderingConfig { const free = this.freeform?.key if (free !== undefined) { - return tags[free] !== undefined + const value = tags[free] + return value !== undefined && value !== "" } return false diff --git a/UI/BigComponents/Histogram.ts b/UI/BigComponents/Histogram.ts index 81daeef55..d786fbf5d 100644 --- a/UI/BigComponents/Histogram.ts +++ b/UI/BigComponents/Histogram.ts @@ -93,7 +93,7 @@ export default class Histogram extends VariableUiElement { keys.sort() break; case "name-rev": - keys.sort().reverse() + keys.sort().reverse(/*Copy of array, inplace reverse if fine*/) break; case "count": keys.sort((k0, k1) => counts.get(k0) - counts.get(k1)) diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index 75b561319..9359289d4 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -543,9 +543,9 @@ class LengthTextField extends TextFieldDef { // Bit of a hack: we project the centerpoint to the closes point on the road - if available if (options?.feature !== undefined && options.feature.geometry.type !== "Point") { const lonlat = <[number, number]>[...options.location] - lonlat.reverse() + lonlat.reverse(/*Changes a clone, this is safe */) options.location = <[number, number]>GeoOperations.nearestPoint(options.feature, lonlat).geometry.coordinates - options.location.reverse() + options.location.reverse(/*Changes a clone, this is safe */) } diff --git a/UI/Popup/ImportButton.ts b/UI/Popup/ImportButton.ts index 7ad8842e8..3f956c3f0 100644 --- a/UI/Popup/ImportButton.ts +++ b/UI/Popup/ImportButton.ts @@ -373,7 +373,7 @@ export class ImportWayButton extends AbstractImportButton implements AutoAction { name: "max_snap_distance", doc: "If the imported object is a LineString or (Multi)Polygon, already existing OSM-points will be reused to construct the geometry of the newly imported way", - defaultValue: "5" + defaultValue: "0.05" }, { name: "move_osm_point_if", @@ -381,7 +381,7 @@ export class ImportWayButton extends AbstractImportButton implements AutoAction }, { name: "max_move_distance", doc: "If an OSM-point is moved, the maximum amount of meters it is moved. Capped on 20m", - defaultValue: "1" + defaultValue: "0.05" }, { name: "snap_onto_layers", doc: "If no existing nearby point exists, but a line of a specified layer is closeby, snap to this layer instead", @@ -406,24 +406,12 @@ export class ImportWayButton extends AbstractImportButton implements AutoAction AbstractImportButton.importedIds.add(originalFeatureTags.data.id) const args = this.parseArgs(argument, originalFeatureTags) const feature = state.allElements.ContainingFeatures.get(id) - console.log("Geometry to auto-import is:", feature) - const geom = feature.geometry - let coordinates: [number, number][] - if (geom.type === "LineString") { - coordinates = geom.coordinates - } else if (geom.type === "Polygon") { - coordinates = geom.coordinates[0] - } - - const mergeConfigs = this.GetMergeConfig(args); - - const action = this.CreateAction( + const action = ImportWayButton.CreateAction( feature, args, state, - mergeConfigs, - coordinates + mergeConfigs ) await state.changes.applyAction(action) } @@ -455,18 +443,8 @@ export class ImportWayButton extends AbstractImportButton implements AutoAction // Upload the way to OSM - const geom = feature.geometry - let coordinates: [number, number][] - if (geom.type === "LineString") { - coordinates = geom.coordinates - } else if (geom.type === "Polygon") { - coordinates = geom.coordinates[0] - } const mergeConfigs = this.GetMergeConfig(args); - - - let action = this.CreateAction(feature, args, state, mergeConfigs, coordinates); - + let action = ImportWayButton.CreateAction(feature, args, state, mergeConfigs); return this.createConfirmPanelForWay( state, args, @@ -508,14 +486,12 @@ export class ImportWayButton extends AbstractImportButton implements AutoAction return mergeConfigs; } - private CreateAction(feature, + private static CreateAction(feature, args: { max_snap_distance: string; snap_onto_layers: string; icon: string; text: string; tags: string; newTags: UIEventSource; targetLayer: string }, state: FeaturePipelineState, - mergeConfigs: any[], - coordinates: [number, number][]) { - + mergeConfigs: any[]) { const coors = feature.geometry.coordinates - if (feature.geometry.type === "Polygon" && coors.length > 1) { + if ((feature.geometry.type === "Polygon" ) && coors.length > 1) { const outer = coors[0] const inner = [...coors] inner.splice(0, 1) @@ -531,7 +507,7 @@ export class ImportWayButton extends AbstractImportButton implements AutoAction return new CreateWayWithPointReuseAction( args.newTags.data, - coordinates, + coors, state, mergeConfigs ) diff --git a/UI/Popup/LoginButton.ts b/UI/Popup/LoginButton.ts index dd16d6411..ecf859daf 100644 --- a/UI/Popup/LoginButton.ts +++ b/UI/Popup/LoginButton.ts @@ -28,7 +28,6 @@ export class LoginToggle extends VariableUiElement { const login = new LoginButton(text, state) super( state.osmConnection.loadingStatus.map(osmConnectionState => { - console.trace("Current osm state is ", osmConnectionState) if(osmConnectionState === "loading"){ return loading } diff --git a/UI/Popup/SplitRoadWizard.ts b/UI/Popup/SplitRoadWizard.ts index 5bdd70667..8ae8b0158 100644 --- a/UI/Popup/SplitRoadWizard.ts +++ b/UI/Popup/SplitRoadWizard.ts @@ -107,7 +107,7 @@ export default class SplitRoadWizard extends Toggle { .filter(p => GeoOperations.distanceBetween(p[0].geometry.coordinates, coordinates) < 5) .map(p => p[1]) .sort((a, b) => a - b) - .reverse() + .reverse(/*Copy/derived list, inplace reverse is fine*/) if (points.length > 0) { for (const point of points) { splitPoints.data.splice(point, 1) diff --git a/Utils.ts b/Utils.ts index 41e4d141e..a39f34e88 100644 --- a/Utils.ts +++ b/Utils.ts @@ -777,5 +777,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be b: parseInt(hex.substr(5, 2), 16), } } + } diff --git a/test/CodeQuality.spec.ts b/test/CodeQuality.spec.ts index 9962892e1..7b3ce78c5 100644 --- a/test/CodeQuality.spec.ts +++ b/test/CodeQuality.spec.ts @@ -2,34 +2,48 @@ import T from "./TestHelper"; import {exec} from "child_process"; export default class CodeQualitySpec extends T { + constructor() { super([ [ "no constructor.name in compiled code", () => { - - const excludedDirs = [".git", "node_modules", "dist", ".cache", ".parcel-cache", "assets"] - - exec("grep \"constructor.name\" -r . " + excludedDirs.map(d => "--exclude-dir=" + d).join(" "), ((error, stdout, stderr) => { - if (error?.message?.startsWith("Command failed: grep")) { - return; - } - if (error !== null) { - throw error - - } - if (stderr !== "") { - throw stderr - } - - const found = stdout.split("\n").filter(s => s !== "").filter(s => s.startsWith("test/")); - if (found.length > 0) { - throw "Found a 'constructor.name' at " + found.join(", ") + ". This is not allowed, as minification does erase names." - } - - })) - - } - ] + CodeQualitySpec.detectInCode("constructor\\.name", "This is not allowed, as minification does erase names.") + }], + [ + "no reverse in compiled code", () => { + CodeQualitySpec.detectInCode("reverse()", "Reverse is stateful and changes the source list. This often causes subtle bugs") + }] ]); } + + /** + * + * @param forbidden: a GREP-regex. This means that '.' is a wildcard and should be escaped to match a literal dot + * @param reason + * @private + */ + private static detectInCode(forbidden: string, reason: string) { + + const excludedDirs = [".git", "node_modules", "dist", ".cache", ".parcel-cache", "assets"] + + exec("grep -n \"" + forbidden + "\" -r . " + excludedDirs.map(d => "--exclude-dir=" + d).join(" "), ((error, stdout, stderr) => { + if (error?.message?.startsWith("Command failed: grep")) { + console.warn("Command failed!") + return; + } + if (error !== null) { + throw error + + } + if (stderr !== "") { + throw stderr + } + + const found = stdout.split("\n").filter(s => s !== "").filter(s => !s.startsWith("./test/")); + if (found.length > 0) { + throw `Found a '${forbidden}' at \n ${found.join("\n ")}.\n ${reason}` + } + + })) + } } \ No newline at end of file diff --git a/test/ImportMultiPolygon.spec.ts b/test/ImportMultiPolygon.spec.ts new file mode 100644 index 000000000..e4b480f92 --- /dev/null +++ b/test/ImportMultiPolygon.spec.ts @@ -0,0 +1,219 @@ +import T from "./TestHelper"; +import CreateMultiPolygonWithPointReuseAction from "../Logic/Osm/Actions/CreateMultiPolygonWithPointReuseAction"; +import { Tag } from "../Logic/Tags/Tag"; +import FeaturePipelineState from "../Logic/State/FeaturePipelineState"; +import { Changes } from "../Logic/Osm/Changes"; +import {ChangesetHandler} from "../Logic/Osm/ChangesetHandler"; +import * as Assert from "assert"; + +export default class ImportMultiPolygonSpec extends T { + + constructor() { + super([ + ["Correct changeset", + async () => { + + const feature = { + "type": "Feature", + "properties": { + "osm_id": "41097039", + "size_grb_building": "1374.89", + "addr:housenumber": "53", + "addr:street": "Startelstraat", + "building": "house", + "source:geometry:entity": "Gbg", + "source:geometry:date": "2014-04-28", + "source:geometry:oidn": "150044", + "source:geometry:uidn": "5403181", + "H_DTM_MIN": "50.35", + "H_DTM_GEM": "50.97", + "H_DSM_MAX": "59.40", + "H_DSM_P99": "59.09", + "HN_MAX": "8.43", + "HN_P99": "8.12", + "detection_method": "derived from OSM landuse: farmyard", + "auto_target_landuse": "farmyard", + "size_source_landuse": "8246.28", + "auto_building": "farm", + "id": "41097039", + "_lat": "50.84633355000016", + "_lon": "5.262964150000011", + "_layer": "grb", + "_length": "185.06002152312757", + "_length:km": "0.2", + "_now:date": "2022-02-22", + "_now:datetime": "2022-02-22 10:15:51", + "_loaded:date": "2022-02-22", + "_loaded:datetime": "2022-02-22 10:15:51", + "_geometry:type": "Polygon", + "_intersects_with_other_features": "", + "_country": "be", + "_overlaps_with_buildings": "[]", + "_overlap_percentage": "null", + "_grb_date": "2014-04-28", + "_grb_ref": "Gbg/150044", + "_building:min_level": "", + "_surface": "548.1242491529038", + "_surface:ha": "0", + "_reverse_overlap_percentage": "null", + "_imported_osm_object_found": "false", + "_imported_osm_still_fresh": "false", + "_target_building_type": "house" + }, + "geometry": { + "type": "Polygon", + "coordinates": <[number, number][][]>[ + [ + [ + 5.262684300000043, + 50.84624409999995 + ], + [ + 5.262777500000024, + 50.84620759999988 + ], + [ + 5.262798899999998, + 50.84621390000019 + ], + [ + 5.262999799999994, + 50.84619519999999 + ], + [ + 5.263107500000007, + 50.84618920000014 + ], + [ + 5.263115, + 50.84620990000026 + ], + [ + 5.26310279999998, + 50.84623050000014 + ], + [ + 5.263117999999977, + 50.846247400000166 + ], + [ + 5.263174599999989, + 50.84631019999971 + ], + [ + 5.263166999999989, + 50.84631459999995 + ], + [ + 5.263243999999979, + 50.84640239999989 + ], + [ + 5.2631607000000065, + 50.84643459999996 + ], + [ + 5.26313309999997, + 50.84640089999985 + ], + [ + 5.262907499999996, + 50.84647790000018 + ], + [ + 5.2628939999999576, + 50.846463699999774 + ], + [ + 5.262872100000033, + 50.846440700000294 + ], + [ + 5.262784699999991, + 50.846348899999924 + ], + [ + 5.262684300000043, + 50.84624409999995 + ] + ], + [ + [ + 5.262801899999976, + 50.84623269999982 + ], + [ + 5.2629535000000285, + 50.84638830000012 + ], + [ + 5.263070700000018, + 50.84634720000008 + ], + [ + 5.262998000000025, + 50.84626279999982 + ], + [ + 5.263066799999966, + 50.84623959999975 + ], + [ + 5.263064000000004, + 50.84623330000007 + ], + [ + 5.263009599999997, + 50.84623730000026 + ], + [ + 5.263010199999956, + 50.84621629999986 + ], + [ + 5.262801899999976, + 50.84623269999982 + ] + ] + ] + }, + } + + const innerRings = [...feature.geometry.coordinates] + innerRings.splice(0, 1) + + const action = new CreateMultiPolygonWithPointReuseAction( + [new Tag("building", "yes")], + feature.geometry.coordinates[0], + innerRings, + undefined, + [], + "import" + ) + const descriptions = await action.Perform(new Changes()) + + function getCoor(id: number): {lat: number, lon:number} { + return descriptions.find(d => d.type === "node" && d.id === id).changes + } + + const ways= descriptions.filter(d => d.type === "way") + T.isTrue(ways[0].id == -18, "unexpected id") + T.isTrue(ways[1].id == -27, "unexpected id") + const outer = ways[0].changes["coordinates"] + const outerExpected = [[5.262684300000043,50.84624409999995],[5.262777500000024,50.84620759999988],[5.262798899999998,50.84621390000019],[5.262999799999994,50.84619519999999],[5.263107500000007,50.84618920000014],[5.263115,50.84620990000026],[5.26310279999998,50.84623050000014],[5.263117999999977,50.846247400000166],[5.263174599999989,50.84631019999971],[5.263166999999989,50.84631459999995],[5.263243999999979,50.84640239999989],[5.2631607000000065,50.84643459999996],[5.26313309999997,50.84640089999985],[5.262907499999996,50.84647790000018],[5.2628939999999576,50.846463699999774],[5.262872100000033,50.846440700000294],[5.262784699999991,50.846348899999924],[5.262684300000043,50.84624409999995]] + T.listIdentical(feature.geometry.coordinates[0], outer) + const inner = ways[1].changes["coordinates"] + T.listIdentical(feature.geometry.coordinates[1], inner) + const members = <{type: string, role: string, ref: number}[]> descriptions.find(d => d.type === "relation").changes["members"] + T.isTrue(members[0].role == "outer", "incorrect role") + T.isTrue(members[1].role == "inner", "incorrect role") + T.isTrue(members[0].type == "way", "incorrect type") + T.isTrue(members[1].type == "way", "incorrect type") + T.isTrue(members[0].ref == -18, "incorrect id") + T.isTrue(members[1].ref == -27, "incorrect id") + }] + ]); + } + + +} \ No newline at end of file diff --git a/test/TestAll.ts b/test/TestAll.ts index 5d43853cc..37f408220 100644 --- a/test/TestAll.ts +++ b/test/TestAll.ts @@ -20,6 +20,7 @@ import CreateNoteImportLayerSpec from "./CreateNoteImportLayer.spec"; import ValidatedTextFieldTranslationsSpec from "./ValidatedTextFieldTranslations.spec"; import CreateCacheSpec from "./CreateCache.spec"; import CodeQualitySpec from "./CodeQuality.spec"; +import ImportMultiPolygonSpec from "./ImportMultiPolygon.spec"; async function main() { @@ -43,7 +44,8 @@ async function main() { new CreateNoteImportLayerSpec(), new ValidatedTextFieldTranslationsSpec(), new CreateCacheSpec(), - new CodeQualitySpec() + new CodeQualitySpec(), + new ImportMultiPolygonSpec() ] ScriptUtils.fixUtils(); const realDownloadFunc = Utils.externalDownloadFunction; From 793d765ead26544cbcb08ca1e04084e775619e37 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Tue, 22 Feb 2022 16:11:00 +0100 Subject: [PATCH 43/43] Add contact information to recycling centers --- Models/Constants.ts | 2 +- assets/layers/direction/direction.json | 2 +- assets/layers/recycling/recycling.json | 49 ++++++++++++++++++++++++-- langs/layers/en.json | 12 +++++++ langs/layers/nl.json | 12 +++++++ 5 files changed, 73 insertions(+), 4 deletions(-) diff --git a/Models/Constants.ts b/Models/Constants.ts index c76a77f17..a8bec17f0 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -61,7 +61,7 @@ export default class Constants { * For every bin, the totals are uploaded as metadata */ static distanceToChangeObjectBins = [25, 50, 100, 500, 1000, 5000, Number.MAX_VALUE] - static themeOrder = ["personal", "cyclofix", "hailhydrant", "bookcases", "toilets", "aed"]; + static themeOrder = ["personal", "cyclofix", "waste" , "etymology", "food","cafes_and_pubs", "playgrounds", "hailhydrant", "toilets", "aed", "bookcases"]; private static isRetina(): boolean { if (Utils.runningFromConsole) { diff --git a/assets/layers/direction/direction.json b/assets/layers/direction/direction.json index 4fa7f7ce6..81dc20931 100644 --- a/assets/layers/direction/direction.json +++ b/assets/layers/direction/direction.json @@ -32,7 +32,7 @@ "presets": [], "mapRendering": [ { - "icon": "direction_gradient:var(--catch-detail-color)", + "icon": "direction_gradient:var(--catch-detail-color)", "iconSize": "200,200,center", "location": [ "point", diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index 270620fd6..bab282396 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -22,12 +22,20 @@ }, "mappings": [ { - "if": "recycling_type=centre", + "if": "name~*", "then": { + "*": "{name}", "en": "Recycling centre", "nl": "Recyclingcentrum" } }, + { + "if": "recycling_type=centre", + "then": { + "en": "Recycling container", + "nl": "Recyclingcontainer" + } + }, { "if": "recycling_type=container", "then": { @@ -419,6 +427,7 @@ } ], "tagRenderings": [ + "images", { "id": "recycling-type", "question": { @@ -471,6 +480,30 @@ } ] }, + { + "id": "recycling-centre-name", + "question": { + "en": "What is the name of this recycling centre?", + "nl": "Wat is de naam van dit recyclagecentrum?" + }, + "render": { + "en": "This recycling centre is named {name}", + "nl": "Dit recyclagecentrum heet {name}" + }, + "freeform": { + "key": "name" + }, + "mappings": [ + { + "if": "noname=yes", + "then": { + "en": "This recycling centre doesn't have a specific name", + "nl": "Dit recyclagecentrum heeft geen specifieke naam" + } + } + ], + "condition": "recycling_type=centre" + }, { "id": "container-location", "question": { @@ -770,6 +803,16 @@ "type": "string" } }, + { + "builtin": [ + "website", + "email", + "phone" + ], + "override": { + "condition": "recycling_type=centre" + } + }, { "id": "opening_hours", "render": "{opening_hours_table()}", @@ -946,7 +989,9 @@ ] } ], - "deletion": {"neededChangesets": 1}, + "deletion": { + "neededChangesets": 1 + }, "allowMove": { "enableRelocation": false, "enableImproveAccuracy": true diff --git a/langs/layers/en.json b/langs/layers/en.json index 1cbc07a8f..ac5a29326 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4294,6 +4294,15 @@ }, "question": "What can be recycled here?" }, + "recycling-centre-name": { + "mappings": { + "0": { + "then": "This recycling centre doesn't have a specific name" + } + }, + "question": "What is the name of this recycling centre?", + "render": "This recycling centre is named {name}" + }, "recycling-type": { "mappings": { "0": { @@ -4316,6 +4325,9 @@ }, "1": { "then": "Recycling container" + }, + "2": { + "then": "Recycling container" } }, "render": "Recycling facility" diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 48e89d361..58d377dd0 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -4086,6 +4086,15 @@ }, "question": "Wat kan hier gerecycled worden?" }, + "recycling-centre-name": { + "mappings": { + "0": { + "then": "Dit recyclagecentrum heeft geen specifieke naam" + } + }, + "question": "Wat is de naam van dit recyclagecentrum?", + "render": "Dit recyclagecentrum heet {name}" + }, "recycling-type": { "mappings": { "0": { @@ -4108,6 +4117,9 @@ }, "1": { "then": "Recyclingcontainer" + }, + "2": { + "then": "Recyclingcontainer" } }, "render": "Recyclingfaciliteit"