From a7f2c26a1def08a27f2fa5fe623be6f9eff4cb16 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sat, 9 Jul 2022 21:41:33 +0000 Subject: [PATCH 01/23] Concept for rendering maproulette tasks --- Logic/FeatureSource/Sources/GeoJsonSource.ts | 22 +++ assets/themes/maproulette/maproulette.json | 145 +++++++++++++++++++ 2 files changed, 167 insertions(+) create mode 100644 assets/themes/maproulette/maproulette.json diff --git a/Logic/FeatureSource/Sources/GeoJsonSource.ts b/Logic/FeatureSource/Sources/GeoJsonSource.ts index 1443760ca..1deca987c 100644 --- a/Logic/FeatureSource/Sources/GeoJsonSource.ts +++ b/Logic/FeatureSource/Sources/GeoJsonSource.ts @@ -82,6 +82,28 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { Utils.downloadJsonCached(url, 60 * 60) .then(json => { self.state.setData("loaded") + // TODO: move somewhere else, just for testing + // Check for maproulette data + if (url.startsWith("https://maproulette.org/api/v2/")) { + console.log("MapRoulette data detected") + const data = json; + let maprouletteFeatures: any[] = []; + data.forEach(element => { + maprouletteFeatures.push({ + type: "Feature", + geometry: { + type: "Point", + coordinates: [element.point.lng, element.point.lat] + }, + properties: { + // Map all properties to the feature + ...element, + } + }); + }); + json.features = maprouletteFeatures; + } + if (json.features === undefined || json.features === null) { return; } diff --git a/assets/themes/maproulette/maproulette.json b/assets/themes/maproulette/maproulette.json new file mode 100644 index 000000000..56b0c5ace --- /dev/null +++ b/assets/themes/maproulette/maproulette.json @@ -0,0 +1,145 @@ +{ + "id": "maproulette", + "title": { + "en": "MapRoulette Test" + }, + "description": { + "en": "MapRoulette Test" + }, + "version": "1.0.0", + "hideFromOverview": true, + "icon": "./assets/svg/bug.svg", + "maintainer": "", + "startLat": 0, + "startLon": 0, + "startZoom": 4, + "layers": [ + { + "id": "maproulette", + "source": { + "geoJson": "https://maproulette.org/api/v2/tasks/box/{x_min}/{y_min}/{x_max}/{y_max}", + "geoJsonZoomLevel": 16, + "osmTags": "id~*" + }, + "mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": { + "render": "pin:white", + "mappings": [ + { + "if": "status=0", + "then": "pin:#959DFF" + }, + { + "if": "status=1", + "then": "pin:#65D2DA" + }, + { + "if": "status=2", + "then": "pin:#F7BB59" + }, + { + "if": "status=3", + "then": "pin:#F7BB59" + }, + { + "if": "status=4", + "then": "pin:#737373" + }, + { + "if": "status=5", + "then": "pin:#CCB186" + }, + { + "if": "status=6", + "then": "pin:#FF5E63" + }, + { + "if": "status=9", + "then": "pin:#FF349C" + } + ] + }, + "iconSize": "40,40,center" + } + ], + "tagRenderings": [ + { + "id": "link", + "render": "View this task" + }, + { + "id": "status", + "render": "Current status: {status}", + "mappings": [ + { + "if": "status=0", + "then": { + "en": "Task is created" + } + }, + { + "if": "status=1", + "then": { + "en": "Task is fixed" + } + }, + { + "if": "status=2", + "then": { + "en": "Task is a false positive" + } + }, + { + "if": "status=3", + "then": { + "en": "Task is skipped" + } + }, + { + "if": "status=4", + "then": { + "en": "Task is deleted" + } + }, + { + "if": "status=5", + "then": { + "en": "Task is already fixed" + } + }, + { + "if": "status=6", + "then": { + "en": "Task is marked as too hard" + } + }, + { + "if": "status=9", + "then": { + "en": "Task is disabled" + } + } + ] + }, + "all_tags" + ], + "description": { + "en": "Layer showing all tasks in MapRoulette" + }, + "minzoom": 15, + "name": { + "en": "MapRoulette Tasks" + }, + "title": { + "render": { + "en": "MapRoulette Item: {parentName}" + } + } + } + ] +} \ No newline at end of file From cba68ec81c35fe5f17d7d1d5dd6b499c7a8427db Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 10 Jul 2022 19:27:47 +0000 Subject: [PATCH 02/23] 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 b694e52b9..d7c8f53a9 100644 --- a/assets/layers/waste_basket/waste_basket.json +++ b/assets/layers/waste_basket/waste_basket.json @@ -303,7 +303,7 @@ }, "allowMove": { "enableRelocation": false, - "enableImproveAccuraccy": true + "enableImproveAccuracy": true }, "mapRendering": [ { From 4b1a6fe32d0f8a8e8d047ab774dcc4766e13586e Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 10 Jul 2022 19:29:37 +0000 Subject: [PATCH 03/23] Split off layer, add logo and filters --- assets/layers/maproulette/license_info.json | 12 ++ assets/layers/maproulette/logomark.svg | 10 + assets/layers/maproulette/maproulette.json | 191 ++++++++++++++++++ .../mapcomplete-changes.json | 4 + assets/themes/maproulette/maproulette.json | 130 +----------- 5 files changed, 219 insertions(+), 128 deletions(-) create mode 100644 assets/layers/maproulette/license_info.json create mode 100644 assets/layers/maproulette/logomark.svg create mode 100644 assets/layers/maproulette/maproulette.json diff --git a/assets/layers/maproulette/license_info.json b/assets/layers/maproulette/license_info.json new file mode 100644 index 000000000..f59ad3208 --- /dev/null +++ b/assets/layers/maproulette/license_info.json @@ -0,0 +1,12 @@ +[ + { + "path": "logomark.svg", + "license": "MIT", + "authors": [ + "MapRoulette" + ], + "sources": [ + "https://github.com/maproulette/docs/blob/master/src/assets/svg/logo.svg" + ] + } +] \ No newline at end of file diff --git a/assets/layers/maproulette/logomark.svg b/assets/layers/maproulette/logomark.svg new file mode 100644 index 000000000..4ad7dd319 --- /dev/null +++ b/assets/layers/maproulette/logomark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/assets/layers/maproulette/maproulette.json b/assets/layers/maproulette/maproulette.json new file mode 100644 index 000000000..3daf86389 --- /dev/null +++ b/assets/layers/maproulette/maproulette.json @@ -0,0 +1,191 @@ +{ + "id": "maproulette", + "source": { + "geoJson": "https://maproulette.org/api/v2/tasks/box/{x_min}/{y_min}/{x_max}/{y_max}", + "geoJsonZoomLevel": 16, + "osmTags": "id~*" + }, + "mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": { + "render": "./assets/layers/maproulette/logomark.svg", + "mappings": [ + { + "if": "status=0", + "then": "pin:#959DFF" + }, + { + "if": "status=1", + "then": "pin:#65D2DA" + }, + { + "if": "status=2", + "then": "pin:#F7BB59" + }, + { + "if": "status=3", + "then": "pin:#F7BB59" + }, + { + "if": "status=4", + "then": "pin:#737373" + }, + { + "if": "status=5", + "then": "pin:#CCB186" + }, + { + "if": "status=6", + "then": "pin:#FF5E63" + }, + { + "if": "status=9", + "then": "pin:#FF349C" + } + ] + }, + "iconSize": "40,40,center" + } + ], + "tagRenderings": [ + { + "id": "link", + "render": "View this task" + }, + { + "id": "status", + "render": "Current status: {status}", + "mappings": [ + { + "if": "status=0", + "then": { + "en": "Task is created" + } + }, + { + "if": "status=1", + "then": { + "en": "Task is fixed" + } + }, + { + "if": "status=2", + "then": { + "en": "Task is a false positive" + } + }, + { + "if": "status=3", + "then": { + "en": "Task is skipped" + } + }, + { + "if": "status=4", + "then": { + "en": "Task is deleted" + } + }, + { + "if": "status=5", + "then": { + "en": "Task is already fixed" + } + }, + { + "if": "status=6", + "then": { + "en": "Task is marked as too hard" + } + }, + { + "if": "status=9", + "then": { + "en": "Task is disabled" + } + } + ] + }, + { + "id": "blurb", + "condition": "blurb~*", + "render": "{blurb}" + } + ], + "description": { + "en": "Layer showing all tasks in MapRoulette" + }, + "minzoom": 15, + "name": { + "en": "MapRoulette Tasks" + }, + "title": { + "render": { + "en": "MapRoulette Item: {parentName}" + } + }, + "filter": [ + { + "id": "status", + "options": [ + { + "question": { + "en": "Show tasks with all statuses" + } + }, + { + "question": { + "en": "Show tasks that are created" + }, + "osmTags": "status=0" + }, + { + "question": { + "en": "Show tasks that are fixed" + }, + "osmTags": "status=1" + }, + { + "question": { + "en": "Show tasks that are false positives" + }, + "osmTags": "status=2" + }, + { + "question": { + "en": "Show tasks that are skipped" + }, + "osmTags": "status=3" + }, + { + "question": { + "en": "Show tasks that are deleted" + }, + "osmTags": "status=4" + }, + { + "question": { + "en": "Show tasks that are already fixed" + }, + "osmTags": "status=5" + }, + { + "question": { + "en": "Show tasks that are marked as too hard" + }, + "osmTags": "status=6" + }, + { + "question": { + "en": "Show tasks that are disabled" + }, + "osmTags": "status=9" + } + ] + } + ] +} \ No newline at end of file diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index 8fc630f67..f02bc13de 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -219,6 +219,10 @@ "if": "theme=mapcomplete-changes", "then": "./assets/svg/logo.svg" }, + { + "if": "theme=maproulette", + "then": "./assets/layers/maproulette/logomark.svg" + }, { "if": "theme=maps", "then": "./assets/themes/maps/logo.svg" diff --git a/assets/themes/maproulette/maproulette.json b/assets/themes/maproulette/maproulette.json index 56b0c5ace..3695f3eda 100644 --- a/assets/themes/maproulette/maproulette.json +++ b/assets/themes/maproulette/maproulette.json @@ -8,138 +8,12 @@ }, "version": "1.0.0", "hideFromOverview": true, - "icon": "./assets/svg/bug.svg", + "icon": "./assets/layers/maproulette/logomark.svg", "maintainer": "", "startLat": 0, "startLon": 0, "startZoom": 4, "layers": [ - { - "id": "maproulette", - "source": { - "geoJson": "https://maproulette.org/api/v2/tasks/box/{x_min}/{y_min}/{x_max}/{y_max}", - "geoJsonZoomLevel": 16, - "osmTags": "id~*" - }, - "mapRendering": [ - { - "location": [ - "point", - "centroid" - ], - "icon": { - "render": "pin:white", - "mappings": [ - { - "if": "status=0", - "then": "pin:#959DFF" - }, - { - "if": "status=1", - "then": "pin:#65D2DA" - }, - { - "if": "status=2", - "then": "pin:#F7BB59" - }, - { - "if": "status=3", - "then": "pin:#F7BB59" - }, - { - "if": "status=4", - "then": "pin:#737373" - }, - { - "if": "status=5", - "then": "pin:#CCB186" - }, - { - "if": "status=6", - "then": "pin:#FF5E63" - }, - { - "if": "status=9", - "then": "pin:#FF349C" - } - ] - }, - "iconSize": "40,40,center" - } - ], - "tagRenderings": [ - { - "id": "link", - "render": "View this task" - }, - { - "id": "status", - "render": "Current status: {status}", - "mappings": [ - { - "if": "status=0", - "then": { - "en": "Task is created" - } - }, - { - "if": "status=1", - "then": { - "en": "Task is fixed" - } - }, - { - "if": "status=2", - "then": { - "en": "Task is a false positive" - } - }, - { - "if": "status=3", - "then": { - "en": "Task is skipped" - } - }, - { - "if": "status=4", - "then": { - "en": "Task is deleted" - } - }, - { - "if": "status=5", - "then": { - "en": "Task is already fixed" - } - }, - { - "if": "status=6", - "then": { - "en": "Task is marked as too hard" - } - }, - { - "if": "status=9", - "then": { - "en": "Task is disabled" - } - } - ] - }, - "all_tags" - ], - "description": { - "en": "Layer showing all tasks in MapRoulette" - }, - "minzoom": 15, - "name": { - "en": "MapRoulette Tasks" - }, - "title": { - "render": { - "en": "MapRoulette Item: {parentName}" - } - } - } + "maproulette" ] } \ No newline at end of file From a6583f9934a416b1850ee160d3fa18f88bf51888 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 10 Jul 2022 19:43:35 +0000 Subject: [PATCH 04/23] Added description, add extra filters --- assets/layers/maproulette/maproulette.json | 32 ++++++++++++++++++++++ assets/themes/maproulette/maproulette.json | 4 +-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/assets/layers/maproulette/maproulette.json b/assets/layers/maproulette/maproulette.json index 3daf86389..4ab2265b4 100644 --- a/assets/layers/maproulette/maproulette.json +++ b/assets/layers/maproulette/maproulette.json @@ -186,6 +186,38 @@ "osmTags": "status=9" } ] + }, + { + "id": "parent-name", + "options": [ + { + "osmTags": "parentName~i~.*{search}.*", + "fields": [ + { + "name": "search" + } + ], + "question": { + "en": "Challenge name contains {search}" + } + } + ] + }, + { + "id": "parent-id", + "options": [ + { + "osmTags": "parentId={search}", + "fields": [ + { + "name": "search" + } + ], + "question": { + "en": "Challenge ID matches {search}" + } + } + ] } ] } \ No newline at end of file diff --git a/assets/themes/maproulette/maproulette.json b/assets/themes/maproulette/maproulette.json index 3695f3eda..9c0ca8f7d 100644 --- a/assets/themes/maproulette/maproulette.json +++ b/assets/themes/maproulette/maproulette.json @@ -1,10 +1,10 @@ { "id": "maproulette", "title": { - "en": "MapRoulette Test" + "en": "MapRoulette Tasks" }, "description": { - "en": "MapRoulette Test" + "en": "Theme showing MapRoulette tasks, allowing you to search, filter and fix them." }, "version": "1.0.0", "hideFromOverview": true, From b4e2c69568495d95f70fa67c85a47d2e317271a4 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Sun, 10 Jul 2022 19:47:17 +0000 Subject: [PATCH 05/23] Generate translations, format --- assets/layers/bench/bench.json | 2 +- .../layers/climbing_area/climbing_area.json | 3 +- .../climbing_opportunity.json | 3 +- assets/layers/food/food.json | 6 +- assets/layers/maxspeed/maxspeed.json | 18 ++-- assets/layers/shops/shops.json | 6 +- langs/layers/de.json | 6 +- langs/layers/en.json | 90 ++++++++++++++++++- langs/layers/nl.json | 27 +++--- langs/themes/en.json | 4 + 10 files changed, 137 insertions(+), 28 deletions(-) diff --git a/assets/layers/bench/bench.json b/assets/layers/bench/bench.json index ea2062d4f..851a43b84 100644 --- a/assets/layers/bench/bench.json +++ b/assets/layers/bench/bench.json @@ -711,7 +711,7 @@ } ], "description": { - "nl": "Deze laag toont zitbanken en enkele vragen over deze zitbanken", + "nl": "Een zitbank is een houten, metalen, stenen, … oppervlak waar een mens kan zitten. Deze laag toont ze en stelt er enkele vragen over.", "en": "A bench is a wooden, metal, stone, … surface where a human can sit. This layers visualises them and asks a few questions about them.", "fr": "Un banc est une surface en bois, métal, pierre… sur laquelle un humain peut s'asseoir. Cette couche permet de les visualiser et pose des questions à leur sujet.", "de": "Diese Karte stellt Sitzbänke aus Holz, Metall, Stein, … dar und stellt ein paar Fragen, um weitere Informationen zu ergänzen.", diff --git a/assets/layers/climbing_area/climbing_area.json b/assets/layers/climbing_area/climbing_area.json index ab75354ba..ffa386cbb 100644 --- a/assets/layers/climbing_area/climbing_area.json +++ b/assets/layers/climbing_area/climbing_area.json @@ -141,7 +141,8 @@ "en": "

Contains {_contained_climbing_routes_count} routes

    {_contained_climbing_routes}
", "fr": "

Contient {_contained_climbing_routes_count} voies

    {_contained_climbing_routes}
", "it": "

Contiene {_contained_climbing_routes_count} vie

    {_contained_climbing_routes}
", - "de": "

Enthält {_contained_climbing_routes_count} Routen

    {_contained_climbing_routes}
" + "de": "

Enthält {_contained_climbing_routes_count} Routen

    {_contained_climbing_routes}
", + "nl": "

Bevat {_contained_climbing_routes_count} routes

    {_contained_climbing_routes}
" }, "condition": "_contained_climbing_routes~*", "id": "Contained_climbing_routes" diff --git a/assets/layers/climbing_opportunity/climbing_opportunity.json b/assets/layers/climbing_opportunity/climbing_opportunity.json index 107ec35ab..ecdba430b 100644 --- a/assets/layers/climbing_opportunity/climbing_opportunity.json +++ b/assets/layers/climbing_opportunity/climbing_opportunity.json @@ -66,7 +66,8 @@ "ja": "ここで登坂はできますか?", "nb_NO": "Er klatring mulig her?", "fr": "Est-il possible d’escalader ici ?", - "it": "È possibile arrampicarsi qua?" + "it": "È possibile arrampicarsi qua?", + "nl": "Is klimmen hier mogelijk?" }, "mappings": [ { diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index 7289dfe15..107503c96 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -792,7 +792,8 @@ "question": { "en": "Accepts cash", "de": "Akzeptiert Bargeld", - "es": "Acepta efectivo" + "es": "Acepta efectivo", + "nl": "Accepteert cash" } } ] @@ -805,7 +806,8 @@ "question": { "en": "Accepts payment cards", "de": "Akzeptiert Kartenzahlung", - "es": "Acepta tarjetas de pago" + "es": "Acepta tarjetas de pago", + "nl": "Accepteert betaalkaarten" } } ] diff --git a/assets/layers/maxspeed/maxspeed.json b/assets/layers/maxspeed/maxspeed.json index dbf5d65f9..ca0ff2dcb 100644 --- a/assets/layers/maxspeed/maxspeed.json +++ b/assets/layers/maxspeed/maxspeed.json @@ -42,20 +42,23 @@ "if": "name=", "then": { "en": "Road without a name", - "de": "Straße ohne Namen" + "de": "Straße ohne Namen", + "nl": "Weg zonder een naam" } } ] }, "description": { "en": "Shows the allowed speed for every road", - "de": "Zeigt die zulässige Geschwindigkeit für jede Straße an" + "de": "Zeigt die zulässige Geschwindigkeit für jede Straße an", + "nl": "Toont de toegestane snelheid voor elke weg" }, "tagRenderings": [ { "render": { "en": "The maximum allowed speed on this road is {maxspeed}", - "de": "Die zulässige Höchstgeschwindigkeit auf dieser Straße ist {maxspeed}" + "de": "Die zulässige Höchstgeschwindigkeit auf dieser Straße ist {maxspeed}", + "nl": "De maximum toegestane snelheid op deze weg is {maxspeed}" }, "question": { "es": "Qué velocidad tiene", @@ -70,10 +73,15 @@ "mappings": [ { "if": { - "and":["highway=living_street","_country!=be"] + "and": [ + "highway=living_street", + "_country!=be" + ] }, "then": { - "en": "This is a living street" + "en": "This is a living street, which has a maxspeed of 20km/h", + "de": "Dies ist eine Wohnstraße, auf der eine Höchstgeschwindigkeit von 20 km/h gilt", + "nl": "Dit is een woonerf en heeft dus een maximale snelheid van 20km/h" }, "icon": { "path": "./assets/layers/maxspeed/living_street_be.svg", diff --git a/assets/layers/shops/shops.json b/assets/layers/shops/shops.json index dd8538c8f..341fd4c0d 100644 --- a/assets/layers/shops/shops.json +++ b/assets/layers/shops/shops.json @@ -107,11 +107,13 @@ "nl": "Wat voor soort winkel is dit?" }, "render": { - "en":"This is a {shop}" + "en": "This is a {shop}" }, "freeform": { "key": "shop", - "addExtraTags": ["fixme=freeform shop key used, to be reviewed"] + "addExtraTags": [ + "fixme=freeform shop key used, to be reviewed" + ] } } }, diff --git a/langs/layers/de.json b/langs/layers/de.json index 525455f6d..2d686ca62 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -4276,6 +4276,9 @@ "mappings": { "0": { "then": "Dies ist eine Wohnstraße, auf der eine Höchstgeschwindigkeit von 20 km/h gilt" + }, + "1": { + "then": "Dies ist eine Wohnstraße, auf der eine Höchstgeschwindigkeit von 20 km/h gilt" } }, "question": "Wie hoch ist die zulässige Höchstgeschwindigkeit, die man auf dieser Straße fahren darf?", @@ -5281,9 +5284,6 @@ "tagRenderings": { "shops-name": { "question": "Wie ist der Name dieses Geschäfts?" - }, - "shops-type-from-id": { - "question": "Was ist das für ein Geschäft?" } }, "title": { diff --git a/langs/layers/en.json b/langs/layers/en.json index 0b3b8a767..305bfde70 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4334,6 +4334,90 @@ "render": "Map" } }, + "maproulette": { + "description": "Layer showing all tasks in MapRoulette", + "filter": { + "0": { + "options": { + "0": { + "question": "Show tasks with all statuses" + }, + "1": { + "question": "Show tasks that are created" + }, + "2": { + "question": "Show tasks that are fixed" + }, + "3": { + "question": "Show tasks that are false positives" + }, + "4": { + "question": "Show tasks that are skipped" + }, + "5": { + "question": "Show tasks that are deleted" + }, + "6": { + "question": "Show tasks that are already fixed" + }, + "7": { + "question": "Show tasks that are marked as too hard" + }, + "8": { + "question": "Show tasks that are disabled" + } + } + }, + "1": { + "options": { + "0": { + "question": "Challenge name contains {search}" + } + } + }, + "2": { + "options": { + "0": { + "question": "Challenge ID matches {search}" + } + } + } + }, + "name": "MapRoulette Tasks", + "tagRenderings": { + "status": { + "mappings": { + "0": { + "then": "Task is created" + }, + "1": { + "then": "Task is fixed" + }, + "2": { + "then": "Task is a false positive" + }, + "3": { + "then": "Task is skipped" + }, + "4": { + "then": "Task is deleted" + }, + "5": { + "then": "Task is already fixed" + }, + "6": { + "then": "Task is marked as too hard" + }, + "7": { + "then": "Task is disabled" + } + } + } + }, + "title": { + "render": "MapRoulette Item: {parentName}" + } + }, "maxspeed": { "description": "Shows the allowed speed for every road", "name": "Maxspeed", @@ -4342,6 +4426,9 @@ "mappings": { "0": { "then": "This is a living street, which has a maxspeed of 20km/h" + }, + "1": { + "then": "This is a living street, which has a maxspeed of 20km/h" } }, "question": "What is the legal maximum speed one is allowed to drive on this road?", @@ -5402,7 +5489,8 @@ "tagRenderings": { "2": { "override": { - "question": "What kind of shop is this?" + "question": "What kind of shop is this?", + "render": "This is a {shop}" } }, "copyshop-print-sizes": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 9613c44f3..db44efd9a 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -2438,6 +2438,9 @@ "Contained routes length hist": { "render": "

Overzicht van lengtes

{histogram(_length_hist)}" }, + "Contained_climbing_routes": { + "render": "

Bevat {_contained_climbing_routes_count} routes

    {_contained_climbing_routes}
" + }, "Rock type (crag/rock/cliff only)": { "mappings": { "0": { @@ -2455,9 +2458,6 @@ }, "question": "Wat is de naam van dit Klimgelegenheid?", "render": "{name}" - }, - "Contained_climbing_routes": { - "render": "

Bevat {_contained_climbing_routes_count} routes

    {_contained_climbing_routes}
" } }, "title": { @@ -4182,16 +4182,27 @@ } }, "maxspeed": { + "description": "Toont de toegestane snelheid voor elke weg", "tagRenderings": { "maxspeed-maxspeed": { "mappings": { "0": { "then": "Dit is een woonerf en heeft dus een maximale snelheid van 20km/h" + }, + "1": { + "then": "Dit is een woonerf en heeft dus een maximale snelheid van 20km/h" } }, "render": "De maximum toegestane snelheid op deze weg is {maxspeed}" } }, + "title": { + "mappings": { + "0": { + "then": "Weg zonder een naam" + } + } + }, "units": { "0": { "applicableUnits": { @@ -4205,14 +4216,6 @@ } } } - }, - "description": "Toont de toegestane snelheid voor elke weg", - "title": { - "mappings": { - "0": { - "then": "Weg zonder een naam" - } - } } }, "nature_reserve": { @@ -6431,4 +6434,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/themes/en.json b/langs/themes/en.json index b3e3d3a17..7b3cd083c 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -717,6 +717,10 @@ "shortDescription": "Shows changes made by MapComplete", "title": "Changes made with MapComplete" }, + "maproulette": { + "description": "Theme showing MapRoulette tasks, allowing you to search, filter and fix them.", + "title": "MapRoulette Tasks" + }, "maps": { "description": "On this map you can find all maps OpenStreetMap knows - typically a big map on an information board showing the area, city or region, e.g. a tourist map on the back of a billboard, a map of a nature reserve, a map of cycling networks in the region, ...)

If a map is missing, you can easily map this map on OpenStreetMap.", "shortDescription": "This theme shows all (touristic) maps that OpenStreetMap knows of", From e01ad24bd2c2595d19af62e4eee17caecc9e507f Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Mon, 11 Jul 2022 07:36:41 +0000 Subject: [PATCH 06/23] Update size --- assets/layers/maproulette/logomark.svg | 85 +++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/assets/layers/maproulette/logomark.svg b/assets/layers/maproulette/logomark.svg index 4ad7dd319..0019edeae 100644 --- a/assets/layers/maproulette/logomark.svg +++ b/assets/layers/maproulette/logomark.svg @@ -1,10 +1,77 @@ - - - - - + + + + + + image/svg+xml + + + + + + + + + + + + - - - - \ No newline at end of file + + + + From a2a49d5d7de5502496b6311ae914699002b5564b Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Tue, 12 Jul 2022 07:42:26 +0000 Subject: [PATCH 07/23] Completer URL --- Logic/FeatureSource/Sources/GeoJsonSource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Logic/FeatureSource/Sources/GeoJsonSource.ts b/Logic/FeatureSource/Sources/GeoJsonSource.ts index 1deca987c..3c4e9142d 100644 --- a/Logic/FeatureSource/Sources/GeoJsonSource.ts +++ b/Logic/FeatureSource/Sources/GeoJsonSource.ts @@ -84,7 +84,7 @@ export default class GeoJsonSource implements FeatureSourceForLayer, Tiled { self.state.setData("loaded") // TODO: move somewhere else, just for testing // Check for maproulette data - if (url.startsWith("https://maproulette.org/api/v2/")) { + if (url.startsWith("https://maproulette.org/api/v2/tasks/box/")) { console.log("MapRoulette data detected") const data = json; let maprouletteFeatures: any[] = []; From 65997291bbdd14f2f95508c05694b6f5e5cbf8c6 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Wed, 13 Jul 2022 08:03:09 +0000 Subject: [PATCH 08/23] Allow closing Maproulette tasks --- Docs/SpecialRenderings.md | 3 +- Logic/Maproulette.ts | 39 +++ Logic/State/UserRelatedState.ts | 8 + Models/Constants.ts | 2 + UI/Popup/ImportButton.ts | 20 +- assets/layers/doctors/doctors.json | 286 +++++++++--------- .../rainbow_crossings/rainbow_crossings.json | 17 +- assets/themes/onwheels/onwheels.json | 12 +- .../rainbow_crossings/rainbow_crossings.json | 8 +- .../street_lighting_assen.json | 37 +++ langs/layers/en.json | 34 +++ langs/themes/en.json | 8 + 12 files changed, 309 insertions(+), 165 deletions(-) create mode 100644 Logic/Maproulette.ts diff --git a/Docs/SpecialRenderings.md b/Docs/SpecialRenderings.md index c4ed606d5..d2419f526 100644 --- a/Docs/SpecialRenderings.md +++ b/Docs/SpecialRenderings.md @@ -347,11 +347,12 @@ snap_onto_layers | _undefined_ | If a way of the given layer(s) is closeby, will max_snap_distance | 5 | The maximum distance that the imported point will be moved to snap onto a way in an already existing layer (in meters). This is previewed to the contributor, similar to the 'add new point'-action of MapComplete note_id | _undefined_ | If given, this key will be read. The corresponding note on OSM will be closed, stating 'imported' location_picker | photo | Chooses the background for the precise location picker, options are 'map', 'photo' or 'osmbasedmap' or 'none' if the precise input picker should be disabled +maproulette_id | _undefined_ | If given, the maproulette challenge will be marked as fixed #### Example usage of import_button - `{import_button(,,Import this data into OpenStreetMap,./assets/svg/addSmall.svg,,5,,photo)}` + `{import_button(,,Import this data into OpenStreetMap,./assets/svg/addSmall.svg,,5,,photo,)}` diff --git a/Logic/Maproulette.ts b/Logic/Maproulette.ts new file mode 100644 index 000000000..470bb75d8 --- /dev/null +++ b/Logic/Maproulette.ts @@ -0,0 +1,39 @@ +import Constants from "../Models/Constants"; + +export default class Maproulette { + /** + * The API endpoint to use + */ + endpoint: string; + + /** + * The API key to use for all requests + */ + private apiKey: string; + + /** + * Creates a new Maproulette instance + * @param endpoint The API endpoint to use + */ + constructor(endpoint: string = "https://maproulette.org/api/v2") { + this.endpoint = endpoint; + this.apiKey = Constants.MaprouletteApiKey; + } + + /** + * Close a task + * @param taskId The task to close + */ + async closeTask(taskId: number): Promise { + const response = await fetch(`${this.endpoint}/task/${taskId}/1`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + "apiKey": this.apiKey, + }, + }); + if (response.status !== 304) { + console.log(`Failed to close task: ${response.status}`); + } + } +} diff --git a/Logic/State/UserRelatedState.ts b/Logic/State/UserRelatedState.ts index 8fbd9c5df..0fc43ebc8 100644 --- a/Logic/State/UserRelatedState.ts +++ b/Logic/State/UserRelatedState.ts @@ -13,6 +13,7 @@ import ChangeToElementsActor from "../Actors/ChangeToElementsActor"; import PendingChangesUploader from "../Actors/PendingChangesUploader"; import * as translators from "../../assets/translators.json" import {post} from "jquery"; +import Maproulette from "../Maproulette"; /** * The part of the state which keeps track of user-related stuff, e.g. the OSM-connection, @@ -34,6 +35,11 @@ export default class UserRelatedState extends ElementsState { */ public mangroveIdentity: MangroveIdentity; + /** + * Maproulette connection + */ + public maprouletteConnection: Maproulette; + public readonly isTranslator : Store; public readonly installedUserThemes: Store @@ -80,6 +86,8 @@ export default class UserRelatedState extends ElementsState { this.osmConnection.GetLongPreference("identity", "mangrove") ); + this.maprouletteConnection = new Maproulette(); + if (layoutToUse?.hideFromOverview) { this.osmConnection.isLoggedIn.addCallbackAndRunD(loggedIn => { if (loggedIn) { diff --git a/Models/Constants.ts b/Models/Constants.ts index b10e8b872..b296af714 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -6,6 +6,8 @@ export default class Constants { public static ImgurApiKey = '7070e7167f0a25a' public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" + // Currently there is no user-friendly way to get the user's API key. See https://github.com/maproulette/maproulette2/issues/476 for more information. + public static readonly MaprouletteApiKey = ""; public static defaultOverpassUrls = [ // The official instance, 10000 queries per day per project allowed diff --git a/UI/Popup/ImportButton.ts b/UI/Popup/ImportButton.ts index e72d60129..42f9241f3 100644 --- a/UI/Popup/ImportButton.ts +++ b/UI/Popup/ImportButton.ts @@ -550,15 +550,21 @@ export class ImportPointButton extends AbstractImportButton { name: "note_id", doc: "If given, this key will be read. The corresponding note on OSM will be closed, stating 'imported'" }, - {name:"location_picker", + { + name:"location_picker", defaultValue: "photo", - doc: "Chooses the background for the precise location picker, options are 'map', 'photo' or 'osmbasedmap' or 'none' if the precise input picker should be disabled"}], + doc: "Chooses the background for the precise location picker, options are 'map', 'photo' or 'osmbasedmap' or 'none' if the precise input picker should be disabled" + }, + { + name: "maproulette_id", + doc: "If given, the maproulette challenge will be marked as fixed" + }], { showRemovedTags: false} ) } private static createConfirmPanelForPoint( - args: { max_snap_distance: string, snap_onto_layers: string, icon: string, text: string, newTags: UIEventSource, targetLayer: string, note_id: string }, + args: { max_snap_distance: string, snap_onto_layers: string, icon: string, text: string, newTags: UIEventSource, targetLayer: string, note_id: string, maproulette_id: string }, state: FeaturePipelineState, guiState: DefaultGuiState, originalFeatureTags: UIEventSource, @@ -600,6 +606,14 @@ export class ImportPointButton extends AbstractImportButton { originalFeatureTags.data["closed_at"] = new Date().toISOString() originalFeatureTags.ping() } + + let maproulette_id = originalFeatureTags.data[args.maproulette_id]; + console.log("Checking if we need to mark a maproulette challenge as fixed (" + maproulette_id + ")") + if (maproulette_id !== undefined) { + // Fetch MapRoulette API key, then use it to mark the challenge as fixed + console.log("Marking maproulette challenge as fixed") + state.maprouletteConnection.closeTask(Number(maproulette_id)); + } } let preciseInputOption = args["location_picker"] diff --git a/assets/layers/doctors/doctors.json b/assets/layers/doctors/doctors.json index 8979e43c0..eefedaf8d 100644 --- a/assets/layers/doctors/doctors.json +++ b/assets/layers/doctors/doctors.json @@ -1,147 +1,147 @@ { - "id": "doctors", - "name": { - "en": "doctors" + "id": "doctors", + "name": { + "en": "doctors" + }, + "description": { + "en": "This layer shows doctor offices, dentists and other healthcare facilities" + }, + "source": { + "osmTags": { + "or": [ + "amenity=doctors", + "amenity=dentist", + "healthcare=physiotherapist" + ] + } + }, + "title": { + "render": { + "en": "Doctors Office {name}" }, - "description": { - "en": "This layer shows doctor offices, dentists and other healthcare facilities" - }, - "source": { - "osmTags": { - "or": [ - "amenity=doctors", - "amenity=dentist", - "healthcare=physiotherapist" - ] - } - }, - "title": { - "render": { - "en": "Doctors Office {name}" - }, - "mappings": [ - { - "if": "amenity=doctors", - "then": "Doctors Office {name}" - }, - { - "if": "amenity=dentist", - "then": "Dentists office {name}" - }, - { - "if": "healthcare=physiotherapist", - "then": "Physiotherapists office {name}" - } - ] - }, - "minzoom": 13, - "tagRenderings": [ - "images", - "opening_hours", - "phone", - "email", - "website", - { - "question": { - "en": "What is the name of this doctors place?" - }, - "render": { - "en": "This doctors place is called {name}" - }, - "freeform": { - "key": "name" - }, - "id": "name" - }, - { - "condition": "amenity=doctors", - "id": "specialty", - "render": { - "en": "This doctor is specialized in {healthcare:speciality}" - }, - "question": { - "en": "What is this doctor specialized in?" - }, - "freeform": { - "key": "healthcare:speciality" - }, - "mappings": [ - { - "if": "healthcare:speciality=general", - "then": { - "en": "This is a general practitioner" - } - }, - { - "if": "healthcare:speciality=gynaecology", - "then": { - "en": "This is a gynaecologist" - } - }, - { - "if": "healthcare:speciality=psychiatry", - "then": { - "en": "This is a psychiatrist" - } - }, - { - "if": "healthcare:speciality=paediatrics", - "then": { - "en": "This is a paediatrician" - } - } - ] - } - ], - "presets": [ - { - "title": { - "en": "a doctors office" - }, - "tags": [ - "amenity=doctors" - ] - }, - { - "title": { - "en": "a dentists office" - }, - "tags": [ - "amenity=dentist" - ] - }, - { - "title": { - "en": "a physiotherapists office" - }, - "tags": [ - "healthcare=physiotherapist" - ] - } - ], - "filter": [ - { - "id": "opened-now", - "options": [ - { - "question": { - "en": "Opened now" - }, - "osmTags": "_isOpen=yes" - } - ] - } - ], - "mapRendering": [ - { - "icon": { - "render": "circle:white;./assets/layers/doctors/doctors.svg" - }, - "iconSize": "40,40,center", - "location": [ - "point", - "centroid" - ] - } + "mappings": [ + { + "if": "amenity=doctors", + "then": "Doctors Office {name}" + }, + { + "if": "amenity=dentist", + "then": "Dentists office {name}" + }, + { + "if": "healthcare=physiotherapist", + "then": "Physiotherapists office {name}" + } ] + }, + "minzoom": 13, + "tagRenderings": [ + "images", + "opening_hours", + "phone", + "email", + "website", + { + "question": { + "en": "What is the name of this doctors place?" + }, + "render": { + "en": "This doctors place is called {name}" + }, + "freeform": { + "key": "name" + }, + "id": "name" + }, + { + "condition": "amenity=doctors", + "id": "specialty", + "render": { + "en": "This doctor is specialized in {healthcare:speciality}" + }, + "question": { + "en": "What is this doctor specialized in?" + }, + "freeform": { + "key": "healthcare:speciality" + }, + "mappings": [ + { + "if": "healthcare:speciality=general", + "then": { + "en": "This is a general practitioner" + } + }, + { + "if": "healthcare:speciality=gynaecology", + "then": { + "en": "This is a gynaecologist" + } + }, + { + "if": "healthcare:speciality=psychiatry", + "then": { + "en": "This is a psychiatrist" + } + }, + { + "if": "healthcare:speciality=paediatrics", + "then": { + "en": "This is a paediatrician" + } + } + ] + } + ], + "presets": [ + { + "title": { + "en": "a doctors office" + }, + "tags": [ + "amenity=doctors" + ] + }, + { + "title": { + "en": "a dentists office" + }, + "tags": [ + "amenity=dentist" + ] + }, + { + "title": { + "en": "a physiotherapists office" + }, + "tags": [ + "healthcare=physiotherapist" + ] + } + ], + "filter": [ + { + "id": "opened-now", + "options": [ + { + "question": { + "en": "Opened now" + }, + "osmTags": "_isOpen=yes" + } + ] + } + ], + "mapRendering": [ + { + "icon": { + "render": "circle:white;./assets/layers/doctors/doctors.svg" + }, + "iconSize": "40,40,center", + "location": [ + "point", + "centroid" + ] + } + ] } \ No newline at end of file diff --git a/assets/layers/rainbow_crossings/rainbow_crossings.json b/assets/layers/rainbow_crossings/rainbow_crossings.json index 35c99d8d9..d13fe3c03 100644 --- a/assets/layers/rainbow_crossings/rainbow_crossings.json +++ b/assets/layers/rainbow_crossings/rainbow_crossings.json @@ -7,8 +7,7 @@ "en": "A layer showing pedestrian crossings with rainbow paintings" }, "source": { - "osmTags": - "highway=crossing" + "osmTags": "highway=crossing" }, "minzoom": 17, "title": { @@ -38,7 +37,7 @@ ], "tagRenderings": [ "images", - { + { "id": "crossing-with-rainbow", "question": { "en": "Does this crossing has rainbow paintings?" @@ -77,10 +76,12 @@ { "icon": { "render": "./assets/themes/rainbow_crossings/crossing.svg", - "mappings": [{ - "if": "crossing:marking=rainbow", - "then": "./assets/themes/rainbow_crossings/logo.svg" - }] + "mappings": [ + { + "if": "crossing:marking=rainbow", + "then": "./assets/themes/rainbow_crossings/logo.svg" + } + ] }, "iconSize": "40,40,center", "location": [ @@ -89,4 +90,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/assets/themes/onwheels/onwheels.json b/assets/themes/onwheels/onwheels.json index 881729e71..920ce1e1c 100644 --- a/assets/themes/onwheels/onwheels.json +++ b/assets/themes/onwheels/onwheels.json @@ -1,10 +1,10 @@ { "id": "onwheels", "title": { - "en": "OnWheels" + "en": "OnWheels" }, "description": { - "en": "On this map, publicly weelchair accessible places are shown and can be easily added" + "en": "On this map, publicly weelchair accessible places are shown and can be easily added" }, "maintainer": "MapComplete", "icon": "./assets/themes/onwheels/crest.svg", @@ -29,11 +29,11 @@ "viewpoint", "doctors" ], - "overrideAll" : { - "minzoom" : "15", - "mapRendering" : [ + "overrideAll": { + "minzoom": "15", + "mapRendering": [ { - "label" : null + "label": null } ] } diff --git a/assets/themes/rainbow_crossings/rainbow_crossings.json b/assets/themes/rainbow_crossings/rainbow_crossings.json index 32fe470a3..1a63df6fc 100644 --- a/assets/themes/rainbow_crossings/rainbow_crossings.json +++ b/assets/themes/rainbow_crossings/rainbow_crossings.json @@ -24,7 +24,9 @@ "=presets": [], "source": { "osmTags": { - "and+": ["crossing:marking=rainbow"] + "and+": [ + "crossing:marking=rainbow" + ] } } } @@ -38,6 +40,4 @@ } } ] -} - - +} \ No newline at end of file diff --git a/assets/themes/street_lighting/street_lighting_assen.json b/assets/themes/street_lighting/street_lighting_assen.json index 646a82550..0ca7388ed 100644 --- a/assets/themes/street_lighting/street_lighting_assen.json +++ b/assets/themes/street_lighting/street_lighting_assen.json @@ -51,6 +51,43 @@ "tagRenderings": [ "all_tags" ] + }, + { + "id": "maproulette", + "name": "Maproulette Tasks", + "source": { + "osmTags": "id~*", + "geoJson": "https://maproulette.org/api/v2/challenge/view/27971", + "isOsmCache": false + }, + "calculatedTags": [ + "_closest_osm_street_lamp=feat.closest('street_lamps')?.properties?.id", + "_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 Maproulette", + "mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": "circle:black", + "iconSize": "20,20,center" + } + ], + "tagRenderings": [ + "all_tags", + { + "id": "link", + "render": "View this task" + }, + { + "id": "import", + + "render": "{import_button(street_lamps,tags,Import,./assets/svg/addSmall.svg,,,,photo,mr_taskId)}" + } + ] } ], "hideFromOverview": true diff --git a/langs/layers/en.json b/langs/layers/en.json index 305bfde70..764a8669d 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -3256,6 +3256,7 @@ "name": "Direction visualization" }, "doctors": { + "description": "This layer shows doctor offices, dentists and other healthcare facilities", "filter": { "0": { "options": { @@ -3278,6 +3279,10 @@ } }, "tagRenderings": { + "name": { + "question": "What is the name of this doctors place?", + "render": "This doctors place is called {name}" + }, "specialty": { "mappings": { "0": { @@ -5074,6 +5079,35 @@ "render": "Bookcase" } }, + "rainbow_crossings": { + "description": "A layer showing pedestrian crossings with rainbow paintings", + "name": "Crossings with rainbow paintings", + "presets": { + "0": { + "description": "Pedestrian crossing", + "title": "a crossing" + } + }, + "tagRenderings": { + "crossing-with-rainbow": { + "mappings": { + "0": { + "then": "This crossing has rainbow paintings" + }, + "1": { + "then": "No rainbow paintings here" + }, + "2": { + "then": "No rainbow paintings here" + } + }, + "question": "Does this crossing has rainbow paintings?" + } + }, + "title": { + "render": "Crossing" + } + }, "recycling": { "description": "A layer with recycling containers and centres", "filter": { diff --git a/langs/themes/en.json b/langs/themes/en.json index 7b3cd083c..011d63f1b 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -745,6 +745,10 @@ "shortDescription": "Publicly accessible towers to enjoy the view", "title": "Observation towers" }, + "onwheels": { + "description": "On this map, publicly weelchair accessible places are shown and can be easily added", + "title": "OnWheels" + }, "openwindpowermap": { "description": "A map for showing and editing wind turbines.", "title": "OpenWindPowerMap" @@ -867,6 +871,10 @@ "shortDescription": "A map showing postboxes and post offices", "title": "Postbox and Post Office Map" }, + "rainbow_crossings": { + "description": "On this map, rainbow-painted pedestrian crossings are shown and can be easily added", + "title": "Rainbow pedestrian crossings" + }, "shops": { "description": "On this map, one can mark basic information about shops, add opening hours and phone numbers", "shortDescription": "An editable map with basic shop information", From 3f2d50b2e18cc1f8562699b1c9897b4708598f51 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Wed, 13 Jul 2022 08:59:02 +0000 Subject: [PATCH 09/23] Split out stuff --- .../maproulette_challenge.json | 190 ++++++++++++++++++ .../street_lighting_assen.json | 49 ++--- 2 files changed, 204 insertions(+), 35 deletions(-) create mode 100644 assets/layers/maproulette_challenge/maproulette_challenge.json diff --git a/assets/layers/maproulette_challenge/maproulette_challenge.json b/assets/layers/maproulette_challenge/maproulette_challenge.json new file mode 100644 index 000000000..23060925b --- /dev/null +++ b/assets/layers/maproulette_challenge/maproulette_challenge.json @@ -0,0 +1,190 @@ +{ + "id": "maproulette_challenge", + "name": null, + "description": { + "en": "Layer showing tasks of a MapRoulette challenge" + }, + "source": { + "osmTags": "id~*", + "geoJson": "https://maproulette.org/api/v2/challenge/view/27971", + "isOsmCache": false + }, + "title": { + "render": { + "en": "Item in MapRoulette" + } + }, + "titleIcons":[ + { + "id": "maproulette", + "render": "" + } + ], + "mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": { + "render": "./assets/layers/maproulette/logomark.svg", + "mappings": [ + { + "if": "mr_taskStatus=Created", + "then": "pin:#959DFF" + }, + { + "if": "mr_taskStatus=Fixed", + "then": "pin:#65D2DA" + }, + { + "if": "mr_taskStatus=False positive", + "then": "pin:#F7BB59" + }, + { + "if": "mr_taskStatus=Skipped", + "then": "pin:#F7BB59" + }, + { + "if": "mr_taskStatus=Deleted", + "then": "pin:#737373" + }, + { + "if": "mr_taskStatus=Already fixed", + "then": "pin:#CCB186" + }, + { + "if": "mr_taskStatus=Too hard", + "then": "pin:#FF5E63" + }, + { + "if": "mr_taskStatus=Disabled", + "then": "pin:#FF349C" + } + ] + }, + "iconSize": "40,40,center" + } + ], + "tagRenderings": [ + { + "id": "status", + "render": "Current status: {status}", + "mappings": [ + { + "if": "mr_taskStatus=Created", + "then": { + "en": "Task is created" + } + }, + { + "if": "mr_taskStatus=Fixed", + "then": { + "en": "Task is fixed" + } + }, + { + "if": "mr_taskStatus=False positive", + "then": { + "en": "Task is a false positive" + } + }, + { + "if": "mr_taskStatus=Skipped", + "then": { + "en": "Task is skipped" + } + }, + { + "if": "mr_taskStatus=Deleted", + "then": { + "en": "Task is deleted" + } + }, + { + "if": "mr_taskStatus=Already fixed", + "then": { + "en": "Task is already fixed" + } + }, + { + "if": "mr_taskStatus=Too hard", + "then": { + "en": "Task is marked as too hard" + } + }, + { + "if": "mr_taskStatus=Disabled", + "then": { + "en": "Task is disabled" + } + } + ] + }, + { + "id": "blurb", + "condition": "blurb~*", + "render": "{blurb}" + } + ], + "filter": [ + { + "id": "status", + "options": [ + { + "question": { + "en": "Show tasks with all statuses" + } + }, + { + "question": { + "en": "Show tasks that are created" + }, + "osmTags": "mr_taskStatus=Created" + }, + { + "question": { + "en": "Show tasks that are fixed" + }, + "osmTags": "mr_taskStatus=Fixed" + }, + { + "question": { + "en": "Show tasks that are false positives" + }, + "osmTags": "mr_taskStatus=False positive" + }, + { + "question": { + "en": "Show tasks that are skipped" + }, + "osmTags": "mr_taskStatus=Skipped" + }, + { + "question": { + "en": "Show tasks that are deleted" + }, + "osmTags": "mr_taskStatus=Deleted" + }, + { + "question": { + "en": "Show tasks that are already fixed" + }, + "osmTags": "mr_taskStatus=Already fixed" + }, + { + "question": { + "en": "Show tasks that are marked as too hard" + }, + "osmTags": "mr_taskStatus=Too hard" + }, + { + "question": { + "en": "Show tasks that are disabled" + }, + "osmTags": "mr_taskStatus=Disabled" + } + ] + } + ] +} \ No newline at end of file diff --git a/assets/themes/street_lighting/street_lighting_assen.json b/assets/themes/street_lighting/street_lighting_assen.json index 0ca7388ed..ce7e0e485 100644 --- a/assets/themes/street_lighting/street_lighting_assen.json +++ b/assets/themes/street_lighting/street_lighting_assen.json @@ -53,41 +53,20 @@ ] }, { - "id": "maproulette", - "name": "Maproulette Tasks", - "source": { - "osmTags": "id~*", - "geoJson": "https://maproulette.org/api/v2/challenge/view/27971", - "isOsmCache": false - }, - "calculatedTags": [ - "_closest_osm_street_lamp=feat.closest('street_lamps')?.properties?.id", - "_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 Maproulette", - "mapRendering": [ - { - "location": [ - "point", - "centroid" - ], - "icon": "circle:black", - "iconSize": "20,20,center" - } - ], - "tagRenderings": [ - "all_tags", - { - "id": "link", - "render": "View this task" - }, - { - "id": "import", - - "render": "{import_button(street_lamps,tags,Import,./assets/svg/addSmall.svg,,,,photo,mr_taskId)}" - } - ] + "builtin": "maproulette_challenge", + "override": { + "calculatedTags": [ + "_closest_osm_street_lamp=feat.closest('street_lamps')?.properties?.id", + "_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'" + ], + "tagRenderings+": [ + { + "id": "import", + "render": "{import_button(street_lamps,tags,Import,./assets/svg/addSmall.svg,,,,photo,mr_taskId)}" + } + ] + } } ], "hideFromOverview": true From e2dd4a4325b4ad01f775f833d8481a429fc9d8a6 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Wed, 13 Jul 2022 11:32:59 +0200 Subject: [PATCH 10/23] Use title icon --- assets/layers/maproulette/maproulette.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/assets/layers/maproulette/maproulette.json b/assets/layers/maproulette/maproulette.json index 4ab2265b4..bcac88c47 100644 --- a/assets/layers/maproulette/maproulette.json +++ b/assets/layers/maproulette/maproulette.json @@ -52,10 +52,6 @@ } ], "tagRenderings": [ - { - "id": "link", - "render": "View this task" - }, { "id": "status", "render": "Current status: {status}", @@ -128,6 +124,12 @@ "en": "MapRoulette Item: {parentName}" } }, + "titleIcons": [ + { + "id": "maproulette", + "render": "" + } + ], "filter": [ { "id": "status", From 8d8f2f6827a58c353948fd84bef8f1909492674c Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Wed, 13 Jul 2022 11:33:11 +0200 Subject: [PATCH 11/23] Move icon --- assets/layers/maproulette_challenge/maproulette_challenge.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/layers/maproulette_challenge/maproulette_challenge.json b/assets/layers/maproulette_challenge/maproulette_challenge.json index 23060925b..cd083bfb3 100644 --- a/assets/layers/maproulette_challenge/maproulette_challenge.json +++ b/assets/layers/maproulette_challenge/maproulette_challenge.json @@ -63,7 +63,7 @@ } ] }, - "iconSize": "40,40,center" + "iconSize": "40,40,bottom" } ], "tagRenderings": [ From befee4b5464771e27bd997b1fdf208f7bac58792 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Wed, 13 Jul 2022 16:12:25 +0200 Subject: [PATCH 12/23] Add details rendering --- Models/Constants.ts | 9 ++- UI/SpecialVisualizations.ts | 38 +++++++++- .../maproulette_challenge.json | 6 +- langs/layers/en.json | 69 +++++++++++++++++++ 4 files changed, 118 insertions(+), 4 deletions(-) diff --git a/Models/Constants.ts b/Models/Constants.ts index b296af714..468ed751d 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -6,7 +6,14 @@ export default class Constants { public static ImgurApiKey = '7070e7167f0a25a' public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" - // Currently there is no user-friendly way to get the user's API key. See https://github.com/maproulette/maproulette2/issues/476 for more information. + + /** + * API key for Maproulette + * + * Currently there is no user-friendly way to get the user's API key. + * See https://github.com/maproulette/maproulette2/issues/476 for more information. + * Using an empty string however does work for most actions, but will attribute all actions to the Superuser. + */ public static readonly MaprouletteApiKey = ""; public static defaultOverpassUrls = [ diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index d036b9bec..abe1ee59b 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -1,4 +1,4 @@ -import {Store, UIEventSource} from "../Logic/UIEventSource"; +import {Store, Stores, UIEventSource} from "../Logic/UIEventSource"; import {VariableUiElement} from "./Base/VariableUIElement"; import LiveQueryHandler from "../Logic/Web/LiveQueryHandler"; import {ImageCarousel} from "./Image/ImageCarousel"; @@ -57,6 +57,7 @@ import {SaveButton} from "./Popup/SaveButton"; import {MapillaryLink} from "./BigComponents/MapillaryLink"; import {CheckBox} from "./Input/Checkboxes"; import Slider from "./Input/Slider"; +import List from "./Base/List"; export interface SpecialVisualization { funcName: string, @@ -1091,7 +1092,40 @@ export default class SpecialVisualizations { })) }, new NearbyImageVis(), - new MapillaryLinkVis() + new MapillaryLinkVis(), + { + funcName: "maproulette_task", + args: [], + constr(state, tagSource, argument, guistate) { + let parentId = tagSource.data.mr_challengeId; + let challenge = Stores.FromPromise(Utils.downloadJsonCached(`https://maproulette.org/api/v2/challenge/${parentId}`,24*60*60*1000)); + + let details = new VariableUiElement( challenge.map(challenge => { + let listItems: BaseUIElement[] = []; + let title: BaseUIElement; + + if (challenge?.name) { + title = new Title(challenge.name); + } + + if (challenge?.description) { + listItems.push(new FixedUiElement(challenge.description)); + } + + if (challenge?.instruction) { + listItems.push(new FixedUiElement(challenge.instruction)); + } + + if(listItems.length === 0) { + return undefined; + } else { + return [title, new List(listItems)]; + } + })) + return details; + }, + docs: "Show details of a MapRoulette task" + } ] specialVisualizations.push(new AutoApplyButton(specialVisualizations)) diff --git a/assets/layers/maproulette_challenge/maproulette_challenge.json b/assets/layers/maproulette_challenge/maproulette_challenge.json index cd083bfb3..5e1385e74 100644 --- a/assets/layers/maproulette_challenge/maproulette_challenge.json +++ b/assets/layers/maproulette_challenge/maproulette_challenge.json @@ -14,7 +14,7 @@ "en": "Item in MapRoulette" } }, - "titleIcons":[ + "titleIcons": [ { "id": "maproulette", "render": "" @@ -67,6 +67,10 @@ } ], "tagRenderings": [ + { + "id": "details", + "render": "{maproulette_task()}" + }, { "id": "status", "render": "Current status: {status}", diff --git a/langs/layers/en.json b/langs/layers/en.json index 764a8669d..5186d74fb 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4423,6 +4423,75 @@ "render": "MapRoulette Item: {parentName}" } }, + "maproulette_challenge": { + "description": "Layer showing tasks of a MapRoulette challenge", + "filter": { + "0": { + "options": { + "0": { + "question": "Show tasks with all statuses" + }, + "1": { + "question": "Show tasks that are created" + }, + "2": { + "question": "Show tasks that are fixed" + }, + "3": { + "question": "Show tasks that are false positives" + }, + "4": { + "question": "Show tasks that are skipped" + }, + "5": { + "question": "Show tasks that are deleted" + }, + "6": { + "question": "Show tasks that are already fixed" + }, + "7": { + "question": "Show tasks that are marked as too hard" + }, + "8": { + "question": "Show tasks that are disabled" + } + } + } + }, + "tagRenderings": { + "status": { + "mappings": { + "0": { + "then": "Task is created" + }, + "1": { + "then": "Task is fixed" + }, + "2": { + "then": "Task is a false positive" + }, + "3": { + "then": "Task is skipped" + }, + "4": { + "then": "Task is deleted" + }, + "5": { + "then": "Task is already fixed" + }, + "6": { + "then": "Task is marked as too hard" + }, + "7": { + "then": "Task is disabled" + } + } + } + }, + "title": { + "render": "Item in MapRoulette" + } + }, "maxspeed": { "description": "Shows the allowed speed for every road", "name": "Maxspeed", From 026e649f8146f1831db0f420bf2e5b7b71f3d3bb Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Wed, 13 Jul 2022 16:26:08 +0200 Subject: [PATCH 13/23] Mark item as closed --- UI/Popup/ImportButton.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UI/Popup/ImportButton.ts b/UI/Popup/ImportButton.ts index 42f9241f3..83149e8a8 100644 --- a/UI/Popup/ImportButton.ts +++ b/UI/Popup/ImportButton.ts @@ -613,6 +613,8 @@ export class ImportPointButton extends AbstractImportButton { // Fetch MapRoulette API key, then use it to mark the challenge as fixed console.log("Marking maproulette challenge as fixed") state.maprouletteConnection.closeTask(Number(maproulette_id)); + originalFeatureTags.data["mr_taskStatus"] = "Fixed"; + originalFeatureTags.ping(); } } From f7506e07cd2cc8b69b8bba40dac64d5df3051b59 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Thu, 14 Jul 2022 15:17:09 +0200 Subject: [PATCH 14/23] Add hotel layer, conversion scripts --- assets/layers/hotel/hotel.json | 53 ++++++ assets/layers/hotel/hotel.svg | 3 + assets/layers/hotel/license_info.json | 15 ++ .../mapcomplete-changes.json | 55 ++++-- assets/themes/onwheels/onwheels.json | 16 +- langs/layers/en.json | 13 ++ langs/layers/nl.json | 13 ++ package-lock.json | 23 ++- package.json | 1 + scripts/onwheels/constants.ts | 100 ++++++++++ scripts/onwheels/convertData.ts | 177 ++++++++++++++++++ 11 files changed, 448 insertions(+), 21 deletions(-) create mode 100644 assets/layers/hotel/hotel.json create mode 100644 assets/layers/hotel/hotel.svg create mode 100644 assets/layers/hotel/license_info.json create mode 100644 scripts/onwheels/constants.ts create mode 100644 scripts/onwheels/convertData.ts diff --git a/assets/layers/hotel/hotel.json b/assets/layers/hotel/hotel.json new file mode 100644 index 000000000..8166b478b --- /dev/null +++ b/assets/layers/hotel/hotel.json @@ -0,0 +1,53 @@ +{ + "id": "hotel", + "name": { + "en": "Hotels", + "nl": "Hotels" + }, + "description": { + "en": "Layer showing all hotels", + "nl": "Laag die alle hotels toont" + }, + "source": { + "osmTags": "tourism=hotel" + }, + "mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": "pin:white" + } + ], + "tagRenderings": [ + "images", + "reviews", + { + "id": "name", + "freeform": { + "key": "name", + "placeholder": { + "en": "Name of the hotel", + "nl": "Naam van het hotel" + } + }, + "question": { + "en": "What is the name of this hotel?", + "nl": "Wat is de naam van dit hotel?" + }, + "render": { + "en": "This hotel is called {name}", + "nl": "Dit hotel heet {name}" + } + }, + "phone", + "email", + "website", + "wheelchair-access" + ], + "allowMove": { + "enableImproveAccuracy": true, + "enableRelocation": true + } +} \ No newline at end of file diff --git a/assets/layers/hotel/hotel.svg b/assets/layers/hotel/hotel.svg new file mode 100644 index 000000000..879d08301 --- /dev/null +++ b/assets/layers/hotel/hotel.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/layers/hotel/license_info.json b/assets/layers/hotel/license_info.json new file mode 100644 index 000000000..38f47cac6 --- /dev/null +++ b/assets/layers/hotel/license_info.json @@ -0,0 +1,15 @@ +[ + { + "path": "hotel.svg", + "license": "", + "authors": [ + "Andy Allan", + "Michael Glanznig", + "Adamant36", + "Paul Dicker" + ], + "sources": [ + "https://github.com/gravitystorm/openstreetmap-carto/blob/master/symbols/tourism/hotel.svg" + ] + } +] \ No newline at end of file diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index e8c3c1536..ca184a473 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -1,13 +1,19 @@ { "id": "mapcomplete-changes", "title": { - "en": "Changes made with MapComplete" + "en": "Changes made with MapComplete", + "de": "Mit MapComplete vorgenommene Änderungen", + "nl": "Wijzigingen gemaakt met MapComplete" }, "shortDescription": { - "en": "Shows changes made by MapComplete" + "en": "Shows changes made by MapComplete", + "de": "Zeigt die mit MapComplete vorgenommenen Änderungen", + "nl": "Toont wijzigingen gemaakt met MapComplete" }, "description": { - "en": "This maps shows all the changes made with MapComplete" + "en": "This maps shows all the changes made with MapComplete", + "de": "Diese Karte zeigt alle mit MapComplete vorgenommenen Änderungen", + "nl": "Deze kaart toont alle wijzigingen die met MapComplete werden gemaakt" }, "maintainer": "", "icon": "./assets/svg/logo.svg", @@ -22,7 +28,8 @@ { "id": "mapcomplete-changes", "name": { - "en": "Changeset centers" + "en": "Changeset centers", + "de": "Zentrum der Änderungssätze" }, "minzoom": 0, "source": { @@ -36,35 +43,47 @@ ], "title": { "render": { - "en": "Changeset for {theme}" + "en": "Changeset for {theme}", + "de": "Änderungssatz für {theme}", + "nl": "Wijzigingset voor {theme}" } }, "description": { - "en": "Shows all MapComplete changes" + "en": "Shows all MapComplete changes", + "de": "Zeigt alle MapComplete Änderungen", + "nl": "Toont alle wijzigingen met MapComplete" }, "tagRenderings": [ { "id": "render_id", "render": { - "en": "Changeset {id}" + "en": "Changeset {id}", + "de": "Änderungssatz {id}", + "nl": "Wijzigingset {id}" } }, { "id": "contributor", "render": { - "en": "Change made by {_last_edit:contributor}" + "en": "Change made by {_last_edit:contributor}", + "de": "Geändert von {_last_edit:contributor}", + "nl": "Wijziging gemaakt door {_last_edit:contributor}" } }, { "id": "theme", "render": { - "en": "Change with theme {theme}" + "en": "Change with theme {theme}", + "de": "Änderung mit Thema {theme}", + "nl": "Wijziging met thema {theme}" }, "mappings": [ { "if": "theme~http.*", "then": { - "en": "Change with unofficial theme {theme}" + "en": "Change with unofficial theme {theme}", + "de": "Änderung mit inoffiziellem Thema {theme}", + "nl": "Wijziging met officieus thema {theme}" } } ] @@ -364,7 +383,9 @@ } ], "question": { - "en": "Themename contains {search}" + "en": "Themename contains {search}", + "de": "Themenname enthält {search}", + "nl": "Themanaam bevat {search}" } } ] @@ -380,7 +401,9 @@ } ], "question": { - "en": "Made by contributor {search}" + "en": "Made by contributor {search}", + "de": "Erstellt von {search}", + "nl": "Gemaakt door bijdrager {search}" } } ] @@ -396,7 +419,9 @@ } ], "question": { - "en": "Not made by contributor {search}" + "en": "Not made by contributor {search}", + "de": "Nicht erstellt von {search}", + "nl": "Niet gemaakt door bijdrager {search}" } } ] @@ -411,7 +436,9 @@ { "id": "link_to_more", "render": { - "en": "More statistics can be found here" + "en": "More statistics can be found here", + "de": "Weitere Statistiken finden Sie hier", + "nl": "Meer statistieken kunnen hier gevonden worden" } }, { diff --git a/assets/themes/onwheels/onwheels.json b/assets/themes/onwheels/onwheels.json index 920ce1e1c..703cf1924 100644 --- a/assets/themes/onwheels/onwheels.json +++ b/assets/themes/onwheels/onwheels.json @@ -27,7 +27,21 @@ "shops", "toilet", "viewpoint", - "doctors" + "doctors", + "hotel", + { + "builtin": "maproulette_challenge", + "override": { + "source": { + "geoJson": "https://maproulette.org/api/v2/challenge/view/28012" + }, + "calculatedTags": [ + "_closest_osm_hotel=feat.closest('hotel')?.properties?.id", + "_closest_osm_hotel_distance=feat.distanceTo(feat.properties._closest_osm_hotel)", + "_has_closeby_feature=Number(feat.properties._closest_osm_hotel_distance) < 50 ? 'yes' : 'no'" + ] + } + } ], "overrideAll": { "minzoom": "15", diff --git a/langs/layers/en.json b/langs/layers/en.json index d0274d74d..deb5fa46e 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4061,6 +4061,19 @@ "render": "Hospital" } }, + "hotel": { + "description": "Layer showing all hotels", + "name": "Hotels", + "tagRenderings": { + "name": { + "freeform": { + "placeholder": "Name of the hotel" + }, + "question": "What is the name of this hotel?", + "render": "This hotel is called {name}" + } + } + }, "hydrant": { "description": "Map layer to show fire hydrants.", "name": "Map of hydrants", diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 11d154dcd..18c26b124 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3937,6 +3937,19 @@ "render": "Hackerspace" } }, + "hotel": { + "description": "Laag die alle hotels toont", + "name": "Hotels", + "tagRenderings": { + "name": { + "freeform": { + "placeholder": "Naam van het hotel" + }, + "question": "Wat is de naam van dit hotel?", + "render": "Dit hotel heet {name}" + } + } + }, "hydrant": { "description": "Kaartlaag met brandkranen.", "name": "Kaart van brandkranen", diff --git a/package-lock.json b/package-lock.json index a7c3cdf0a..98f04eb85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@turf/length": "^6.5.0", "@turf/turf": "^6.5.0", "@types/chai": "^4.3.0", + "@types/geojson": "^7946.0.10", "@types/jquery": "^3.5.5", "@types/leaflet-markercluster": "^1.0.3", "@types/leaflet-providers": "^1.2.0", @@ -3223,9 +3224,9 @@ "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==" }, "node_modules/@types/geojson": { - "version": "7946.0.8", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", - "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==" + "version": "7946.0.10", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" }, "node_modules/@types/jquery": { "version": "3.5.5", @@ -7170,6 +7171,11 @@ "rbush": "^3.0.1" } }, + "node_modules/geojson-rbush/node_modules/@types/geojson": { + "version": "7946.0.8", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", + "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==" + }, "node_modules/geojson-rbush/node_modules/quickselect": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", @@ -19249,9 +19255,9 @@ "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==" }, "@types/geojson": { - "version": "7946.0.8", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", - "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==" + "version": "7946.0.10", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" }, "@types/jquery": { "version": "3.5.5", @@ -22417,6 +22423,11 @@ "rbush": "^3.0.1" }, "dependencies": { + "@types/geojson": { + "version": "7946.0.8", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", + "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==" + }, "quickselect": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", diff --git a/package.json b/package.json index 6d91d1aa2..18abddf4c 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "@turf/length": "^6.5.0", "@turf/turf": "^6.5.0", "@types/chai": "^4.3.0", + "@types/geojson": "^7946.0.10", "@types/jquery": "^3.5.5", "@types/leaflet-markercluster": "^1.0.3", "@types/leaflet-providers": "^1.2.0", diff --git a/scripts/onwheels/constants.ts b/scripts/onwheels/constants.ts new file mode 100644 index 000000000..8dd4ef94c --- /dev/null +++ b/scripts/onwheels/constants.ts @@ -0,0 +1,100 @@ +/** + * Class containing all constants and tables used in the script + * + * @class Constants + */ +export default class Constants { + /** + * Table used to determine tags for the category + * + * Keys are the original category names, + * values are an object containing the tags + */ + public static categories = { + restaurant: { + amenity: "restaurant", + }, + parking: { + amenity: "parking", + }, + hotel: { + leisure: "hotel", + }, + wc: { + amenity: "toilets", + }, + winkel: { + shop: "yes", + }, + apotheek: { + amenity: "pharmacy", + healthcare: "pharmacy", + }, + ziekenhuis: { + amenity: "hospital", + healthcare: "hospital", + }, + bezienswaardigheid: { + tourism: "attraction", + }, + ontspanning: { + fixme: "Needs proper tags", + }, + cafe: { + amenity: "cafe", + }, + dienst: { + fixme: "Needs proper tags", + }, + bank: { + amenity: "bank", + }, + gas: { + amenity: "fuel", + }, + medical: { + fixme: "Needs proper tags", + }, + obstacle: { + fixme: "Needs proper tags", + }, + }; + + /** + * Table used to rename original Onwheels properties to their corresponding OSM properties + * + * Keys are the original Onwheels properties, values are the corresponding OSM properties + */ + public static names = { + ID: "id", + Naam: "name", + Straat: "addr:street", + Nummer: "addr:housenumber", + Postcode: "addr:postcode", + Plaats: "addr:city", + Website: "website", + Email: "email", + "Aantal aangepaste parkeerplaatsen": "capacity:disabled", + "Aantal treden": "step_count", + "Hellend vlak aanwezig": "ramp", + "Baby verzorging aanwezig": "changing_table", + "Totale hoogte van de treden": "kerb:height" + }; + + /** + * In some cases types might need to be converted as well + * + * Keys are the OSM properties, values are the wanted type + */ + public static types = { + "Hellend vlak aanwezig": "boolean", + "Baby verzorging aanwezig": "boolean", + }; + + /** + * Some tags also need to have units added + */ + public static units = { + "Totale hoogte van de treden": "cm", + }; +} diff --git a/scripts/onwheels/convertData.ts b/scripts/onwheels/convertData.ts new file mode 100644 index 000000000..e35d6321f --- /dev/null +++ b/scripts/onwheels/convertData.ts @@ -0,0 +1,177 @@ +import { parse } from "csv-parse/sync"; +import { readFileSync, writeFileSync } from "fs"; +import { Feature, FeatureCollection, GeoJsonProperties } from "geojson"; +import Constants from "./constants"; + +/** + * Function to determine the tags for a category + * + * @param category The category of the item + * @returns List of tags for the category + */ +function categoryTags(category: string): GeoJsonProperties { + const tags = Constants.categories[category]; + if (!tags) { + throw `Unknown category: ${category}`; + } + return tags; +} + +/** + * Rename tags to match the OSM standard + * + * @param item The item to convert + * @returns GeoJsonProperties for the item + */ +function renameTags(item): GeoJsonProperties { + const properties: GeoJsonProperties = {}; + for (const key in item) { + if (Constants.names[key] && item[key]) { + properties[Constants.names[key]] = item[key]; + } + } + return properties; +} + +function convertTypes(properties: GeoJsonProperties): GeoJsonProperties { + for (const property in properties) { + // Determine the original tag by looking at the value in the names table + const originalTag = Object.keys(Constants.names).find( + (tag) => Constants.names[tag] === property + ); + // Check if we need to convert the value + if (Constants.types[originalTag]) { + switch (Constants.types[originalTag]) { + case "boolean": + properties[property] = properties[property] === "1" ? "yes" : "no"; + break; + default: + break; + } + } + } + return properties; +} + +/** + * Function to add units to the properties if necessary + * + * @param properties The properties to add units to + * @returns The properties with units added + */ +function addUnits(properties: GeoJsonProperties): GeoJsonProperties { + for (const property in properties) { + // Check if the property needs units, and doesn't already have them + if (Constants.units[property] && property.match(/.*([A-z]).*/gi) === null) { + properties[ + property + ] = `${properties[property]} ${Constants.units[property]}`; + } + } + return properties; +} + +/** + * Main function to convert original CSV into GeoJSON + * + * @param args List of arguments [input.csv] + */ +function main(args: string[]): void { + const csvOptions = { + columns: true, + skip_empty_lines: true, + trim: true, + }; + const file = args[0]; + const output = args[1]; + + // Create an empty list to store the converted features + var items: Feature[] = []; + + // Read CSV file + const csv: Record[] = parse(readFileSync(file), csvOptions); + + // Loop through all the entries + for (var i = 0; i < csv.length; i++) { + const item = csv[i]; + + // Determine coordinates + const lat = Number(item["Latitude"]); + const lon = Number(item["Longitude"]); + + // Check if coordinates are valid + if (isNaN(lat) || isNaN(lon)) { + throw `Not a valid lat or lon for entry ${i}: ${JSON.stringify(item)}`; + } + + // Create a new collection to store the converted properties + var properties: GeoJsonProperties = {}; + + // Add standard tags for category + const category = item["Categorie"]; + properties = { ...properties, ...categoryTags(category) }; + + // Add the rest of the needed tags + properties = { ...properties, ...renameTags(item) }; + + // Convert types + properties = convertTypes(properties); + + // Loop through all the properties + // for (var key in item) { + // // Check if we need the property, and it's not empty + // if (Constants.names[key] && item[key]) { + // // Check if the type needs to be converted + // if (Constants.types[key]) { + // // Conversion necessary, use the typeTable + // switch (Constants.types[key]) { + // case "boolean": + // properties[Constants.names[key]] = + // item[key] === "1" ? "yes" : "no"; + // break; + // default: + // properties[Constants.names[key]] = item[key]; + // break; + // } + // } else { + // // No conversion necessary, we can just add the property + // properties[Constants.names[key]] = item[key]; + // } + // } + // } + + // Add units if necessary + addUnits(properties); + + // Create the new feature + const feature: Feature = { + type: "Feature", + id: item["ID"], + geometry: { + type: "Point", + coordinates: [lon, lat], + }, + properties, + }; + + // Push it to the list we created earlier + items.push(feature); + } + + // Make a FeatureCollection out of it + const featureCollection: FeatureCollection = { + type: "FeatureCollection", + features: items, + }; + + // Output the data to the console + console.log(JSON.stringify(featureCollection)); + + // Write the data to a file + if (output) { + writeFileSync(`${output}.geojson`, JSON.stringify(featureCollection, null, 2)); + } +} + +// Execute the main function, with the stripped arguments +main(process.argv.slice(2)); From 5290e0042b9ddec778fb72d3cbd1232e95f1cf29 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Thu, 14 Jul 2022 15:45:57 +0200 Subject: [PATCH 15/23] Add presets and titles --- assets/layers/hotel/hotel.json | 30 ++++++++++++++++++- .../Conversion/PrepareLayer.spec.ts | 2 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/assets/layers/hotel/hotel.json b/assets/layers/hotel/hotel.json index 8166b478b..bf698f69b 100644 --- a/assets/layers/hotel/hotel.json +++ b/assets/layers/hotel/hotel.json @@ -11,13 +11,41 @@ "source": { "osmTags": "tourism=hotel" }, + "minzoom": 13, + "title": { + "render": { + "en": "Hotel", + "nl": "Hotel" + }, + "mappings": [ + { + "if": "name~*", + "then": { + "en": "Hotel {name}", + "nl": "Hotel {name}" + } + } + ] + }, + "presets": [ + { + "title": { + "en": "a hotel", + "nl": "een hotel" + }, + "tags": [ + "tourism=hotel" + ] + } + ], "mapRendering": [ { "location": [ "point", "centroid" ], - "icon": "pin:white" + "icon": "circle:white;./assets/layers/hotel/hotel.svg", + "iconSize": "40,40,center" } ], "tagRenderings": [ diff --git a/test/Models/ThemeConfig/Conversion/PrepareLayer.spec.ts b/test/Models/ThemeConfig/Conversion/PrepareLayer.spec.ts index d395401be..116006a62 100644 --- a/test/Models/ThemeConfig/Conversion/PrepareLayer.spec.ts +++ b/test/Models/ThemeConfig/Conversion/PrepareLayer.spec.ts @@ -123,7 +123,7 @@ describe('RewriteSpecial', function () { const r = new RewriteSpecial().convert(tr, "test").result expect(r).to.deep.eq({ "id": "uk_addresses_import_button", - "render": {'*': "{import_button(address,urpn_count=$urpn_count;ref:GB:uprn=$ref:GB:uprn$,Add this address,./assets/themes/uk_addresses/housenumber_add.svg,,,,none)}"} + "render": {'*': "{import_button(address,urpn_count=$urpn_count;ref:GB:uprn=$ref:GB:uprn$,Add this address,./assets/themes/uk_addresses/housenumber_add.svg,,,,none,)}"} }) }) }); From 512282772a6631085db25a4725df557d7200244d Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Mon, 18 Jul 2022 10:08:39 +0200 Subject: [PATCH 16/23] Generate translations --- assets/themes/buurtnatuur/buurtnatuur.json | 5 ++++- assets/themes/grb_import/grb.json | 2 +- assets/themes/speelplekken/speelplekken.json | 2 +- langs/layers/en.json | 13 +++++++++++++ langs/layers/nl.json | 13 +++++++++++++ 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/assets/themes/buurtnatuur/buurtnatuur.json b/assets/themes/buurtnatuur/buurtnatuur.json index 0527a4e39..a1ff62cfb 100644 --- a/assets/themes/buurtnatuur/buurtnatuur.json +++ b/assets/themes/buurtnatuur/buurtnatuur.json @@ -35,7 +35,10 @@ "or": [ "leisure=nature_reserve", { - "and": ["boundary=protected_area","protect_class!=22"] + "and": [ + "boundary=protected_area", + "protect_class!=22" + ] } ] } diff --git a/assets/themes/grb_import/grb.json b/assets/themes/grb_import/grb.json index d0581433e..4edd16593 100644 --- a/assets/themes/grb_import/grb.json +++ b/assets/themes/grb_import/grb.json @@ -630,7 +630,7 @@ } } ], - "isShown":{ + "isShown": { "or": [ "_imported_osm_object_found!=true", "_imported_osm_still_fresh!=true" diff --git a/assets/themes/speelplekken/speelplekken.json b/assets/themes/speelplekken/speelplekken.json index 9b0b1825e..de451a2b5 100644 --- a/assets/themes/speelplekken/speelplekken.json +++ b/assets/themes/speelplekken/speelplekken.json @@ -288,6 +288,6 @@ "then": "./assets/themes/speelplekken/youtube.svg" } ], - "isShown":"_is_shadowed!=yes" + "isShown": "_is_shadowed!=yes" } } \ No newline at end of file diff --git a/langs/layers/en.json b/langs/layers/en.json index b83c28225..d958714f6 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -4064,6 +4064,11 @@ "hotel": { "description": "Layer showing all hotels", "name": "Hotels", + "presets": { + "0": { + "title": "a hotel" + } + }, "tagRenderings": { "name": { "freeform": { @@ -4072,6 +4077,14 @@ "question": "What is the name of this hotel?", "render": "This hotel is called {name}" } + }, + "title": { + "mappings": { + "0": { + "then": "Hotel {name}" + } + }, + "render": "Hotel" } }, "hydrant": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 548a74d7f..1077d9515 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3940,6 +3940,11 @@ "hotel": { "description": "Laag die alle hotels toont", "name": "Hotels", + "presets": { + "0": { + "title": "een hotel" + } + }, "tagRenderings": { "name": { "freeform": { @@ -3948,6 +3953,14 @@ "question": "Wat is de naam van dit hotel?", "render": "Dit hotel heet {name}" } + }, + "title": { + "mappings": { + "0": { + "then": "Hotel {name}" + } + }, + "render": "Hotel" } }, "hydrant": { From 63bb1a4da30066eb3325e6d651d2139d31f33854 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Mon, 18 Jul 2022 16:08:41 +0200 Subject: [PATCH 17/23] Show buttons for adding tags and importing --- UI/Popup/TagApplyButton.ts | 7 +++++++ assets/themes/onwheels/onwheels.json | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/UI/Popup/TagApplyButton.ts b/UI/Popup/TagApplyButton.ts index 0a654895b..7ccca7ca0 100644 --- a/UI/Popup/TagApplyButton.ts +++ b/UI/Popup/TagApplyButton.ts @@ -42,6 +42,13 @@ export default class TagApplyButton implements AutoAction { public static generateTagsToApply(spec: string, tagSource: Store): Store { + // Check whether we need to look up a single value + + if (!spec.includes(";") && !spec.includes("=") && spec.includes("$")){ + // We seem to be dealing with a single value, fetch it + spec = tagSource.data[spec.replace("$","")] + } + const tgsSpec = spec.split(";").map(spec => { const kv = spec.split("=").map(s => s.trim()); if (kv.length != 2) { diff --git a/assets/themes/onwheels/onwheels.json b/assets/themes/onwheels/onwheels.json index 703cf1924..3617652ce 100644 --- a/assets/themes/onwheels/onwheels.json +++ b/assets/themes/onwheels/onwheels.json @@ -39,6 +39,18 @@ "_closest_osm_hotel=feat.closest('hotel')?.properties?.id", "_closest_osm_hotel_distance=feat.distanceTo(feat.properties._closest_osm_hotel)", "_has_closeby_feature=Number(feat.properties._closest_osm_hotel_distance) < 50 ? 'yes' : 'no'" + ], + "+tagRenderings": [ + { + "id": "import-button", + "condition": "_has_closeby_feature=no", + "render": "{import_button(hotel,tags,Import,./assets/svg/addSmall.svg,,,,photo,mr_taskId)}" + }, + { + "id": "tag-apply-button", + "condition": "_has_closeby_feature=yes", + "render": "{tag_apply($tags, Add all the suggested tags, apply_icon.svg, _closest_osm_hotel)}" + } ] } } From 862ef98c3e3c357554f6895c363a95ee8ae300e5 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Mon, 18 Jul 2022 16:10:06 +0200 Subject: [PATCH 18/23] Fixed script --- scripts/onwheels/constants.ts | 2 +- scripts/onwheels/convertData.ts | 69 ++++++++++++++++++++------------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/scripts/onwheels/constants.ts b/scripts/onwheels/constants.ts index 8dd4ef94c..fcd142890 100644 --- a/scripts/onwheels/constants.ts +++ b/scripts/onwheels/constants.ts @@ -18,7 +18,7 @@ export default class Constants { amenity: "parking", }, hotel: { - leisure: "hotel", + tourism: "hotel", }, wc: { amenity: "toilets", diff --git a/scripts/onwheels/convertData.ts b/scripts/onwheels/convertData.ts index e35d6321f..a0f58587f 100644 --- a/scripts/onwheels/convertData.ts +++ b/scripts/onwheels/convertData.ts @@ -10,7 +10,11 @@ import Constants from "./constants"; * @returns List of tags for the category */ function categoryTags(category: string): GeoJsonProperties { - const tags = Constants.categories[category]; + const tags = { + tags: Object.keys(Constants.categories[category]).map((tag) => { + return `${tag}=${Constants.categories[category][tag]}`; + }), + }; if (!tags) { throw `Unknown category: ${category}`; } @@ -25,9 +29,15 @@ function categoryTags(category: string): GeoJsonProperties { */ function renameTags(item): GeoJsonProperties { const properties: GeoJsonProperties = {}; + properties.tags = []; for (const key in item) { if (Constants.names[key] && item[key]) { - properties[Constants.names[key]] = item[key]; + if (Constants.names[key] == "name" || Constants.names[key] == "id") { + properties[Constants.names[key]] = item[key]; + } + if (Constants.names[key] !== "id") { + properties.tags.push(Constants.names[key] + "=" + item[key]); + } } } return properties; @@ -71,6 +81,24 @@ function addUnits(properties: GeoJsonProperties): GeoJsonProperties { return properties; } +/** + * Function that adds Maproulette instructions and blurb to each item + * + * @param properties The properties to add Maproulette tags to + * @param item The original CSV item + */ +function addMaprouletteTags(properties: GeoJsonProperties, item: any): GeoJsonProperties { + properties[ + "blurb" + ] = `This is feature out of the ${item["Categorie"]} category. + It may match another OSM item, if so, you can add any missing tags to it. + If it doesn't match any other OSM item, you can create a new one. + Here is a list of tags that can be added: + ${properties["tags"].split(";").join("\n")} + You can also easily import this item using MapComplete: https://mapcomplete.osm.be/onwheels.html#${properties["id"]}`; + return properties; +} + /** * Main function to convert original CSV into GeoJSON * @@ -109,40 +137,24 @@ function main(args: string[]): void { // Add standard tags for category const category = item["Categorie"]; - properties = { ...properties, ...categoryTags(category) }; + const tagsCategory = categoryTags(category); // Add the rest of the needed tags properties = { ...properties, ...renameTags(item) }; + // Merge them together + properties.tags = [...tagsCategory.tags, ...properties.tags]; + properties.tags = properties.tags.join(";"); + // Convert types properties = convertTypes(properties); - // Loop through all the properties - // for (var key in item) { - // // Check if we need the property, and it's not empty - // if (Constants.names[key] && item[key]) { - // // Check if the type needs to be converted - // if (Constants.types[key]) { - // // Conversion necessary, use the typeTable - // switch (Constants.types[key]) { - // case "boolean": - // properties[Constants.names[key]] = - // item[key] === "1" ? "yes" : "no"; - // break; - // default: - // properties[Constants.names[key]] = item[key]; - // break; - // } - // } else { - // // No conversion necessary, we can just add the property - // properties[Constants.names[key]] = item[key]; - // } - // } - // } - // Add units if necessary addUnits(properties); + // Add Maproulette tags + properties = addMaprouletteTags(properties, item); + // Create the new feature const feature: Feature = { type: "Feature", @@ -169,7 +181,10 @@ function main(args: string[]): void { // Write the data to a file if (output) { - writeFileSync(`${output}.geojson`, JSON.stringify(featureCollection, null, 2)); + writeFileSync( + `${output}.geojson`, + JSON.stringify(featureCollection, null, 2) + ); } } From 983ada79f3c12384dd59a35e19fae6d59ad1785c Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Tue, 19 Jul 2022 12:06:26 +0200 Subject: [PATCH 19/23] Changed tagrendering --- assets/themes/onwheels/onwheels.json | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/assets/themes/onwheels/onwheels.json b/assets/themes/onwheels/onwheels.json index 56dbe0eb9..22f1d64d2 100644 --- a/assets/themes/onwheels/onwheels.json +++ b/assets/themes/onwheels/onwheels.json @@ -45,12 +45,34 @@ { "id": "import-button", "condition": "_has_closeby_feature=no", - "render": "{import_button(hotel,tags,Import,./assets/svg/addSmall.svg,,,,photo,mr_taskId)}" + "render": { + "special": { + "type": "import_button", + "targetLayer": "hotel", + "tags": "tags", + "text": { + "en": "Import" + }, + "icon": "./assets/svg/addSmall.svg", + "location_picker": "photo", + "maproulette_id": "mr_taskId" + } + } }, { "id": "tag-apply-button", "condition": "_has_closeby_feature=yes", - "render": "{tag_apply($tags, Add all the suggested tags, apply_icon.svg, _closest_osm_hotel)}" + "render": { + "special": { + "type": "tag_apply", + "tags_to_apply": "$tags", + "message": { + "en": "Add all the suggested tags" + }, + "image": "./assets/svg/addSmall.svg", + "id_of_object_to_apply_this_one": "_closest_osm_hotel" + } + } } ] } From e312253354abdfffdcda4dbbb81e34cb813c0fce Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Tue, 19 Jul 2022 12:09:04 +0200 Subject: [PATCH 20/23] Fix script (again) --- scripts/onwheels/convertData.ts | 61 +++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/scripts/onwheels/convertData.ts b/scripts/onwheels/convertData.ts index a0f58587f..63686ecf7 100644 --- a/scripts/onwheels/convertData.ts +++ b/scripts/onwheels/convertData.ts @@ -44,22 +44,36 @@ function renameTags(item): GeoJsonProperties { } function convertTypes(properties: GeoJsonProperties): GeoJsonProperties { - for (const property in properties) { - // Determine the original tag by looking at the value in the names table - const originalTag = Object.keys(Constants.names).find( - (tag) => Constants.names[tag] === property + // Split the tags into a list + let tags = properties.tags.split(";"); + + for (const tag in tags) { + // Split the tag into key and value + const key = tags[tag].split("=")[0]; + const value = tags[tag].split("=")[1]; + const originalKey = Object.keys(Constants.names).find( + (tag) => Constants.names[tag] === key ); - // Check if we need to convert the value - if (Constants.types[originalTag]) { - switch (Constants.types[originalTag]) { + + if (Constants.types[originalKey]) { + // We need to convert the value to the correct type + let newValue; + switch (Constants.types[originalKey]) { case "boolean": - properties[property] = properties[property] === "1" ? "yes" : "no"; + newValue = value === "1" ? "yes" : "no"; break; default: + newValue = value; break; } + tags[tag] = `${key}=${newValue}`; } } + + // Rejoin the tags + properties.tags = tags.join(";"); + + // Return the properties return properties; } @@ -70,14 +84,26 @@ function convertTypes(properties: GeoJsonProperties): GeoJsonProperties { * @returns The properties with units added */ function addUnits(properties: GeoJsonProperties): GeoJsonProperties { - for (const property in properties) { + // Split the tags into a list + let tags = properties.tags.split(";"); + + for (const tag in tags) { + const key = tags[tag].split("=")[0]; + const value = tags[tag].split("=")[1]; + const originalKey = Object.keys(Constants.names).find( + (tag) => Constants.names[tag] === key + ); + // Check if the property needs units, and doesn't already have them - if (Constants.units[property] && property.match(/.*([A-z]).*/gi) === null) { - properties[ - property - ] = `${properties[property]} ${Constants.units[property]}`; + if (Constants.units[originalKey] && value.match(/.*([A-z]).*/gi) === null) { + tags[tag] = `${key}=${value} ${Constants.units[originalKey]}`; } } + + // Rejoin the tags + properties.tags = tags.join(";"); + + // Return the properties return properties; } @@ -150,7 +176,7 @@ function main(args: string[]): void { properties = convertTypes(properties); // Add units if necessary - addUnits(properties); + properties = addUnits(properties); // Add Maproulette tags properties = addMaprouletteTags(properties, item); @@ -176,15 +202,14 @@ function main(args: string[]): void { features: items, }; - // Output the data to the console - console.log(JSON.stringify(featureCollection)); - - // Write the data to a file + // Write the data to a file or output to the console if (output) { writeFileSync( `${output}.geojson`, JSON.stringify(featureCollection, null, 2) ); + } else { + console.log(JSON.stringify(featureCollection)); } } From 5f1bc701690697ba55a6a0e0efb1d21a9f31eeee Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Tue, 19 Jul 2022 20:54:05 +0200 Subject: [PATCH 21/23] Slightly patch script --- scripts/onwheels/constants.ts | 4 +++- scripts/onwheels/convertData.ts | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/scripts/onwheels/constants.ts b/scripts/onwheels/constants.ts index fcd142890..dbe0cbd36 100644 --- a/scripts/onwheels/constants.ts +++ b/scripts/onwheels/constants.ts @@ -78,7 +78,8 @@ export default class Constants { "Aantal treden": "step_count", "Hellend vlak aanwezig": "ramp", "Baby verzorging aanwezig": "changing_table", - "Totale hoogte van de treden": "kerb:height" + "Totale hoogte van de treden": "kerb:height", + "Deurbreedte": "door:width", }; /** @@ -96,5 +97,6 @@ export default class Constants { */ public static units = { "Totale hoogte van de treden": "cm", + "Deurbreedte": "cm", }; } diff --git a/scripts/onwheels/convertData.ts b/scripts/onwheels/convertData.ts index 63686ecf7..4183d1cd7 100644 --- a/scripts/onwheels/convertData.ts +++ b/scripts/onwheels/convertData.ts @@ -30,12 +30,23 @@ function categoryTags(category: string): GeoJsonProperties { function renameTags(item): GeoJsonProperties { const properties: GeoJsonProperties = {}; properties.tags = []; + // Loop through the original item tags for (const key in item) { + // Check if we need it and it's not a null value if (Constants.names[key] && item[key]) { + // Name and id tags need to be in the properties if (Constants.names[key] == "name" || Constants.names[key] == "id") { properties[Constants.names[key]] = item[key]; } + // Other tags need to be in the tags variable if (Constants.names[key] !== "id") { + // Website needs to have at least any = encoded + if(Constants.names[key] == "website") { + let website = item[key]; + // Encode URL + website = website.replace("=", "%3D"); + item[key] = website; + } properties.tags.push(Constants.names[key] + "=" + item[key]); } } @@ -43,6 +54,12 @@ function renameTags(item): GeoJsonProperties { return properties; } +/** + * Convert types to match the OSM standard + * + * @param properties The properties to convert + * @returns The converted properties + */ function convertTypes(properties: GeoJsonProperties): GeoJsonProperties { // Split the tags into a list let tags = properties.tags.split(";"); From 8a52f9440fc42ea33685ff2e4c5bfe2602886523 Mon Sep 17 00:00:00 2001 From: Robin van der Linde Date: Wed, 27 Jul 2022 10:24:58 +0200 Subject: [PATCH 22/23] Dont actually mark task as fixed in dryrun --- UI/Popup/ImportButton.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/UI/Popup/ImportButton.ts b/UI/Popup/ImportButton.ts index 83149e8a8..cdf299e7d 100644 --- a/UI/Popup/ImportButton.ts +++ b/UI/Popup/ImportButton.ts @@ -608,13 +608,16 @@ export class ImportPointButton extends AbstractImportButton { } let maproulette_id = originalFeatureTags.data[args.maproulette_id]; - console.log("Checking if we need to mark a maproulette challenge as fixed (" + maproulette_id + ")") + console.log("Checking if we need to mark a maproulette task as fixed (" + maproulette_id + ")") if (maproulette_id !== undefined) { - // Fetch MapRoulette API key, then use it to mark the challenge as fixed - console.log("Marking maproulette challenge as fixed") - state.maprouletteConnection.closeTask(Number(maproulette_id)); - originalFeatureTags.data["mr_taskStatus"] = "Fixed"; - originalFeatureTags.ping(); + if (state.featureSwitchIsTesting.data){ + console.log("Not marking maproulette task " + maproulette_id + " as fixed, because we are in testing mode") + } else { + console.log("Marking maproulette task as fixed") + state.maprouletteConnection.closeTask(Number(maproulette_id)); + originalFeatureTags.data["mr_taskStatus"] = "Fixed"; + originalFeatureTags.ping(); + } } } From 03054f678f3475bbc3cc544232c771e37615ab55 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Wed, 27 Jul 2022 14:47:41 +0200 Subject: [PATCH 23/23] Move maproulette layer to the bottom to fix translation generation --- assets/themes/onwheels/onwheels.json | 78 ++++++++++++++-------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/assets/themes/onwheels/onwheels.json b/assets/themes/onwheels/onwheels.json index e33141ff5..9991c8ab2 100644 --- a/assets/themes/onwheels/onwheels.json +++ b/assets/themes/onwheels/onwheels.json @@ -20,7 +20,7 @@ "widenFactor": 2, "hideFromOverview": false, "layers": [ - { + { "builtin": "indoors", "override": { "minzoom": 19, @@ -329,6 +329,44 @@ ] } }, + { + "builtin": "governments", + "override": { + "minzoom": 15, + "mapRendering": [ + { + "icon": "./assets/themes/onwheels/government.svg", + "iconSize": "40,40,bottom" + } + ] + } + }, + { + "builtin": "current_view", + "override": { + "+mapRendering": [ + { + "location": [ + "point", + "centroid" + ], + "icon": "statistics" + } + ], + "=title": { + "render": { + "en": "Statistics", + "de": "Statistik" + } + }, + "tagRenderings": [ + { + "id": "stats", + "render": "{statistics()}" + } + ] + } + }, { "builtin": "maproulette_challenge", "override": { @@ -375,44 +413,6 @@ } ] } - }, - { - "builtin": "governments", - "override": { - "minzoom": 15, - "mapRendering": [ - { - "icon": "./assets/themes/onwheels/government.svg", - "iconSize": "40,40,bottom" - } - ] - } - }, - { - "builtin": "current_view", - "override": { - "+mapRendering": [ - { - "location": [ - "point", - "centroid" - ], - "icon": "statistics" - } - ], - "=title": { - "render": { - "en": "Statistics", - "de": "Statistik" - } - }, - "tagRenderings": [ - { - "id": "stats", - "render": "{statistics()}" - } - ] - } } ], "overrideAll": {