From 592be68f09b06f22d45d5d5c274c91315d1613f7 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 27 Jul 2024 02:18:58 +0200 Subject: [PATCH] Themes: first version of circular economy theme --- Cleanup.osc | 1122 +++++++++++++++++ .../assisted_repair/assisted_repair.json | 166 +++ assets/layers/ghost_bike/ghost_bike.json | 7 +- assets/layers/questions/questions.json | 6 + assets/layers/recycling/furniture.svg | 46 + assets/layers/recycling/furniture.svg.license | 2 + assets/layers/recycling/license_info.json | 10 + assets/layers/tool_library/tool_library.json | 151 +++ assets/themes/circular_economy/circular.svg | 74 ++ .../circular_economy/circular.svg.license | 2 + .../circular_economy/circular_economy.json | 66 + .../themes/circular_economy/license_info.json | 10 + .../mapcomplete-changes.json | 4 + package.json | 2 +- scripts/importscripts/cleanRepair.ts | 232 ++++ src/Models/Constants.ts | 1 + src/Models/ThemeConfig/TagRenderingConfig.ts | 12 +- src/UI/Map/Icon.svelte | 3 + src/UI/StudioGUI.svelte | 4 +- src/Utils.ts | 8 +- 20 files changed, 1917 insertions(+), 11 deletions(-) create mode 100644 Cleanup.osc create mode 100644 assets/layers/assisted_repair/assisted_repair.json create mode 100644 assets/layers/recycling/furniture.svg create mode 100644 assets/layers/recycling/furniture.svg.license create mode 100644 assets/layers/tool_library/tool_library.json create mode 100644 assets/themes/circular_economy/circular.svg create mode 100644 assets/themes/circular_economy/circular.svg.license create mode 100644 assets/themes/circular_economy/circular_economy.json create mode 100644 assets/themes/circular_economy/license_info.json create mode 100644 scripts/importscripts/cleanRepair.ts diff --git a/Cleanup.osc b/Cleanup.osc new file mode 100644 index 000000000..8216e09ff --- /dev/null +++ b/Cleanup.osc @@ -0,0 +1,1122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/layers/assisted_repair/assisted_repair.json b/assets/layers/assisted_repair/assisted_repair.json new file mode 100644 index 000000000..68543014c --- /dev/null +++ b/assets/layers/assisted_repair/assisted_repair.json @@ -0,0 +1,166 @@ +{ + "minzoom": 1, + "pointRendering": [ + { + "location": [ + "point", + "centroid" + ], + "marker": [ + { + "icon": "circle", + "color": "white" + }, + { + "icon": { + "render": "gear", + "mappings": [ + { + "if": { + "or": [ + "bicycle:repair=yes", + "service:bicycle:repair=yes" + ] + }, + "then": "./assets/layers/hackerspace/bicycle.svg" + } + ] + } + } + ] + } + ], + "tagRenderings": [ + "images", + "preset_description", + { + "question": { + "en": "What is the name of this repair workshop?" + }, + "id": "name", + "render": { + "en": "This workshop is called {name}" + }, + "freeform": { + "key": "name" + } + }, + "opening_hours_by_appointment", + "contact", + "mastodon", + "facebook", + { + "question": { + "en": "What type of items are repaired here?" + }, + "id": "item:repair", + "mappings": [ + { + "if": "service:mobile_phone:repair=yes", + "alsoShowIf": "mobile_phone:repair=yes", + "addExtraTags": ["mobile_phone:repair="], + "then": { + "en": "Mobile phones are repaired here" + }, + "ifnot": "service:mobile_phone:repair=no" + }, + { + "if": "service:computer:repair=yes", + "alsoShowIf": "computer:repair=yes", + "addExtraTags": ["computer:repair="], + "then": { + "en": "Computers are repaired here" + }, + "ifnot": "service:computer:repair=no" + }, + { + "if": "service:bicycle:repair=yes", + "alsoShowIf": "bicycle:repair=yes", + "addExtraTags": ["bicycle:repair="], + "then": { + "en": "Bicycles are repaired here" + }, + "ifnot": "service:bicycle:repair=no" + }, + { + "if": "service:electronics:repair=yes", + "alsoShowIf": "bicycle:repair=yes", + "addExtraTags": ["bicycle:repair="], + "then": { + "en": "Electronic devices are repaired here" + }, + "ifnot": "service:electronics:repair=no", + "icon": "./assets/layers/recycling/small_electrical_appliances.svg" + + }, + { + "if": "service:furniture:repair=yes", + "alsoShowIf": "furniture:repair=yes", + "addExtraTags": ["furniture:repair="], + "then": { + "en": "Furniture is repaired here" + }, + "ifnot": "service:furniture:repair=no", + "icon": "./assets/layers/recycling/furniture.svg" + + }, + { + "if": "service:clothes:repair=yes", + "alsoShowIf": "clothes:repair=yes", + "addExtraTags": ["clothes:repair="], + "then": { + "en": "Clothes are repaired here" + }, + "ifnot": "service:clothes:repair=no", + "icon": "./assets/layers/recycling/clothes.svg" + } + ], + "multiAnswer": true + } + ], + "lineRendering": [ + { + "width": 1, + "color": "blue" + } + ], + "id": "assisted_repair", + "name": { + "en": "Repair cafés and assisted repair workshops" + }, + "description": { + "en": "A self-assisted workshop is a location where people can come and repair their goods with help of volunteers and with the tools available at the given location. A repair café is a type of event organized regularly along the same principles." + }, + "source": { + "osmTags": "repair=assisted_self_service" + }, + "title": { + "render": { + "en": "Workshop for assisted repair" + }, + "mappings": [ + { + "if": "name~*", + "then": { + "en": "{name}" + } + } + ] + }, + "deletion": {}, + "allowMove": true, + "presets": [ + { + "title": { + "en": "an assisted repair workshop" + }, + "tags": [ + "amenity=workshop", + "repair=assisted_self_service" + ], + "description": { + "en": "A location with a permanent workshop where people can come to repair items with the help of volunteers." + } + } + ] +} diff --git a/assets/layers/ghost_bike/ghost_bike.json b/assets/layers/ghost_bike/ghost_bike.json index 4718beb7e..9174d1ee8 100644 --- a/assets/layers/ghost_bike/ghost_bike.json +++ b/assets/layers/ghost_bike/ghost_bike.json @@ -141,12 +141,7 @@ } ], "tagRenderings": [ - { - "id": "ghost-bike-explanation", - "render": { - "*": "{preset_description()}" - } - }, + "preset_description", "images", { "question": { diff --git a/assets/layers/questions/questions.json b/assets/layers/questions/questions.json index d96e48314..10dfaf073 100644 --- a/assets/layers/questions/questions.json +++ b/assets/layers/questions/questions.json @@ -2884,6 +2884,12 @@ } } ] + }, + { + "id": "preset_description", + "render": { + "*": "{preset_description()}" + } } ] } diff --git a/assets/layers/recycling/furniture.svg b/assets/layers/recycling/furniture.svg new file mode 100644 index 000000000..072ded709 --- /dev/null +++ b/assets/layers/recycling/furniture.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/recycling/furniture.svg.license b/assets/layers/recycling/furniture.svg.license new file mode 100644 index 000000000..9e4e1ee25 --- /dev/null +++ b/assets/layers/recycling/furniture.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: https://pixabay.com/users/clker-free-vector-images-3736/ +SPDX-License-Identifier: CC0 \ No newline at end of file diff --git a/assets/layers/recycling/license_info.json b/assets/layers/recycling/license_info.json index 1d9f7fd52..dbe8aecdc 100644 --- a/assets/layers/recycling/license_info.json +++ b/assets/layers/recycling/license_info.json @@ -79,6 +79,16 @@ "https://thenounproject.com/icon/tube-fluorescent-light-3756518/" ] }, + { + "path": "furniture.svg", + "license": "CC0-1.0", + "authors": [ + "https://pixabay.com/users/clker-free-vector-images-3736/" + ], + "sources": [ + "https://pixabay.com/es/vectors/sof%C3%A1-sill%C3%B3n-muebles-silla-azul-29277/" + ] + }, { "path": "garden_waste.svg", "license": "CC-BY-SA-4.0", diff --git a/assets/layers/tool_library/tool_library.json b/assets/layers/tool_library/tool_library.json new file mode 100644 index 000000000..015472846 --- /dev/null +++ b/assets/layers/tool_library/tool_library.json @@ -0,0 +1,151 @@ +{ + "credits": "Not logged in", + "minzoom": 3, + "pointRendering": [ + { + "location": [ + "point", + "centroid" + ], + "marker": [ + { + "icon": "pin", + "color": "#93E800FF" + }, + { + "icon": "./assets/layers/bike_repair_station/repair_station.svg" + } + ], + "anchor": "bottom", + "label": { + "mappings": [ + { + "if": "name~*", + "then": { + "en": "{name}" + } + } + ] + }, + "labelCssClasses": "bg-white rounded px-2" + } + ], + "tagRenderings": [ + "images", + "contact", + "facebook", + "opening_hours_by_appointment", + { + "question": { + "en": "Is a membership required to borrow tools here?" + }, + "id": "membership", + "mappings": [ + { + "then": { + "en": "No membership is required to borrow tools here" + }, + "if": "membership=no" + }, + { + "if": "membership=required", + "then": { + "en": "A membership is required to use this tool library" + } + } + ] + }, + { + "question": { + "en": "How much does a membership cost?" + }, + "id": "membership_charge", + "questionHint": { + "en": "There might be many formulas. In case of doubt, pick the most common one" + }, + "freeform": { + "key": "charge:membership", + "type": "currency" + }, + "condition": "membership=required", + "render": { + "en": "A membership costs {charge:membership}" + } + }, + + { + "question": { + "en": "Is a fee asked to borrow tools?" + }, + "id": "fee", + "mappings": [ + { + "if": "fee=no", + "then": { + "en": "Borrowing tools is free" + } + }, + { + "if": "fee=yes", + "then": { + "en": "A fee is asked when borrowing tools" + } + }, + { + "if": "fee=donation", + "then": { + "en": "A donation can be given when borrowing tools" + } + } + ], + "questionHint": { + "en": "If a membership is required, we assume that the membership is already paid for." + } + } + ], + "lineRendering": [ + { + "width": 1, + "color": "blue" + } + ], + "id": "tool_library", + "name": { + "en": "Tool libraries" + }, + "description": { + "en": "A tool library is a place where people from the general public can borrow tools" + }, + "source": { + "osmTags": "amenity=tool_library" + }, + "title": { + "render": { + "en": "Tool library {name}" + } + }, + "presets": [ + { + "title": { + "en": "a tool library" + }, + "tags": [ + "amenity=tool_library" + ], + "description": { + "en": "A tool library is a place where people from the general public can borrow tools" + } + } + ], + "deletion": true, + "allowMove": true, + "units": [ + { + "charge:membership": { + "inverted": true, + "quantity": "duration", + "denominations": ["years","months","weeks"] + } + } + ] +} diff --git a/assets/themes/circular_economy/circular.svg b/assets/themes/circular_economy/circular.svg new file mode 100644 index 000000000..07fe878a2 --- /dev/null +++ b/assets/themes/circular_economy/circular.svg @@ -0,0 +1,74 @@ + + + +image/svg+xml diff --git a/assets/themes/circular_economy/circular.svg.license b/assets/themes/circular_economy/circular.svg.license new file mode 100644 index 000000000..ed0288300 --- /dev/null +++ b/assets/themes/circular_economy/circular.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Pieter Vander Vennet +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/themes/circular_economy/circular_economy.json b/assets/themes/circular_economy/circular_economy.json new file mode 100644 index 000000000..55ccd45d9 --- /dev/null +++ b/assets/themes/circular_economy/circular_economy.json @@ -0,0 +1,66 @@ +{ + "id": "circular_economy", + "title": { + "en": "Circular economy" + }, + "icon": "./assets/themes/circular_economy/circular.svg", + "description": { + "en": "Various items which help people to share, reuse or recycle." + }, + "layers": [ + { + "builtin": "public_bookcase", + "override": { + "minzoom": 14 + } + }, + { + "builtin": [ + "bicycle_library" + ], + "override": { + "minzoom": 10 + } + }, + "bike_repair_station", + "tool_library", + "assisted_repair", + { + "builtin": "shops", + "override": { + "minzoom": 10, + "id": "shops_second_hand", + "=name": { + "en": "Second hand shops" + }, + "filter": null, + "source": { + "=osmTags": { + "or": [ + "shop=second_hand", + "shop=charity", + { + "and": [ + "shop~*", + "second_hand=yes", + "second_hand=only" + ] + } + ] + } + }, + "presets": null + } + }, + { + "builtin": "shops", + "override": { + "minzoom": 17, + "name": null, + "filter": { + "sameAs": "shops_second_hand" + } + } + } + ] +} diff --git a/assets/themes/circular_economy/license_info.json b/assets/themes/circular_economy/license_info.json new file mode 100644 index 000000000..62d42cc6e --- /dev/null +++ b/assets/themes/circular_economy/license_info.json @@ -0,0 +1,10 @@ +[ + { + "path": "circular.svg", + "license": "CC0-1.0", + "authors": [ + "Pieter Vander Vennet" + ], + "sources": [] + } +] \ 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 1054fd6f4..fbcd5c35c 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -189,6 +189,10 @@ "if": "theme=charging_stations", "then": "./assets/themes/charging_stations/logo.svg" }, + { + "if": "theme=circular_economy", + "then": "./assets/themes/circular_economy/circular.svg" + }, { "if": "theme=climbing", "then": "./assets/themes/climbing/climbing_icon.svg" diff --git a/package.json b/package.json index 31bc03af1..0faab7f7b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mapcomplete", - "version": "0.44.11", + "version": "0.44.12", "repository": "https://github.com/pietervdvn/MapComplete", "description": "A small website to edit OSM easily", "bugs": "https://github.com/pietervdvn/MapComplete/issues", diff --git a/scripts/importscripts/cleanRepair.ts b/scripts/importscripts/cleanRepair.ts new file mode 100644 index 000000000..4c21abe09 --- /dev/null +++ b/scripts/importscripts/cleanRepair.ts @@ -0,0 +1,232 @@ +import Script from "../Script" +import { writeFileSync } from "fs" +import { Feature, Geometry } from "geojson" +import { OsmObject } from "../../src/Logic/Osm/OsmObject" +import OsmObjectDownloader from "../../src/Logic/Osm/OsmObjectDownloader" +import { Changes } from "../../src/Logic/Osm/Changes" +import { ImmutableStore } from "../../src/Logic/UIEventSource" +import { OsmConnection } from "../../src/Logic/Osm/OsmConnection" +import ChangeTagAction from "../../src/Logic/Osm/Actions/ChangeTagAction" +import { Tag } from "../../src/Logic/Tags/Tag" +import { ChangeDescription } from "../../src/Logic/Osm/Actions/ChangeDescription" +import { GeoOperations } from "../../src/Logic/GeoOperations" +import { Overpass } from "../../src/Logic/Osm/Overpass" +import { TagUtils } from "../../src/Logic/Tags/TagUtils" +import { BBox } from "../../src/Logic/BBox" + +export default class CleanRepair extends Script { + + constructor() { + super("Cleans 'repair'-tags for mass retagging") + } + + + async main(args: string[]) { + const path = args[0] + console.log("Loading", path) + const criteria = TagUtils.Tag({ + and: [ + "repair~*", + "repair!=no", + "repair!=yes", + "repair!=brand", + "repair!=only", + "repair!=only_sold", + "repair!=assisted_self_service" + ] + }) + const overpass = new Overpass(criteria, [], + "https://overpass-api.de/api/interpreter" + ) + const data: Feature> [] = (await overpass.queryGeoJson(BBox.global))[0].features + console.log("Got", data.length, "features; sample", data[0]) + const changes = new Changes({ + dryRun: new ImmutableStore(true), + osmConnection: new OsmConnection({ + dryRun: new ImmutableStore(true) + }) + }) + + const metakeys = ["id", "version", "changeset", "user", "uid", "timestamp"] + + const replace = { + "phone": "mobile_phone", + "phones": "mobile_phone", + "mobile": "mobile_phone", + "cellphone": "mobile_phone", + "pc": "computers", + "mobile_phones": "mobile_phone", + "mobilephones": "mobile_phone", + "mobilephone": "mobile_phone", + "clocks": "clock", + "elektronik": "electronics", + "tires": "tyres", + "welcome": "yes", + "tyre": "tyres", + "electronic_products": "electronics", + "shoe": "shoes", + "pc_repairs": "computer", + "computers": "computer", + "body_construction": "body_work", + "body": "body_work", + + "body_repairer": "body_work", + "instruments": "musical_instrument", + "service": "yes", + "punture": "tyres", + "electricity": "electronics", + "self_service": "assisted_self_repair", + "paint": "bodywork", + "paint shop": "bodywork", + "paint_shop": "bodywork", + "lawnmower": "lawn_mower", + "aircon": "air_conditioning", + "*": "yes", + "ammeublement": "furniture", + "all": "yes", + "appliances": "appliance", + "electronic": "electronics", "escooter": "electric_scooter", + "aviation maintenance, repair, and_overhaul": "airplane", + "aviation_maintenance": "airplanes", + "bags": "bag", + "boats": "boat", + "boilers": "boiler", + "breaks": "brakes", + "car": "cars", + "tv": "television", + "clothing": "clothes", + "coat_of_lacquer": "body_work", + "cycle": "bicycle", + "cars": "car", + "blacharstwo": "tin", "lakiernictwo": "body_work", + "tire": "tyres", "powder_coating": "body_work", + "leather_products": "leather", + "motocycle": "motorcycle", + "motor": "motorcycle", "motoo": "motorcycle", + "motorbike": "motorcycle", "motorcycle_repair": "motorcycle", "motorsports": "motorcycle", + "printers": "printer", + "tyres24": "tyres", + "paintings": "painting", + "paintwork": "body_work", + "pumps": "pump", + "shoes:yes": "shoes", + "wheel": "tyres", + "wheels": "tyres", + "vacuum": "vacuum_cleaner", + "glass": "car_glassj" + } + + const brands = ["garage", "audi", "renault", "apple", "honda", "ducati", "ford", "mazda","garage_renault_aie"] + + const valid = ["train", "tv", "jewelry", "scooter", ...Object.values(replace), "watch", "oldtimer", "car", "bicycle", "boat", "windbreaker", + "agricultural", "alternator", "antiques", "atv", "auto", + "aviation maintenance", "bag", "bags", + "battery", "bicyle", "borehole", "building", + "camera", "car_glass", "caravan", + "carpenter", "coffee_machine", "construction machinery", "cycle", + "dentures", "ducati", "electric motor", + "electric_bike", "electric_scooter", "espresso_machines", "exhaust", + "fire_extinguishers", "fountain_pen", "fridge", + "garden_machinery", "gas appliances", "generator", + "glasses", "golfcart", "guitar", + "hammock", "hardware", "heating pumps", + "hifi", "hvac", "installation", "jewellery", "keys", "kick_scooter", + "kitesurfing", "tools", "toys", "tractor", + "trailer", "transformer", "truck", + "typewriter", "sail", "sewing_machine", "ship", "picture", "pillow", "plastic", + "cash_register", "cnc", "laptop", + "laundry_machines", + "photo_camera", "photocopier", "piano", + "power_tools", "pressure_gauges", "printer", + , "snowboard", "snowmobile", "starter", "machines", "mainframe", + "outboard_motor", + "video", "washing_machine", "ski", "radiator", + "radio", "refrigerator", + "rv", "ski", "window", "zipper", "weighing_scale", + "small_electric_vehicle" + + ].map(s => s.replace(/ /g, "_")) + + + const skip = ["yes", "no", "only", "brand", "assisted_self_repair", "only_sold"] + const dloader = new OsmObjectDownloader() + const rm = ["50243147100015", "81342677200048", "and overhaul", "repair", "unset", "сервисный_центр", "taller_de_michu", "quitandinha_g_&_a", "mechanika"].map(v => v.replace(/ /g, "_")) + const objects: OsmObject[] = [] + const changesToMake: ChangeDescription [] = [] + const first = GeoOperations.centerpointCoordinates(data[0]) + for (const f of data) { + if (GeoOperations.distanceBetween(first, GeoOperations.centerpointCoordinates(f)) > 2500000) { + continue + } + let keyRaw = f.properties.repair + keyRaw = replace[keyRaw] ?? keyRaw + if (brands.some(br => keyRaw.toLowerCase().indexOf(br.trim()) >= 0)) { + f.properties.repair = "brand" + } if(skip.indexOf(keyRaw) >= 0){ + f.properties.repair = keyRaw + } else { + + const r = keyRaw.replace(/\/|,/g, ";").split(";").map(k => k.trim().replace(/ /g, "_").toLowerCase()) + for (let key of r) { + key = replace[key] ?? key + + if (rm.indexOf(key) >= 0) { + delete f.properties.repair + continue + } + + f.properties[key + ":repair"] = "yes" + delete f.properties.repair + + } + + } + if (f.properties.service === "repair") { + delete f.properties.service + f.properties.repair = "yes" + } + const id = f.properties["id"] + const osm = await dloader.DownloadObjectAsync(id) + if (osm === "deleted") { + continue + } + objects.push(osm) + for (const key in f.properties) { + if (metakeys.indexOf(key) >= 0) { + continue + } + const value = f.properties[key] + const ct = await new ChangeTagAction(id, new Tag(key, value), f.properties, { + changeType: "fix", + theme: "script" + }).CreateChangeDescriptions() + changesToMake.push(...ct) + console.log(ct.map(cd => cd.tags)) + if (f.properties.repair === undefined) { + const ct = await new ChangeTagAction(id, new Tag("repair", ""), f.properties, { + changeType: "fix", + theme: "script" + }).CreateChangeDescriptions() + changesToMake.push(...ct) + } + } + } + + const + changedObjects = changes.CreateChangesetObjects(changesToMake, objects) + + const + osc = Changes.createChangesetFor("", changedObjects) + + writeFileSync( + "Cleanup.osc" + , + osc + , + "utf8" + ) + + } +} + +new CleanRepair().run() diff --git a/src/Models/Constants.ts b/src/Models/Constants.ts index 067159fa8..ec32ad16e 100644 --- a/src/Models/Constants.ts +++ b/src/Models/Constants.ts @@ -127,6 +127,7 @@ export default class Constants { "clock", "close", "crosshair", + "gear", "help", "home", "invalid", diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index 263dd3c16..65daef0ed 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -366,9 +366,17 @@ export default class TagRenderingConfig { throw `${ctx}: Invalid mapping: "if" is defined as an array. Use {"and": } or {"or": } instead` } - if (mapping.addExtraTags !== undefined && multiAnswer) { - throw `${ctx}: Invalid mapping: got a multi-Answer with addExtraTags; this is not allowed` + if(mapping.addExtraTags !== undefined && !Array.isArray(mapping.addExtraTags)){ + throw `${ctx}.addExtraTags: expected a list, but got a ${typeof mapping.addExtraTags}` + } + if (mapping.addExtraTags !== undefined && multiAnswer) { + const usedKeys = mapping.addExtraTags?.flatMap(et => TagUtils.Tag(et).usedKeys()) + if(usedKeys.some(key => TagUtils.Tag(mapping.if).usedKeys().indexOf(key ) > 0)){ + throw `${ctx}: Invalid mapping: got a multi-Answer with addExtraTags which also modifies one of the keys; this is not allowed` + } + } + let hideInAnswer: boolean | TagsFilter = false if (typeof mapping.hideInAnswer === "boolean") { diff --git a/src/UI/Map/Icon.svelte b/src/UI/Map/Icon.svelte index fc59cac53..5a001b6a5 100644 --- a/src/UI/Map/Icon.svelte +++ b/src/UI/Map/Icon.svelte @@ -36,6 +36,7 @@ import Bug from "../../assets/svg/Bug.svelte" import Cross_bottom_right from "../../assets/svg/Cross_bottom_right.svelte" import { Utils } from "../../Utils" + import Gear from "../../assets/svg/Gear.svelte" /** * Renders a single icon. @@ -126,6 +127,8 @@ {:else if icon === "addSmall"} + {:else if icon === "gear"} + {:else if icon === "link"} {:else if icon === "popout"} diff --git a/src/UI/StudioGUI.svelte b/src/UI/StudioGUI.svelte index f4b947961..883d17b21 100644 --- a/src/UI/StudioGUI.svelte +++ b/src/UI/StudioGUI.svelte @@ -278,9 +278,10 @@ -
+
Enable more options (expert mode) MapComplete version {version} +
{$uid}
{:else if state === "edit_layer"} @@ -307,6 +308,7 @@

Your layers

+
Your id is {$uid}

Layers by other contributors

diff --git a/src/Utils.ts b/src/Utils.ts index c0da44571..a22766235 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -630,7 +630,13 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be for (const key in source) { if (key.startsWith("=")) { - const trimmedKey = key.substr(1) + const trimmedKey = key.substring(1) + target[trimmedKey] = source[key] + continue + } + + if (key.endsWith("=")) { + const trimmedKey = key.substring(0, key.length - 1) target[trimmedKey] = source[key] continue }