diff --git a/Docs/Layers/bike_parking.md b/Docs/Layers/bike_parking.md index 4ed03e6d3..6dc851a3d 100644 --- a/Docs/Layers/bike_parking.md +++ b/Docs/Layers/bike_parking.md @@ -62,7 +62,7 @@ Elements must match the expression ** [bicycle_parking](https://wiki.openstreetmap.org/wiki/Key:bicycle_parking) | [string](../SpecialInputElements.md#string) | [stands](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dstands) [safe_loops](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dsafe_loops) [wall_loops](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dwall_loops) [handlebar_holder](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dhandlebar_holder) [rack](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Drack) [two_tier](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dtwo_tier) [shed](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dshed) [bollard](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dbollard) [floor](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dfloor) [lockers](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dlockers) | +| [bicycle_parking](https://wiki.openstreetmap.org/wiki/Key:bicycle_parking) | [string](../SpecialInputElements.md#string) | [stands](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dstands) [safe_loops](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dsafe_loops) [wall_loops](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dwall_loops) [handlebar_holder](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dhandlebar_holder) [rack](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Drack) [two_tier](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dtwo_tier) [shed](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dshed) [bollard](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dbollard) [floor](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dfloor) [lockers](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dlockers) [lean_and_stick](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dlean_and_stick) | | [location](https://wiki.openstreetmap.org/wiki/Key:location) | Multiple choice | [underground](https://wiki.openstreetmap.org/wiki/Tag:location%3Dunderground) [surface](https://wiki.openstreetmap.org/wiki/Tag:location%3Dsurface) [rooftop](https://wiki.openstreetmap.org/wiki/Tag:location%3Drooftop) | | [covered](https://wiki.openstreetmap.org/wiki/Key:covered) | Multiple choice | [yes](https://wiki.openstreetmap.org/wiki/Tag:covered%3Dyes) [no](https://wiki.openstreetmap.org/wiki/Tag:covered%3Dno) | | [capacity](https://wiki.openstreetmap.org/wiki/Key:capacity) | [nat](../SpecialInputElements.md#nat) | | @@ -98,6 +98,7 @@ The question is `What is the type of this bicycle parking?` - *Bollard* is shown if with bicycle_parking=bollard - *An area on the floor which is marked for bicycle parking* is shown if with bicycle_parking=floor - *A locker - the bicycles are enclosed completely individually or with a few bicycles together. The locker is too small to fit a person standing..* is shown if with bicycle_parking=lockers + - *A lean-to bracket with possibility to use a lock through eyelet. The seat tube can be held by the stand by an anchor* is shown if with bicycle_parking=lean_and_stick ### Underground? @@ -128,7 +129,7 @@ The question is `Who can use this bicycle parking?` - *Publicly accessible* is shown if with access=yes - *Access is primarily for visitors to a business* is shown if with access=customers - *Access is limited to members of a school, company or organisation* is shown if with access=members - - *Access is limited to members of a school, company or organisation* is shown if with access=private + - *Private bicycle parking which is never available to the public, also not via a membership fee* is shown if with access=private ### fee diff --git a/Docs/Layers/bike_shop.md b/Docs/Layers/bike_shop.md index e90f2bf4d..9e220d842 100644 --- a/Docs/Layers/bike_shop.md +++ b/Docs/Layers/bike_shop.md @@ -15,6 +15,7 @@ A shop specifically selling bicycles or related items - [images](#images) - [reviews](#reviews) - [shops-name](#shops-name) + - [shop_types](#shop_types) - [brand](#brand) - [second_hand](#second_hand) - [opening_hours](#opening_hours) @@ -32,9 +33,18 @@ A shop specifically selling bicycles or related items - [bike_second_hand](#bike_second_hand) - [repairs_bikes](#repairs_bikes) - [bicycle_rental](#bicycle_rental) + - [bicycle-types](#bicycle-types) + - [rental-capacity-city_bike](#rental-capacity-city_bike) + - [rental-capacity-ebike](#rental-capacity-ebike) + - [rental-capacity-kid_bike](#rental-capacity-kid_bike) + - [rental-capacity-bmx](#rental-capacity-bmx) + - [rental-capacity-mtb](#rental-capacity-mtb) + - [rental-capacity-bicycle_pannier](#rental-capacity-bicycle_pannier) + - [rental-capacity-tandem_bicycle](#rental-capacity-tandem_bicycle) - [bike_pump_service](#bike_pump_service) - [bike_repair_tools](#bike_repair_tools) - [bike_wash](#bike_wash) + - [bike_cleaning-service_bicycle_cleaning_charge](#bike_cleaning-service_bicycle_cleaning_charge) - [internet](#internet) - [internet-fee](#internet-fee) - [internet-ssid](#internet-ssid) @@ -78,6 +88,7 @@ Elements must match **any** of the following expressions: | attribute | type | values which are supported by this layer | -----|-----|----- | | [name](https://wiki.openstreetmap.org/wiki/Key:name) | [string](../SpecialInputElements.md#string) | | +| [shop](https://wiki.openstreetmap.org/wiki/Key:shop) | [string](../SpecialInputElements.md#string) | [bicycle_rental](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dbicycle_rental) [agrarian](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dagrarian) [alcohol](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dalcohol) [anime](https://wiki.openstreetmap.org/wiki/Tag:shop%3Danime) [antiques](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dantiques) [appliance](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dappliance) [art](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dart) [baby_goods](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dbaby_goods) [bag](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dbag) [bakery](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dbakery) [bathroom_furnishing](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dbathroom_furnishing) [beauty](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dbeauty) [bed](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dbed) [beverages](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dbeverages) [bicycle](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dbicycle) [boat](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dboat) [bookmaker](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dbookmaker) [books](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dbooks) [brewing_supplies](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dbrewing_supplies) [butcher](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dbutcher) [camera](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcamera) [candles](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcandles) [cannabis](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcannabis) [car](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcar) [car_parts](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcar_parts) [car_repair](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcar_repair) [caravan](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcaravan) [carpet](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcarpet) [catalogue](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcatalogue) [charity](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcharity) [cheese](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcheese) [chemist](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dchemist) [chocolate](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dchocolate) [clothes](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dclothes) [coffee](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcoffee) [collector](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcollector) [computer](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcomputer) [confectionery](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dconfectionery) [convenience](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dconvenience) [copyshop](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcopyshop) [cosmetics](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcosmetics) [country_store](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcountry_store) [craft](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcraft) [curtain](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dcurtain) [dairy](https://wiki.openstreetmap.org/wiki/Tag:shop%3Ddairy) [deli](https://wiki.openstreetmap.org/wiki/Tag:shop%3Ddeli) [department_store](https://wiki.openstreetmap.org/wiki/Tag:shop%3Ddepartment_store) [doityourself](https://wiki.openstreetmap.org/wiki/Tag:shop%3Ddoityourself) [doors](https://wiki.openstreetmap.org/wiki/Tag:shop%3Ddoors) [dry_cleaning](https://wiki.openstreetmap.org/wiki/Tag:shop%3Ddry_cleaning) [e-cigarette](https://wiki.openstreetmap.org/wiki/Tag:shop%3De-cigarette) [electrical](https://wiki.openstreetmap.org/wiki/Tag:shop%3Delectrical) [electronics](https://wiki.openstreetmap.org/wiki/Tag:shop%3Delectronics) [erotic](https://wiki.openstreetmap.org/wiki/Tag:shop%3Derotic) [fabric](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dfabric) [farm](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dfarm) [fashion_accessories](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dfashion_accessories) [fireplace](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dfireplace) [fishing](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dfishing) [flooring](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dflooring) [florist](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dflorist) [frame](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dframe) [frozen_food](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dfrozen_food) [fuel](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dfuel) [funeral_directors](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dfuneral_directors) [furniture](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dfurniture) [games](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dgames) [garden_centre](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dgarden_centre) [gas](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dgas) [general](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dgeneral) [gift](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dgift) [greengrocer](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dgreengrocer) [hairdresser](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dhairdresser) [hairdresser_supply](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dhairdresser_supply) [hardware](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dhardware) [health_food](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dhealth_food) [hearing_aids](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dhearing_aids) [herbalist](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dherbalist) [hifi](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dhifi) [honey](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dhoney) [household_linen](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dhousehold_linen) [houseware](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dhouseware) [hunting](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dhunting) [interior_decoration](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dinterior_decoration) [jewelry](https://wiki.openstreetmap.org/wiki/Tag:shop%3Djewelry) [kiosk](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dkiosk) [kitchen](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dkitchen) [laundry](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dlaundry) [leather](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dleather) [lighting](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dlighting) [locksmith](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dlocksmith) [lottery](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dlottery) [mall](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dmall) [massage](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dmassage) [medical_supply](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dmedical_supply) [military_surplus](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dmilitary_surplus) [mobile_phone](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dmobile_phone) [model](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dmodel) [money_lender](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dmoney_lender) [motorcycle](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dmotorcycle) [motorcycle_repair](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dmotorcycle_repair) [music](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dmusic) [musical_instrument](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dmusical_instrument) [newsagent](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dnewsagent) [nutrition_supplements](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dnutrition_supplements) [nuts](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dnuts) [optician](https://wiki.openstreetmap.org/wiki/Tag:shop%3Doptician) [outdoor](https://wiki.openstreetmap.org/wiki/Tag:shop%3Doutdoor) [outpost](https://wiki.openstreetmap.org/wiki/Tag:shop%3Doutpost) [paint](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dpaint) [party](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dparty) [pasta](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dpasta) [pastry](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dpastry) [pawnbroker](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dpawnbroker) [perfumery](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dperfumery) [pet](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dpet) [pet_grooming](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dpet_grooming) [photo](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dphoto) [pottery](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dpottery) [printer_ink](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dprinter_ink) [psychic](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dpsychic) [pyrotechnics](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dpyrotechnics) [radiotechnics](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dradiotechnics) [religion](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dreligion) [rental](https://wiki.openstreetmap.org/wiki/Tag:shop%3Drental) [repair](https://wiki.openstreetmap.org/wiki/Tag:shop%3Drepair) [rice](https://wiki.openstreetmap.org/wiki/Tag:shop%3Drice) [scuba_diving](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dscuba_diving) [seafood](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dseafood) [second_hand](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dsecond_hand) [sewing](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dsewing) [shoe_repair](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dshoe_repair) [shoes](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dshoes) [spices](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dspices) [sports](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dsports) [stationery](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dstationery) [storage_rental](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dstorage_rental) [supermarket](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dsupermarket) [swimming_pool](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dswimming_pool) [tailor](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dtailor) [tattoo](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dtattoo) [tea](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dtea) [telecommunication](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dtelecommunication) [ticket](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dticket) [tiles](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dtiles) [tobacco](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dtobacco) [tool_hire](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dtool_hire) [toys](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dtoys) [trade](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dtrade) [travel_agency](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dtravel_agency) [trophy](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dtrophy) [tyres](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dtyres) [vacuum_cleaner](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dvacuum_cleaner) [variety_store](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dvariety_store) [video](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dvideo) [video_games](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dvideo_games) [watches](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dwatches) [water](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dwater) [water_sports](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dwater_sports) [weapons](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dweapons) [wholesale](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dwholesale) [wigs](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dwigs) [window_blind](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dwindow_blind) [wine](https://wiki.openstreetmap.org/wiki/Tag:shop%3Dwine) | | [brand](https://wiki.openstreetmap.org/wiki/Key:brand) | [string](../SpecialInputElements.md#string) | | | [second_hand](https://wiki.openstreetmap.org/wiki/Key:second_hand) | Multiple choice | [only](https://wiki.openstreetmap.org/wiki/Tag:second_hand%3Donly) [yes](https://wiki.openstreetmap.org/wiki/Tag:second_hand%3Dyes) [no](https://wiki.openstreetmap.org/wiki/Tag:second_hand%3Dno) | | [opening_hours](https://wiki.openstreetmap.org/wiki/Key:opening_hours) | [opening_hours](../SpecialInputElements.md#opening_hours) | | @@ -90,9 +101,18 @@ Elements must match **any** of the following expressions: | [service:bicycle:second_hand](https://wiki.openstreetmap.org/wiki/Key:service:bicycle:second_hand) | Multiple choice | [yes](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:second_hand%3Dyes) [no](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:second_hand%3Dno) [only](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:second_hand%3Donly) | | [service:bicycle:repair](https://wiki.openstreetmap.org/wiki/Key:service:bicycle:repair) | Multiple choice | [yes](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:repair%3Dyes) [no](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:repair%3Dno) [only_sold](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:repair%3Donly_sold) [brand](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:repair%3Dbrand) | | [service:bicycle:rental](https://wiki.openstreetmap.org/wiki/Key:service:bicycle:rental) | Multiple choice | [yes](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:rental%3Dyes) [no](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:rental%3Dno) | +| [rental](https://wiki.openstreetmap.org/wiki/Key:rental) | [string](../SpecialInputElements.md#string) | [city_bike](https://wiki.openstreetmap.org/wiki/Tag:rental%3Dcity_bike) [ebike](https://wiki.openstreetmap.org/wiki/Tag:rental%3Debike) [bmx](https://wiki.openstreetmap.org/wiki/Tag:rental%3Dbmx) [mtb](https://wiki.openstreetmap.org/wiki/Tag:rental%3Dmtb) [kid_bike](https://wiki.openstreetmap.org/wiki/Tag:rental%3Dkid_bike) [tandem](https://wiki.openstreetmap.org/wiki/Tag:rental%3Dtandem) [racebike](https://wiki.openstreetmap.org/wiki/Tag:rental%3Dracebike) [bike_helmet](https://wiki.openstreetmap.org/wiki/Tag:rental%3Dbike_helmet) [cargo_bike](https://wiki.openstreetmap.org/wiki/Tag:rental%3Dcargo_bike) | +| [capacity:city_bike](https://wiki.openstreetmap.org/wiki/Key:capacity:city_bike) | [pnat](../SpecialInputElements.md#pnat) | | +| [capacity:ebike](https://wiki.openstreetmap.org/wiki/Key:capacity:ebike) | [pnat](../SpecialInputElements.md#pnat) | | +| [capacity:kid_bike](https://wiki.openstreetmap.org/wiki/Key:capacity:kid_bike) | [pnat](../SpecialInputElements.md#pnat) | | +| [capacity:bmx](https://wiki.openstreetmap.org/wiki/Key:capacity:bmx) | [pnat](../SpecialInputElements.md#pnat) | | +| [capacity:mtb](https://wiki.openstreetmap.org/wiki/Key:capacity:mtb) | [pnat](../SpecialInputElements.md#pnat) | | +| [capacity:bicycle_pannier](https://wiki.openstreetmap.org/wiki/Key:capacity:bicycle_pannier) | [pnat](../SpecialInputElements.md#pnat) | | +| [capacity:tandem_bicycle](https://wiki.openstreetmap.org/wiki/Key:capacity:tandem_bicycle) | [pnat](../SpecialInputElements.md#pnat) | | | [service:bicycle:pump](https://wiki.openstreetmap.org/wiki/Key:service:bicycle:pump) | Multiple choice | [yes](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:pump%3Dyes) [no](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:pump%3Dno) [separate](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:pump%3Dseparate) | | [service:bicycle:diy](https://wiki.openstreetmap.org/wiki/Key:service:bicycle:diy) | Multiple choice | [yes](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:diy%3Dyes) [no](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:diy%3Dno) [only_sold](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:diy%3Donly_sold) | | [service:bicycle:cleaning](https://wiki.openstreetmap.org/wiki/Key:service:bicycle:cleaning) | Multiple choice | [yes](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:cleaning%3Dyes) [diy](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:cleaning%3Ddiy) [no](https://wiki.openstreetmap.org/wiki/Tag:service:bicycle:cleaning%3Dno) | +| [service:bicycle:cleaning:charge](https://wiki.openstreetmap.org/wiki/Key:service:bicycle:cleaning:charge) | [string](../SpecialInputElements.md#string) | | | [internet_access](https://wiki.openstreetmap.org/wiki/Key:internet_access) | Multiple choice | [wlan](https://wiki.openstreetmap.org/wiki/Tag:internet_access%3Dwlan) [no](https://wiki.openstreetmap.org/wiki/Tag:internet_access%3Dno) [terminal](https://wiki.openstreetmap.org/wiki/Tag:internet_access%3Dterminal) [wired](https://wiki.openstreetmap.org/wiki/Tag:internet_access%3Dwired) [terminal;wifi](https://wiki.openstreetmap.org/wiki/Tag:internet_access%3Dterminal;wifi) | | [internet_access:fee](https://wiki.openstreetmap.org/wiki/Key:internet_access:fee) | Multiple choice | [yes](https://wiki.openstreetmap.org/wiki/Tag:internet_access:fee%3Dyes) [no](https://wiki.openstreetmap.org/wiki/Tag:internet_access:fee%3Dno) [customers](https://wiki.openstreetmap.org/wiki/Tag:internet_access:fee%3Dcustomers) | | [internet_access:ssid](https://wiki.openstreetmap.org/wiki/Key:internet_access:ssid) | [string](../SpecialInputElements.md#string) | [Telekom](https://wiki.openstreetmap.org/wiki/Tag:internet_access:ssid%3DTelekom) | @@ -117,6 +137,179 @@ _This tagrendering has no question and is thus read-only_ The question is `What is the name of this shop?` *This shop is called {name}* is shown if `name` is set +### shop_types + +The question is `What kind of shop is this?` +*This is a {shop}* is shown if `shop` is set + + - *Bicycle rental shop* is shown if with shop=bicycle_rental + - *Farm Supply Shop* is shown if with shop=agrarian + - *Liquor Store* is shown if with shop=alcohol + - *Anime / Manga Shop* is shown if with shop=anime + - *Antique Shop* is shown if with shop=antiques + - *Appliance Store* is shown if with shop=appliance + - *Art Store* is shown if with shop=art + - *Baby Goods Store* is shown if with shop=baby_goods + - *Bag/Luggage Store* is shown if with shop=bag + - *Bakery* is shown if with shop=bakery + - *Bathroom Furnishing Store* is shown if with shop=bathroom_furnishing + - *Beauty Shop* is shown if with shop=beauty + - *Bedding/Mattress Store* is shown if with shop=bed + - *Beverage Store* is shown if with shop=beverages + - *Bicycle Shop* is shown if with shop=bicycle + - *Boat Store* is shown if with shop=boat + - *Bookmaker* is shown if with shop=bookmaker + - *Bookstore* is shown if with shop=books + - *Brewing Supply Store* is shown if with shop=brewing_supplies + - *Butcher* is shown if with shop=butcher + - *Camera Equipment Store* is shown if with shop=camera + - *Candle Shop* is shown if with shop=candles + - *Cannabis Shop* is shown if with shop=cannabis + - *Car Dealership* is shown if with shop=car + - *Car Parts Store* is shown if with shop=car_parts + - *Car Repair Shop* is shown if with shop=car_repair + - *RV Dealership* is shown if with shop=caravan + - *Carpet Store* is shown if with shop=carpet + - *Catalog Shop* is shown if with shop=catalogue + - *Charity Store* is shown if with shop=charity + - *Cheese Store* is shown if with shop=cheese + - *Drugstore* is shown if with shop=chemist + - *Chocolate Store* is shown if with shop=chocolate + - *Clothing Store* is shown if with shop=clothes + - *Coffee Store* is shown if with shop=coffee + - *Collectibles Shop* is shown if with shop=collector + - *Computer Store* is shown if with shop=computer + - *Candy Store* is shown if with shop=confectionery + - *Convenience Store* is shown if with shop=convenience + - *Copy Store* is shown if with shop=copyshop + - *Cosmetics Store* is shown if with shop=cosmetics + - *Rural Supplies Store* is shown if with shop=country_store + - *Arts & Crafts Store* is shown if with shop=craft + - *Curtain Store* is shown if with shop=curtain + - *Dairy Store* is shown if with shop=dairy + - *Delicatessen* is shown if with shop=deli + - *Department Store* is shown if with shop=department_store + - *DIY Store* is shown if with shop=doityourself + - *Door Shop* is shown if with shop=doors + - *Dry Cleaner* is shown if with shop=dry_cleaning + - *E-Cigarette Shop* is shown if with shop=e-cigarette + - *Electrical Equipment Store* is shown if with shop=electrical + - *Electronics Store* is shown if with shop=electronics + - *Erotic Store* is shown if with shop=erotic + - *Fabric Store* is shown if with shop=fabric + - *Produce Stand* is shown if with shop=farm + - *Fashion Accessories Store* is shown if with shop=fashion_accessories + - *Fireplace Store* is shown if with shop=fireplace + - *Fishing Shop* is shown if with shop=fishing + - *Flooring Supply Shop* is shown if with shop=flooring + - *Florist* is shown if with shop=florist + - *Framing Shop* is shown if with shop=frame + - *Frozen Food Store* is shown if with shop=frozen_food + - *Fuel Shop* is shown if with shop=fuel + - *Funeral Home* is shown if with shop=funeral_directors + - *Furniture Store* is shown if with shop=furniture + - *Tabletop Game Store* is shown if with shop=games + - *Garden Center* is shown if with shop=garden_centre + - *Bottled Gas Shop* is shown if with shop=gas + - *General Store* is shown if with shop=general + - *Gift Shop* is shown if with shop=gift + - *Greengrocer* is shown if with shop=greengrocer + - *Hairdresser* is shown if with shop=hairdresser + - *Hairdresser Supply Store* is shown if with shop=hairdresser_supply + - *Hardware Store* is shown if with shop=hardware + - *Health Food Store* is shown if with shop=health_food + - *Hearing Aids Store* is shown if with shop=hearing_aids + - *Herbalist* is shown if with shop=herbalist + - *Hifi Store* is shown if with shop=hifi + - *Honey Store* is shown if with shop=honey + - *Household Linen Shop* is shown if with shop=household_linen + - *Houseware Store* is shown if with shop=houseware + - *Hunting Shop* is shown if with shop=hunting + - *Interior Decoration Store* is shown if with shop=interior_decoration + - *Jewelry Store* is shown if with shop=jewelry + - *Kiosk* is shown if with shop=kiosk + - *Kitchen Design Store* is shown if with shop=kitchen + - *Laundry* is shown if with shop=laundry + - *Leather Store* is shown if with shop=leather + - *Lighting Store* is shown if with shop=lighting + - *Locksmith* is shown if with shop=locksmith + - *Lottery Shop* is shown if with shop=lottery + - *Mall* is shown if with shop=mall + - *Massage Shop* is shown if with shop=massage + - *Medical Supply Store* is shown if with shop=medical_supply + - *Military Surplus Store* is shown if with shop=military_surplus + - *Mobile Phone Store* is shown if with shop=mobile_phone + - *Model Shop* is shown if with shop=model + - *Money Lender* is shown if with shop=money_lender + - *Motorcycle Dealership* is shown if with shop=motorcycle + - *Motorcycle Repair Shop* is shown if with shop=motorcycle_repair + - *Music Store* is shown if with shop=music + - *Musical Instrument Store* is shown if with shop=musical_instrument + - *Newsstand* is shown if with shop=newsagent + - *Nutrition Supplements Store* is shown if with shop=nutrition_supplements + - *Nuts Shop* is shown if with shop=nuts + - *Optician* is shown if with shop=optician + - *Outdoors Store* is shown if with shop=outdoor + - *Online Retailer Outpost* is shown if with shop=outpost + - *Paint Store* is shown if with shop=paint + - *Party Supply Store* is shown if with shop=party + - *Pasta Store* is shown if with shop=pasta + - *Pastry Shop* is shown if with shop=pastry + - *Pawnshop* is shown if with shop=pawnbroker + - *Perfume Store* is shown if with shop=perfumery + - *Pet Store* is shown if with shop=pet + - *Pet Groomer* is shown if with shop=pet_grooming + - *Photography Store* is shown if with shop=photo + - *Pottery Store* is shown if with shop=pottery + - *Printer Ink Store* is shown if with shop=printer_ink + - *Psychic* is shown if with shop=psychic + - *Fireworks Store* is shown if with shop=pyrotechnics + - *Radio/Electronic Component Store* is shown if with shop=radiotechnics + - *Religious Store* is shown if with shop=religion + - *Rental Shop* is shown if with shop=rental + - *Repair Shop* is shown if with shop=repair + - *Rice Store* is shown if with shop=rice + - *Scuba Diving Shop* is shown if with shop=scuba_diving + - *Seafood Shop* is shown if with shop=seafood + - *Thrift Store* is shown if with shop=second_hand + - *Sewing Supply Shop* is shown if with shop=sewing + - *Shoe Repair Shop* is shown if with shop=shoe_repair + - *Shoe Store* is shown if with shop=shoes + - *Spice Shop* is shown if with shop=spices + - *Sporting Goods Store* is shown if with shop=sports + - *Stationery Store* is shown if with shop=stationery + - *Storage Rental* is shown if with shop=storage_rental + - *Supermarket* is shown if with shop=supermarket + - *Pool Supply Store* is shown if with shop=swimming_pool + - *Tailor* is shown if with shop=tailor + - *Tattoo Parlor* is shown if with shop=tattoo + - *Tea Store* is shown if with shop=tea + - *Telecom Retail Store* is shown if with shop=telecommunication + - *Ticket Seller* is shown if with shop=ticket + - *Tile Shop* is shown if with shop=tiles + - *Tobacco Shop* is shown if with shop=tobacco + - *Tool Rental* is shown if with shop=tool_hire + - *Toy Store* is shown if with shop=toys + - *Trade Shop* is shown if with shop=trade + - *Travel Agency* is shown if with shop=travel_agency + - *Trophy Shop* is shown if with shop=trophy + - *Tire Store* is shown if with shop=tyres + - *Vacuum Cleaner Store* is shown if with shop=vacuum_cleaner + - *Discount Store* is shown if with shop=variety_store + - *Video Store* is shown if with shop=video + - *Video Game Store* is shown if with shop=video_games + - *Watches Shop* is shown if with shop=watches + - *Drinking Water Shop* is shown if with shop=water + - *Watersport/Swim Shop* is shown if with shop=water_sports + - *Weapon Shop* is shown if with shop=weapons + - *Wholesale Store* is shown if with shop=wholesale + - *Wig Shop* is shown if with shop=wigs + - *Window Blind Store* is shown if with shop=window_blind + - *Wine Shop* is shown if with shop=wine + +This tagrendering has labels +`description` + ### brand The question is `What is the brand of this shop?` @@ -241,7 +434,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -262,7 +455,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -271,7 +464,89 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ + +### bicycle-types + +The question is `What kind of bicycles and accessories are rented here?` +*{rental} is rented here* is shown if `rental` is set + + - *Normal city bikes can be rented here* is shown if with rental=city_bike + - *Electrical bikes can be rented here* is shown if with rental=ebike + - *BMX bikes can be rented here* is shown if with rental=bmx + - *Mountainbikes can be rented here* is shown if with rental=mtb + - *Bikes for children can be rented here* is shown if with rental=kid_bike + - *Tandem bicycles can be rented here* is shown if with rental=tandem + - *Race bicycles can be rented here* is shown if with rental=racebike + - *Bike helmets can be rented here* is shown if with rental=bike_helmet + - *Cargo bikes can be rented here* is shown if with rental=cargo_bike + +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:rental=yes | bicycle_rental~.+ +This tagrendering has labels +`bicycle_rental` + +### rental-capacity-city_bike + +The question is `How many city bikes can be rented here?` +*{capacity:city_bike} city bikes can be rented here* is shown if `capacity:city_bike` is set + +This tagrendering is only visible in the popup if the following condition is met: (service:bicycle:rental=yes | bicycle_rental~.+) & rental~^(.*city_bike.*)$ +This tagrendering has labels +`bicycle_rental` + +### rental-capacity-ebike + +The question is `How many electrical bikes can be rented here?` +*{capacity:ebike} electrical bikes can be rented here* is shown if `capacity:ebike` is set + +This tagrendering is only visible in the popup if the following condition is met: (service:bicycle:rental=yes | bicycle_rental~.+) & rental~^(.*ebike.*)$ +This tagrendering has labels +`bicycle_rental` + +### rental-capacity-kid_bike + +The question is `How many bikes for children can be rented here?` +*{capacity:kid_bike} bikes for children can be rented here* is shown if `capacity:kid_bike` is set + +This tagrendering is only visible in the popup if the following condition is met: (service:bicycle:rental=yes | bicycle_rental~.+) & rental~^(.*kid_bike.*)$ +This tagrendering has labels +`bicycle_rental` + +### rental-capacity-bmx + +The question is `How many BMX bikes can be rented here?` +*{capacity:bmx} BMX bikes can be rented here* is shown if `capacity:bmx` is set + +This tagrendering is only visible in the popup if the following condition is met: (service:bicycle:rental=yes | bicycle_rental~.+) & rental~^(.*bmx.*)$ +This tagrendering has labels +`bicycle_rental` + +### rental-capacity-mtb + +The question is `How many mountainbikes can be rented here?` +*{capacity:mtb} mountainbikes can be rented here* is shown if `capacity:mtb` is set + +This tagrendering is only visible in the popup if the following condition is met: (service:bicycle:rental=yes | bicycle_rental~.+) & rental~^(.*mtb.*)$ +This tagrendering has labels +`bicycle_rental` + +### rental-capacity-bicycle_pannier + +The question is `How many bicycle panniers can be rented here?` +*{capacity:bicycle_pannier} bicycle panniers can be rented here* is shown if `capacity:bicycle_pannier` is set + +This tagrendering is only visible in the popup if the following condition is met: (service:bicycle:rental=yes | bicycle_rental~.+) & rental~^(.*bicycle_pannier.*)$ +This tagrendering has labels +`bicycle_rental` + +### rental-capacity-tandem_bicycle + +The question is `How many tandem can be rented here?` +*{capacity:tandem_bicycle} tandem can be rented here* is shown if `capacity:tandem_bicycle` is set + +This tagrendering is only visible in the popup if the following condition is met: (service:bicycle:rental=yes | bicycle_rental~.+) & rental~^(.*tandem_bicycle.*)$ +This tagrendering has labels +`bicycle_rental` ### bike_pump_service @@ -303,6 +578,16 @@ The question is `Are bicycles washed here?` This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=bicycle_repair | ^(service:bicycle:.*)$~~^(yes|only)$ | service:bicycle:cleaning~.+ +### bike_cleaning-service_bicycle_cleaning_charge + +The question is `How much does it cost to use the cleaning service?` +*Using the cleaning service costs {service:bicycle:cleaning:charge}* is shown if `service:bicycle:cleaning:charge` is set + + - *The cleaning service is free to use* is shown if with service:bicycle:cleaning:fee=no + - *Free to use* is shown if with service:bicycle:cleaning:fee=yes & service:bicycle:cleaning:charge=. _This option cannot be chosen as answer_ + +This tagrendering is only visible in the popup if the following condition is met: amenity!=bike_wash & amenity!=bicycle_wash & service:bicycle:cleaning!=no & service:bicycle:cleaning~.+ + ### internet The question is `Does this place offer internet access?` diff --git a/Docs/Layers/dogshop.md b/Docs/Layers/dogshop.md index 026801f30..a2fbb70d0 100644 --- a/Docs/Layers/dogshop.md +++ b/Docs/Layers/dogshop.md @@ -426,7 +426,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -447,7 +447,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -456,7 +456,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Layers/medical_shops.md b/Docs/Layers/medical_shops.md index 1f905755c..81ad7c004 100644 --- a/Docs/Layers/medical_shops.md +++ b/Docs/Layers/medical_shops.md @@ -435,7 +435,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -456,7 +456,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -465,7 +465,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Layers/shops.md b/Docs/Layers/shops.md index 401f578b3..8ae84d125 100644 --- a/Docs/Layers/shops.md +++ b/Docs/Layers/shops.md @@ -443,7 +443,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -464,7 +464,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -473,7 +473,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Layers/shops_glutenfree.md b/Docs/Layers/shops_glutenfree.md index 99abdf598..8a0690007 100644 --- a/Docs/Layers/shops_glutenfree.md +++ b/Docs/Layers/shops_glutenfree.md @@ -440,7 +440,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -461,7 +461,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -470,7 +470,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Layers/shops_lactosefree.md b/Docs/Layers/shops_lactosefree.md index 1cdc41679..620367e78 100644 --- a/Docs/Layers/shops_lactosefree.md +++ b/Docs/Layers/shops_lactosefree.md @@ -440,7 +440,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -461,7 +461,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -470,7 +470,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Layers/shops_second_hand.md b/Docs/Layers/shops_second_hand.md index 9259031e5..2346109be 100644 --- a/Docs/Layers/shops_second_hand.md +++ b/Docs/Layers/shops_second_hand.md @@ -426,7 +426,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -447,7 +447,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -456,7 +456,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Layers/shops_with_climbing_shoe_repair.md b/Docs/Layers/shops_with_climbing_shoe_repair.md index f783ae36e..689551a74 100644 --- a/Docs/Layers/shops_with_climbing_shoe_repair.md +++ b/Docs/Layers/shops_with_climbing_shoe_repair.md @@ -435,7 +435,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -456,7 +456,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -465,7 +465,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Layers/sport_shops.md b/Docs/Layers/sport_shops.md index e05b0b62e..7921d8f63 100644 --- a/Docs/Layers/sport_shops.md +++ b/Docs/Layers/sport_shops.md @@ -430,7 +430,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -451,7 +451,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -460,7 +460,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Schemas/DenominationConfigJson.schema.json b/Docs/Schemas/DenominationConfigJson.schema.json index 5f4f5cc02..a56187795 100644 --- a/Docs/Schemas/DenominationConfigJson.schema.json +++ b/Docs/Schemas/DenominationConfigJson.schema.json @@ -60,6 +60,73 @@ ], "additionalProperties": false }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, "Record>": { "type": "object", "additionalProperties": false diff --git a/Docs/Schemas/FilterConfigJson.schema.json b/Docs/Schemas/FilterConfigJson.schema.json index 24db048c9..208c4ecb4 100644 --- a/Docs/Schemas/FilterConfigJson.schema.json +++ b/Docs/Schemas/FilterConfigJson.schema.json @@ -9,48 +9,7 @@ "description": "The options for a filter\nIf there are multiple options these will be a list of radio buttons\nIf there is only one option this will be a checkbox\nFiltering is done based on the given osmTags that are compared to the objects in that layer.\n\nAn example which searches by name:\n\n```\n{\n \"id\": \"shop-name\",\n \"options\": [\n {\n \"fields\": [\n {\n \"name\": \"search\",\n \"type\": \"string\"\n }\n ],\n \"osmTags\": \"name~i~.*{search}.*\",\n \"question\": {\n \"en\": \"Only show shops with name {search}\",\n }\n }\n ]\n }\n ```", "type": "array", "items": { - "type": "object", - "properties": { - "question": {}, - "osmTags": { - "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", - "anyOf": [ - { - "$ref": "#/definitions/{and:TagConfigJson[];}" - }, - { - "$ref": "#/definitions/{or:TagConfigJson[];}" - }, - { - "type": "string" - } - ] - }, - "default": { - "type": "boolean" - }, - "fields": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", - "type": "string" - }, - "type": { - "type": "string" - } - }, - "required": [ - "name" - ] - } - } - }, - "required": [ - "question" - ] + "$ref": "#/definitions/FilterConfigOptionJson" } }, "#": { @@ -121,6 +80,73 @@ "or" ], "additionalProperties": false + }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false } }, "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/Docs/Schemas/IconConfigJson.schema.json b/Docs/Schemas/IconConfigJson.schema.json index d002c7bc4..144998e53 100644 --- a/Docs/Schemas/IconConfigJson.schema.json +++ b/Docs/Schemas/IconConfigJson.schema.json @@ -113,6 +113,73 @@ ], "additionalProperties": false }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, "Record>": { "type": "object", "additionalProperties": false diff --git a/Docs/Schemas/LayerConfigJson.schema.json b/Docs/Schemas/LayerConfigJson.schema.json index dd88f554c..684e142f1 100644 --- a/Docs/Schemas/LayerConfigJson.schema.json +++ b/Docs/Schemas/LayerConfigJson.schema.json @@ -18,7 +18,7 @@ ] }, "description": { - "description": "A description for the features shown in this layer.\nThis often resembles the introduction of the wiki.osm.org-page for this feature.\n\ngroup: Basic\nquestion: How would you describe the features that are shown on this layer?", + "description": "question: How would you describe the features that are shown on this layer?\n\nA description for the features shown in this layer.\nThis often resembles the introduction of the wiki.osm.org-page for this feature.\n\ngroup: Basic", "anyOf": [ { "$ref": "#/definitions/Record" @@ -28,6 +28,10 @@ } ] }, + "searchTerms": { + "description": "question: What are some other terms used to describe these objects?\n\nThis is used in the search functionality", + "$ref": "#/definitions/Record" + }, "source": { "description": "Question: Where should the data be fetched from?\ntitle: Data Source\n\nThis determines where the data for the layer is fetched: from OSM or from an external geojson dataset.\n\nIf no 'geojson' is defined, data will be fetched from overpass and the OSM-API.\n\nEvery source _must_ define which tags _must_ be present in order to be picked up.\n\nNote: a source must always be defined. 'special' is only allowed if this is a builtin-layer\n\ntypes: Load data with specific tags from OpenStreetMap ; Load data from an external geojson source ;\ntypesdefault: 0\nifunset: Determine the tags automatically based on the presets\ngroup: Basic", "anyOf": [ @@ -324,7 +328,7 @@ } }, "filter": { - "description": "All the extra questions for filtering.\nIf a string is given, mapComplete will search in\n1. The tagrenderings for a match on ID and use the mappings as options\n2. search 'filters.json' for the appropriate filter or\n3. will try to parse it as `layername.filterid` and us that one.\n\n\ngroup: filters", + "description": "All the extra questions for filtering.\nIf a string is given, mapComplete will search in\n1. The tagrenderings for a match on ID and use the mappings as options\n2. search 'filters.json' for the appropriate filter or\n3. will try to parse it as `layername.filterid` and us that one.\n\nNote: adding \"#filter\":\"no-auto\" will disable the filters added by tagRenderings\n\ngroup: filters", "anyOf": [ { "type": "array", @@ -352,6 +356,13 @@ } ] }, + "#filter": { + "description": "Set this to disable the feature that tagRenderings can introduce filters", + "enum": [ + "no-auto" + ], + "type": "string" + }, "deletion": { "description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: Use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion\nifunset: Do not allow deletion", "anyOf": [ @@ -413,6 +424,17 @@ "enableMorePrivacy": { "description": "question: Should a theme using this layer leak some location info when making changes?\n\nWhen a changeset is made, a 'distance to object'-class is written to the changeset.\nFor some particular themes and layers, this might leak too much information, and we want to obfuscate this\n\nifunset: Write 'change_within_x_m' as usual and if GPS is enabled\niftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey", "type": "boolean" + }, + "snapName": { + "description": "question: When a feature is snapped to this name, how should this item be called?\n\nIn the move wizard, the option `snap object onto {snapName}` is shown\n\ngroup: hidden", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] } }, "required": [ @@ -479,6 +501,73 @@ ], "additionalProperties": false }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, "Record>": { "type": "object", "additionalProperties": false @@ -812,18 +901,24 @@ }, "filter": { "description": "This tagRendering can introduce this builtin filter", - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] } }, "additionalProperties": false }, - "Record": { - "type": "object", - "additionalProperties": false - }, "MappingConfigJson": { "type": "object", "properties": { @@ -1333,7 +1428,7 @@ ] }, "labels": { - "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer", + "description": "What labels should be applied on this tagRendering?\n\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\n\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search", "type": "array", "items": { "type": "string" @@ -1445,10 +1540,20 @@ }, "filter": { "description": "This tagRendering can introduce this builtin filter", - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] } }, "required": [ @@ -1571,7 +1676,7 @@ ] }, "labels": { - "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer", + "description": "What labels should be applied on this tagRendering?\n\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\n\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search", "type": "array", "items": { "type": "string" @@ -1683,10 +1788,20 @@ }, "filter": { "description": "This tagRendering can introduce this builtin filter", - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] } }, "additionalProperties": false @@ -1802,48 +1917,7 @@ "description": "The options for a filter\nIf there are multiple options these will be a list of radio buttons\nIf there is only one option this will be a checkbox\nFiltering is done based on the given osmTags that are compared to the objects in that layer.\n\nAn example which searches by name:\n\n```\n{\n \"id\": \"shop-name\",\n \"options\": [\n {\n \"fields\": [\n {\n \"name\": \"search\",\n \"type\": \"string\"\n }\n ],\n \"osmTags\": \"name~i~.*{search}.*\",\n \"question\": {\n \"en\": \"Only show shops with name {search}\",\n }\n }\n ]\n }\n ```", "type": "array", "items": { - "type": "object", - "properties": { - "question": {}, - "osmTags": { - "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", - "anyOf": [ - { - "$ref": "#/definitions/{and:TagConfigJson[];}" - }, - { - "$ref": "#/definitions/{or:TagConfigJson[];}" - }, - { - "type": "string" - } - ] - }, - "default": { - "type": "boolean" - }, - "fields": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", - "type": "string" - }, - "type": { - "type": "string" - } - }, - "required": [ - "name" - ] - } - } - }, - "required": [ - "question" - ] + "$ref": "#/definitions/FilterConfigOptionJson" } }, "#": { diff --git a/Docs/Schemas/LayoutConfigJson.schema.json b/Docs/Schemas/LayoutConfigJson.schema.json index 984b85e37..2dde50d99 100644 --- a/Docs/Schemas/LayoutConfigJson.schema.json +++ b/Docs/Schemas/LayoutConfigJson.schema.json @@ -381,6 +381,73 @@ ], "additionalProperties": false }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, "Record>": { "type": "object", "additionalProperties": false @@ -714,18 +781,24 @@ }, "filter": { "description": "This tagRendering can introduce this builtin filter", - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] } }, "additionalProperties": false }, - "Record": { - "type": "object", - "additionalProperties": false - }, "MappingConfigJson": { "type": "object", "properties": { @@ -1235,7 +1308,7 @@ ] }, "labels": { - "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer", + "description": "What labels should be applied on this tagRendering?\n\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\n\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search", "type": "array", "items": { "type": "string" @@ -1347,10 +1420,20 @@ }, "filter": { "description": "This tagRendering can introduce this builtin filter", - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] } }, "required": [ @@ -1473,7 +1556,7 @@ ] }, "labels": { - "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer", + "description": "What labels should be applied on this tagRendering?\n\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\n\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search", "type": "array", "items": { "type": "string" @@ -1585,10 +1668,20 @@ }, "filter": { "description": "This tagRendering can introduce this builtin filter", - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] } }, "additionalProperties": false @@ -1704,48 +1797,7 @@ "description": "The options for a filter\nIf there are multiple options these will be a list of radio buttons\nIf there is only one option this will be a checkbox\nFiltering is done based on the given osmTags that are compared to the objects in that layer.\n\nAn example which searches by name:\n\n```\n{\n \"id\": \"shop-name\",\n \"options\": [\n {\n \"fields\": [\n {\n \"name\": \"search\",\n \"type\": \"string\"\n }\n ],\n \"osmTags\": \"name~i~.*{search}.*\",\n \"question\": {\n \"en\": \"Only show shops with name {search}\",\n }\n }\n ]\n }\n ```", "type": "array", "items": { - "type": "object", - "properties": { - "question": {}, - "osmTags": { - "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", - "anyOf": [ - { - "$ref": "#/definitions/{and:TagConfigJson[];}" - }, - { - "$ref": "#/definitions/{or:TagConfigJson[];}" - }, - { - "type": "string" - } - ] - }, - "default": { - "type": "boolean" - }, - "fields": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", - "type": "string" - }, - "type": { - "type": "string" - } - }, - "required": [ - "name" - ] - } - } - }, - "required": [ - "question" - ] + "$ref": "#/definitions/FilterConfigOptionJson" } }, "#": { @@ -1964,7 +2016,7 @@ ] }, "description": { - "description": "A description for the features shown in this layer.\nThis often resembles the introduction of the wiki.osm.org-page for this feature.\n\ngroup: Basic\nquestion: How would you describe the features that are shown on this layer?", + "description": "question: How would you describe the features that are shown on this layer?\n\nA description for the features shown in this layer.\nThis often resembles the introduction of the wiki.osm.org-page for this feature.\n\ngroup: Basic", "anyOf": [ { "$ref": "#/definitions/Record" @@ -1974,6 +2026,10 @@ } ] }, + "searchTerms": { + "description": "question: What are some other terms used to describe these objects?\n\nThis is used in the search functionality", + "$ref": "#/definitions/Record" + }, "source": { "description": "Question: Where should the data be fetched from?\ntitle: Data Source\n\nThis determines where the data for the layer is fetched: from OSM or from an external geojson dataset.\n\nIf no 'geojson' is defined, data will be fetched from overpass and the OSM-API.\n\nEvery source _must_ define which tags _must_ be present in order to be picked up.\n\nNote: a source must always be defined. 'special' is only allowed if this is a builtin-layer\n\ntypes: Load data with specific tags from OpenStreetMap ; Load data from an external geojson source ;\ntypesdefault: 0\nifunset: Determine the tags automatically based on the presets\ngroup: Basic", "anyOf": [ @@ -2270,7 +2326,7 @@ } }, "filter": { - "description": "All the extra questions for filtering.\nIf a string is given, mapComplete will search in\n1. The tagrenderings for a match on ID and use the mappings as options\n2. search 'filters.json' for the appropriate filter or\n3. will try to parse it as `layername.filterid` and us that one.\n\n\ngroup: filters", + "description": "All the extra questions for filtering.\nIf a string is given, mapComplete will search in\n1. The tagrenderings for a match on ID and use the mappings as options\n2. search 'filters.json' for the appropriate filter or\n3. will try to parse it as `layername.filterid` and us that one.\n\nNote: adding \"#filter\":\"no-auto\" will disable the filters added by tagRenderings\n\ngroup: filters", "anyOf": [ { "type": "array", @@ -2298,6 +2354,13 @@ } ] }, + "#filter": { + "description": "Set this to disable the feature that tagRenderings can introduce filters", + "enum": [ + "no-auto" + ], + "type": "string" + }, "deletion": { "description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: Use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion\nifunset: Do not allow deletion", "anyOf": [ @@ -2359,6 +2422,17 @@ "enableMorePrivacy": { "description": "question: Should a theme using this layer leak some location info when making changes?\n\nWhen a changeset is made, a 'distance to object'-class is written to the changeset.\nFor some particular themes and layers, this might leak too much information, and we want to obfuscate this\n\nifunset: Write 'change_within_x_m' as usual and if GPS is enabled\niftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey", "type": "boolean" + }, + "snapName": { + "description": "question: When a feature is snapped to this name, how should this item be called?\n\nIn the move wizard, the option `snap object onto {snapName}` is shown\n\ngroup: hidden", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] } }, "required": [ @@ -2386,7 +2460,7 @@ ] }, "description": { - "description": "A description for the features shown in this layer.\nThis often resembles the introduction of the wiki.osm.org-page for this feature.\n\ngroup: Basic\nquestion: How would you describe the features that are shown on this layer?", + "description": "question: How would you describe the features that are shown on this layer?\n\nA description for the features shown in this layer.\nThis often resembles the introduction of the wiki.osm.org-page for this feature.\n\ngroup: Basic", "anyOf": [ { "$ref": "#/definitions/Record" @@ -2396,6 +2470,10 @@ } ] }, + "searchTerms": { + "description": "question: What are some other terms used to describe these objects?\n\nThis is used in the search functionality", + "$ref": "#/definitions/Record" + }, "source": { "description": "Question: Where should the data be fetched from?\ntitle: Data Source\n\nThis determines where the data for the layer is fetched: from OSM or from an external geojson dataset.\n\nIf no 'geojson' is defined, data will be fetched from overpass and the OSM-API.\n\nEvery source _must_ define which tags _must_ be present in order to be picked up.\n\nNote: a source must always be defined. 'special' is only allowed if this is a builtin-layer\n\ntypes: Load data with specific tags from OpenStreetMap ; Load data from an external geojson source ;\ntypesdefault: 0\nifunset: Determine the tags automatically based on the presets\ngroup: Basic", "anyOf": [ @@ -2692,7 +2770,7 @@ } }, "filter": { - "description": "All the extra questions for filtering.\nIf a string is given, mapComplete will search in\n1. The tagrenderings for a match on ID and use the mappings as options\n2. search 'filters.json' for the appropriate filter or\n3. will try to parse it as `layername.filterid` and us that one.\n\n\ngroup: filters", + "description": "All the extra questions for filtering.\nIf a string is given, mapComplete will search in\n1. The tagrenderings for a match on ID and use the mappings as options\n2. search 'filters.json' for the appropriate filter or\n3. will try to parse it as `layername.filterid` and us that one.\n\nNote: adding \"#filter\":\"no-auto\" will disable the filters added by tagRenderings\n\ngroup: filters", "anyOf": [ { "type": "array", @@ -2720,6 +2798,13 @@ } ] }, + "#filter": { + "description": "Set this to disable the feature that tagRenderings can introduce filters", + "enum": [ + "no-auto" + ], + "type": "string" + }, "deletion": { "description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: Use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion\nifunset: Do not allow deletion", "anyOf": [ @@ -2781,6 +2866,17 @@ "enableMorePrivacy": { "description": "question: Should a theme using this layer leak some location info when making changes?\n\nWhen a changeset is made, a 'distance to object'-class is written to the changeset.\nFor some particular themes and layers, this might leak too much information, and we want to obfuscate this\n\nifunset: Write 'change_within_x_m' as usual and if GPS is enabled\niftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey", "type": "boolean" + }, + "snapName": { + "description": "question: When a feature is snapped to this name, how should this item be called?\n\nIn the move wizard, the option `snap object onto {snapName}` is shown\n\ngroup: hidden", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] } }, "additionalProperties": false diff --git a/Docs/Schemas/LineRenderingConfigJson.schema.json b/Docs/Schemas/LineRenderingConfigJson.schema.json index b65b2fdf9..6356b7d70 100644 --- a/Docs/Schemas/LineRenderingConfigJson.schema.json +++ b/Docs/Schemas/LineRenderingConfigJson.schema.json @@ -152,6 +152,73 @@ ], "additionalProperties": false }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, "Record>": { "type": "object", "additionalProperties": false @@ -485,10 +552,20 @@ }, "filter": { "description": "This tagRendering can introduce this builtin filter", - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] } }, "additionalProperties": false diff --git a/Docs/Schemas/MappingConfigJson.schema.json b/Docs/Schemas/MappingConfigJson.schema.json index f6d5ddbc6..0df8a69e9 100644 --- a/Docs/Schemas/MappingConfigJson.schema.json +++ b/Docs/Schemas/MappingConfigJson.schema.json @@ -179,6 +179,73 @@ ], "additionalProperties": false }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, "Record>": { "type": "object", "additionalProperties": false @@ -512,17 +579,23 @@ }, "filter": { "description": "This tagRendering can introduce this builtin filter", - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] } }, "additionalProperties": false - }, - "Record": { - "type": "object", - "additionalProperties": false } }, "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/Docs/Schemas/MinimalTagRenderingConfigJson.schema.json b/Docs/Schemas/MinimalTagRenderingConfigJson.schema.json index 38a9b1ea5..01afd84ce 100644 --- a/Docs/Schemas/MinimalTagRenderingConfigJson.schema.json +++ b/Docs/Schemas/MinimalTagRenderingConfigJson.schema.json @@ -87,6 +87,73 @@ "or" ], "additionalProperties": false + }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false } }, "$schema": "http://json-schema.org/draft-07/schema#", diff --git a/Docs/Schemas/MoveConfigJson.schema.json b/Docs/Schemas/MoveConfigJson.schema.json index 14ffd3768..0dfc010a5 100644 --- a/Docs/Schemas/MoveConfigJson.schema.json +++ b/Docs/Schemas/MoveConfigJson.schema.json @@ -70,6 +70,73 @@ ], "additionalProperties": false }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, "Record>": { "type": "object", "additionalProperties": false diff --git a/Docs/Schemas/PointRenderingConfigJson.schema.json b/Docs/Schemas/PointRenderingConfigJson.schema.json index cc73b530a..70b806ee7 100644 --- a/Docs/Schemas/PointRenderingConfigJson.schema.json +++ b/Docs/Schemas/PointRenderingConfigJson.schema.json @@ -226,6 +226,73 @@ ], "additionalProperties": false }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, "Record>": { "type": "object", "additionalProperties": false @@ -559,10 +626,20 @@ }, "filter": { "description": "This tagRendering can introduce this builtin filter", - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] } }, "additionalProperties": false diff --git a/Docs/Schemas/QuestionableTagRenderingConfigJson.schema.json b/Docs/Schemas/QuestionableTagRenderingConfigJson.schema.json index da24141e2..8c1d81199 100644 --- a/Docs/Schemas/QuestionableTagRenderingConfigJson.schema.json +++ b/Docs/Schemas/QuestionableTagRenderingConfigJson.schema.json @@ -114,7 +114,7 @@ ] }, "labels": { - "description": "A list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer", + "description": "What labels should be applied on this tagRendering?\n\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\n\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search", "type": "array", "items": { "type": "string" @@ -226,10 +226,20 @@ }, "filter": { "description": "This tagRendering can introduce this builtin filter", - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] } }, "required": [ @@ -295,6 +305,73 @@ ], "additionalProperties": false }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, "Record>": { "type": "object", "additionalProperties": false @@ -628,18 +705,24 @@ }, "filter": { "description": "This tagRendering can introduce this builtin filter", - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] } }, "additionalProperties": false }, - "Record": { - "type": "object", - "additionalProperties": false - }, "MappingConfigJson": { "type": "object", "properties": { diff --git a/Docs/Schemas/RewritableConfigJson.schema.json b/Docs/Schemas/RewritableConfigJson.schema.json index 377170bad..bf0b5a8f9 100644 --- a/Docs/Schemas/RewritableConfigJson.schema.json +++ b/Docs/Schemas/RewritableConfigJson.schema.json @@ -106,6 +106,73 @@ ], "additionalProperties": false }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, "Record>": { "type": "object", "additionalProperties": false @@ -439,18 +506,24 @@ }, "filter": { "description": "This tagRendering can introduce this builtin filter", - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] } }, "additionalProperties": false }, - "Record": { - "type": "object", - "additionalProperties": false - }, "MappingConfigJson": { "type": "object", "properties": { diff --git a/Docs/Schemas/TagRenderingConfigJson.schema.json b/Docs/Schemas/TagRenderingConfigJson.schema.json index f23085548..fd1f1cc5d 100644 --- a/Docs/Schemas/TagRenderingConfigJson.schema.json +++ b/Docs/Schemas/TagRenderingConfigJson.schema.json @@ -170,10 +170,20 @@ }, "filter": { "description": "This tagRendering can introduce this builtin filter", - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] } }, "definitions": { @@ -236,6 +246,73 @@ ], "additionalProperties": false }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, "Record>": { "type": "object", "additionalProperties": false diff --git a/Docs/Schemas/ThemeConfigJson.schema.json b/Docs/Schemas/ThemeConfigJson.schema.json new file mode 100644 index 000000000..2dde50d99 --- /dev/null +++ b/Docs/Schemas/ThemeConfigJson.schema.json @@ -0,0 +1,2958 @@ +{ + "description": "Defines the entire theme.\n\nA theme is the collection of the layers that are shown; the intro text, the icon, ...\nIt more or less defines the entire experience.\n\nMost of the fields defined here are metadata about the theme, such as its name, description, supported languages, default starting location, ...\n\nThe main chunk of the json will however be the 'layers'-array, where the details of your layers are.\n\nGeneral remark: a type (string | any) indicates either a fixed or a translatable string.", + "type": "object", + "properties": { + "id": { + "description": "question: What is the id of this layout?\n\nThe id is a unique string to identify the theme\n\nIt should be\n- in english\n- describe the theme in a single word (or a few words)\n- all lowercase and with only [a-z] or underscores (_)\n\nThis is used as hashtag in the changeset message, which will read something like \"Adding data with #mapcomplete for theme #\"\n\nOn official themes, it'll become the name of the page, e.g.\n'cyclestreets' which become 'cyclestreets.html'\n\ntype: id\ngroup: basic", + "type": "string" + }, + "title": { + "description": "question: What is the title of this theme?\n\nThe human-readable title, as shown in the welcome message and the index page\ngroup: basic", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "mustHaveLanguage": { + "description": "Only used in 'generateLayerOverview': if present, every translation will be checked to make sure it is fully translated.\n\nThis must be a list of two-letter, lowercase codes which identifies the language, e.g. \"en\", \"nl\", ...", + "type": "array", + "items": { + "type": "string" + } + }, + "description": { + "description": "question: How would you describe this theme?\nThe description, as shown in the welcome message and the more-screen\ngroup: basic", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "shortDescription": { + "description": "A short description, showed as social description and in the 'more theme'-buttons.\nNote that if this one is not defined, the first sentence of 'description' is used\ngroup: hidden", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "descriptionTail": { + "description": "A part of the description, shown under the login-button.\ngroup: hidden", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "icon": { + "description": "question: What icon should be used to represent this theme?\n\nUsed as logo in the more-screen and (for official themes) as favicon, webmanifest logo, ...\n\nEither a URL or a base64 encoded value (which should include 'data:image/svg+xml;base64)\n\nType: icon\nsuggestions: return Constants.defaultPinIcons.map(i => ({if: \"value=\"+i, then: i, icon: i}))\ngroup: basic", + "type": "string" + }, + "socialImage": { + "description": "question: What image should be used as social image preview?\nThis is included as og:image-tag on official themes.\n\nSee https://www.h3xed.com/web-and-internet/how-to-use-og-image-meta-tag-facebook-reddit for more information\nifunset: use the default social image of mapcomplete (or generate one based on the icon)\nType: image\ngroup: basic", + "type": "string" + }, + "extraLink": { + "description": "question: should an extra help button be shown in certain circumstances?\nAdds an additional button on the top-left of the application.\nThis can link to an arbitrary location.\n\nFor example {icon: \"./assets/svg/pop-out.svg\", href: 'https://mapcomplete.org/{theme}.html?lat={lat}&lon={lon}&z={zoom}, requirements: [\"iframe\",\"no-welcome-message]},\n\ngroup: advanced\nifunset: show a link to open MapComplete full screen if used in an iframe", + "$ref": "#/definitions/default" + }, + "startZoom": { + "description": "question: At what zoomlevel should this theme open?\nDefault location and zoom to start.\nNote that this is barely used. Once the user has visited mapcomplete at least once, the previous location of the user will be used\nifunset: Use the default startzoom (0)\ntype: float\ngroup: start_location", + "type": "number" + }, + "startLat": { + "description": "question: At what start latitude should this theme open?\nDefault location and zoom to start.\nNote that this is barely used. Once the user has visited mapcomplete at least once, the previous location of the user will be used\nifunset: Use 0 as start latitude\ntype: float\ngroup: start_location", + "type": "number" + }, + "startLon": { + "description": "question: At what start longitude should this theme open?\nDefault location and zoom to start.\nNote that this is barely used. Once the user has visited mapcomplete at least once, the previous location of the user will be used\nifunset: Use 0 as start longitude\ntype: float\ngroup: start_location", + "type": "number" + }, + "defaultBackgroundId": { + "description": "The id of the default background. BY default: vanilla OSM", + "type": "string" + }, + "credits": { + "description": "Who helped to create this theme and should be attributed?", + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "hideFromOverview": { + "description": "If set to true, this layout will not be shown in the overview with more themes", + "type": "boolean" + }, + "layers": { + "description": "question: What layers should this map show?\ntype: layer[]\ntypes: hidden | layer | hidden\ngroup: layers\ntitle: value[\"builtin\"] ?? value[\"id\"] ?? value\nsuggestions: return Array.from(layers.keys()).map(key => ({if: \"value=\"+key, then: \"\"+key+\" (builtin) - \"+layers.get(key).description}))\n\nA theme must contain at least one layer.\n\nA layer contains all features of a single type, for example \"shops\", \"bicycle pumps\", \"benches\".\nNote that every layer contains a specification of attributes that it should match. MapComplete will fetch the relevant data from either overpass, the OSM-API or the cache server.\nIf a feature can match multiple layers, the first matching layer in the list will be used.\nThis implies that the _order_ of the layers is important.\n\n\n
\nNote that builtin layers can be reused. Either put in the name of the layer to reuse, or use {builtin: \"layername\", override: ...}\n\nThe 'override'-object will be copied over the original values of the layer, which allows to change certain aspects of the layer\n\nFor example: If you would like to use layer nature reserves, but only from a specific operator (eg. Natuurpunt) you would use the following in your theme:\n\n```\n\"layer\": {\n \"builtin\": \"nature_reserve\",\n \"override\": {\"source\":\n {\"osmTags\": {\n \"+and\":[\"operator=Natuurpunt\"]\n }\n }\n }\n}\n```\n\nIt's also possible to load multiple layers at once, for example, if you would like for both drinking water and benches to start at the zoomlevel at 12, you would use the following:\n\n```\n\"layer\": {\n \"builtin\": [\"benches\", \"drinking_water\"],\n \"override\": {\"minzoom\": 12}\n}\n```\n
", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/LayerConfigJson" + }, + { + "type": "object", + "properties": { + "builtin": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "override": { + "$ref": "#/definitions/Partial" + }, + "hideTagRenderingsWithLabels": { + "description": "TagRenderings with any of these labels will be removed from the layer.\nNote that the 'id' and 'group' are considered labels too", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "builtin", + "override" + ] + }, + { + "type": "string" + } + ] + } + }, + "overrideAll": { + "description": "An override applied on all layers of the theme.\n\nE.g.: if there are two layers defined:\n```\n\"layers\":[\n {\"title\": ..., \"tagRenderings\": [...], \"osmSource\":{\"tags\": ...}},\n {\"title\", ..., \"tagRenderings\", [...], \"osmSource\":{\"tags\" ...}}\n]\n```\n\nand overrideAll is specified:\n```\n\"overrideAll\": {\n \"osmSource\":{\"geoJsonSource\":\"xyz\"}\n}\nthen the result will be that all the layers will have these properties applied and result in:\n\"layers\":[\n {\"title\": ..., \"tagRenderings\": [...], \"osmSource\":{\"tags\": ..., \"geoJsonSource\":\"xyz\"}},\n {\"title\", ..., \"tagRenderings\", [...], \"osmSource\":{\"tags\" ..., \"geoJsonSource\":\"xyz\"}}\n]\n```\n\nIf the overrideAll contains a list where the keys starts with a plus, the values will be appended (instead of discarding the old list), for example\n\n\"overrideAll\": {\n \"+tagRenderings\": [ { ... some tagrendering ... }]\n}\n\nIn the above scenario, `sometagrendering` will be added at the beginning of the tagrenderings of every layer", + "$ref": "#/definitions/Partial" + }, + "tileLayerSources": { + "description": "Define some (overlay) slippy map tilesources", + "type": "array", + "items": { + "allOf": [ + { + "$ref": "#/definitions/RasterLayerProperties" + }, + { + "type": "object", + "properties": { + "defaultState": { + "type": "boolean" + } + } + } + ] + } + }, + "customCss": { + "description": "The URL of a custom CSS stylesheet to modify the layout\ngroup: advanced", + "type": "string" + }, + "lockLocation": { + "description": "If set to true, the basemap will not scroll outside of the area visible on initial zoom.\nIf set to [[lon, lat], [lon, lat]], the map will not scroll outside of those bounds.\nOff by default, which will enable panning to the entire world", + "anyOf": [ + { + "type": "array", + "items": [ + { + "type": "array", + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "minItems": 2, + "maxItems": 2 + }, + { + "type": "array", + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "minItems": 2, + "maxItems": 2 + } + ], + "minItems": 2, + "maxItems": 2 + }, + { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + } + } + } + ] + }, + "enableUserBadge": { + "description": "question: Should a user be able to login with OpenStreetMap?\n\nIf not logged in, will not show the login buttons and hide all the editable elements.\nAs such, MapComplete will become read-only and a purely visualisation tool.\n\nifunset: Enable the possiblity to login with OpenStreetMap (default)\niffalse: Do not enable to login with OpenStreetMap, have a read-only view of MapComplete.\niftrue: Enable the possiblity to login with OpenStreetMap\ngroup: feature_switches", + "type": "boolean" + }, + "enableShareScreen": { + "description": "question: Should the tab with options to share the current screen be enabled?\n\nOn can get the iFrame embed code here\n\nifunset: Enable the sharescreen (default)\niffalse: Do not enable the share screen\niftrue: Enable the share screen\ngroup: feature_switches", + "type": "boolean" + }, + "enableMoreQuests": { + "description": "question: Should the user be able to switch to different themes?\n\nTypically enabled in iframes and/or on commisioned themes\n\niftrue: enable to go back to the index page showing all themes\niffalse: do not enable to go back to the index page showing all themes; hide the 'more themes' buttons\nifunset: mapcomplete default: enable to go back to the index page showing all themes\ngroup: feature_switches", + "type": "boolean" + }, + "enableLayers": { + "description": "question: Should the user be able to enable/disable layers and to filter the layers?\n\nThe corresponding URL-parameter is 'fs-filters' instead of 'fs-layers'\niftrue: enable the filters/layers pane\niffalse: do not enable to filter or to disable layers; hide the 'filter' tab from the overview and the button at the bottom-left\nifunset: mapcomplete default: enable to filter or to enable/disable layers\ngroup: feature_switches", + "type": "boolean" + }, + "enableSearch": { + "description": "question: Should the user be able to search for locations?\n\nifunset: MapComplete default: allow to search\niftrue: Allow to search\niffalse: Do not allow to search; hide the search-bar\ngroup: feature_switches", + "type": "boolean" + }, + "enableAddNewPoints": { + "description": "question: Should the user be able to add new points?\n\nAdding new points is only possible if the loaded layers have presets set.\nSome layers do not have presets. If the theme only has layers without presets, then adding new points will not be possible.\n\nifunset: MapComplete default: allow to create new points\niftrue: Allow to create new points\niffalse: Do not allow to create new points, even if the layers in this theme support creating new points\ngroup: feature_switches", + "type": "boolean" + }, + "enableGeolocation": { + "description": "question: Should the user be able to use their GPS to geolocate themselfes on the map?\nifunset: MapComplete default: allow to use the GPS\niftrue: Allow to use the GPS\niffalse: Do not allow to use the GPS, hide the geolocation-buttons\ngroup: feature_switches", + "type": "boolean" + }, + "enableBackgroundLayerSelection": { + "description": "Enable switching the backgroundlayer.\nIf false, the quickswitch-buttons are removed (bottom left) and the dropdown in the layer selection is removed as well\n\nquestion: Should the user be able to switch the background layer?\n\niftrue: Allow to switch the background layer\niffalse: Do not allow to switch the background layer\nifunset: MapComplete default: Allow to switch the background layer\ngroup: feature_switches", + "type": "boolean" + }, + "enableShowAllQuestions": { + "description": "question: Should the questions about a feature be presented one by one or all at once?\niftrue: Show all unanswered questions at the same time\niffalse: Show unanswered questions one by one\nifunset: MapComplete default: Use the preference of the user to show questions at the same time or one by one\ngroup: feature_switches", + "type": "boolean" + }, + "enableDownload": { + "description": "question: Should the 'download as CSV'- and 'download as Geojson'-buttons be enabled?\niftrue: Enable the option to download the map as CSV and GeoJson\niffalse: Disable the option to download the map as CSV and GeoJson\nifunset: MapComplete default: Enable the option to download the map as CSV and GeoJson\ngroup: feature_switches", + "type": "boolean" + }, + "enablePdfDownload": { + "description": "question: Should the 'download as PDF'-button be enabled?\niftrue: Enable the option to download the map as PDF\niffalse: Enable the option to download the map as PDF\nifunset: MapComplete default: Enable the option to download the map as PDF\ngroup: feature_switches", + "type": "boolean" + }, + "enableNoteImports": { + "description": "question: Should the 'notes' from OpenStreetMap be loaded and parsed for import helper notes?\nIf true, notes will be loaded and parsed. If a note is an import (as created by the import_helper.html-tool from mapcomplete),\nthese notes will be shown if a relevant layer is present.\n\nifunset: MapComplete default: do not load import notes for sideloaded themes but do load them for official themes\niftrue: Load notes and show import notes\niffalse: Do not load import notes\ngroup: advanced", + "type": "boolean" + }, + "enableTerrain": { + "description": "question: Should the map use elevation data to give a 3D-feel?\n\nThis is especially useful for hiking maps, skiing maps etc...\n\nifunset: MapComplete default: don't use terrain\niftrue: Use elevation and render 3D\niffalse: Do not use terrain\ngroup: advanced", + "type": "boolean" + }, + "overpassUrl": { + "description": "question: What overpass-api instance should be used for this layout?\n\nifunset: Use the default, builtin collection of overpass instances\ngroup: advanced", + "type": "array", + "items": { + "type": "string" + } + }, + "overpassTimeout": { + "description": "question: After how much seconds should the overpass-query stop?\nIf a query takes too long, the overpass-server will abort.\nOnce can set the amount of time before overpass gives up here.\nifunset: use the default amount of 30 seconds as timeout\ntype: pnat\ngroup: advanced", + "type": "number" + }, + "overpassMaxZoom": { + "description": "At low zoom levels, overpass is used to query features.\nAt high zoom level, the OSM api is used to fetch one or more BBOX aligning with a slippy tile.\nThe overpassMaxZoom controls the flipoverpoint: if the zoom is this or lower, overpass is used.", + "type": "number" + }, + "osmApiTileSize": { + "description": "When the OSM-api is used to fetch features, it does so in a tiled fashion.\nThese tiles are using a ceratin zoom level, that can be controlled here\nDefault: overpassMaxZoom + 1", + "type": "number" + }, + "enableNodeDatabase": { + "description": "Enables tracking of all nodes when data is loaded.\nThis is useful for the 'ImportWay' and 'ConflateWay'-buttons who need this database.\n\nNote: this flag will be automatically set and can thus be ignored.\ngroup: hidden", + "type": "boolean" + }, + "enableMorePrivacy": { + "description": "question: Should this theme leak some location info when making changes?\n\nWhen a changeset is made, a 'distance to object'-class is written to the changeset.\nFor some particular themes and layers, this might leak too much information, and we want to obfuscate this\n\nifunset: Write 'change_within_x_m' as usual and if GPS is enabled\niftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey", + "type": "boolean" + }, + "enableCache": { + "description": "question: Should this theme have the cache enabled?\n\nShould only be dissabled in highly specific cases, such as the GRB-theme\n\nifunset: Cache is enabled\niffalse: Do not cache data\ngroup: hidden", + "type": "boolean" + }, + "_usedImages": { + "description": "Set by the preprocessor\ngroup: hidden", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "description", + "icon", + "id", + "layers", + "title" + ], + "definitions": { + "TagConfigJson": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "type": "object", + "properties": { + "or": { + "type": "array", + "items": { + "$ref": "#/definitions/TagConfigJson" + } + } + }, + "required": [ + "or" + ] + }, + { + "type": "string" + } + ] + }, + "{and:TagConfigJson[];}": { + "type": "object", + "properties": { + "and": { + "type": "array", + "items": { + "$ref": "#/definitions/TagConfigJson" + } + } + }, + "required": [ + "and" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, + "{or:TagConfigJson[];}": { + "type": "object", + "properties": { + "or": { + "type": "array", + "items": { + "$ref": "#/definitions/TagConfigJson" + } + } + }, + "required": [ + "or" + ], + "additionalProperties": false + }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, + "Record>": { + "type": "object", + "additionalProperties": false + }, + "DenominationConfigJson": { + "type": "object", + "properties": { + "useIfNoUnitGiven": { + "description": "If this evaluates to true and the value to interpret has _no_ unit given, assumes that this unit is meant.\nAlternatively, a list of country codes can be given where this acts as the default interpretation\n\nE.g., a denomination using \"meter\" would probably set this flag to \"true\";\na denomination for \"mp/h\" will use the condition \"_country=gb\" to indicate that it is the default in the UK.\n\nIf none of the units indicate that they are the default, the first denomination will be used instead", + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "boolean" + } + ] + }, + "canonicalDenomination": { + "description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used", + "type": "string" + }, + "canonicalDenominationSingular": { + "description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here", + "type": "string" + }, + "alternativeDenomination": { + "description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well", + "type": "array", + "items": { + "type": "string" + } + }, + "human": { + "description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "humanSingular": { + "description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "prefix": { + "description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field", + "type": "boolean" + }, + "addSpace": { + "description": "If set, add a space between the quantity and the denomination.\n\nE.g.: `50 mph` instad of `50mph`", + "type": "boolean" + } + }, + "required": [ + "canonicalDenomination" + ], + "additionalProperties": false + }, + "MinimalTagRenderingConfigJson": { + "description": "Mostly used for lineRendering and pointRendering", + "type": "object", + "properties": { + "render": { + "description": "question: What value should be shown (if no predefined option matches)?\n\nThis piece of text will be shown in the infobox.\nNote that \"&LBRACEkey&RBRACE\"-parts are substituted by the corresponding values of the element.\n\nThis value will be used if there is no mapping which matches (or there are no matches)\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`", + "type": "string" + }, + "mappings": { + "description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes", + "type": "array", + "items": { + "type": "object", + "properties": { + "if": { + "$ref": "#/definitions/TagConfigJson", + "description": "question: When should this single mapping match?\n\nIf this condition is met, then the text under `then` will be shown.\nIf no value matches, and the user selects this mapping as an option, then these tags will be uploaded to OSM.\n\nFor example: {'if': 'diet:vegetarion=yes', 'then':'A vegetarian option is offered here'}\n\nThis can be an substituting-tag as well, e.g. {'if': 'addr:street:={_calculated_nearby_streetname}', 'then': '{_calculated_nearby_streetname}'}" + }, + "then": { + "description": "question: What text should be shown?\n\nIf the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option", + "type": "string" + } + }, + "required": [ + "if", + "then" + ] + } + } + }, + "additionalProperties": false + }, + "IconConfigJson": { + "type": "object", + "properties": { + "icon": { + "description": "question: What icon should be used?\ntypes: Use a different icon depending on the value of some attributes ; icon\nsuggestions: return Constants.defaultPinIcons.map(i => ({if: \"value=\"+i, then: i, icon: i}))", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "builtin": { + "type": "string" + }, + "override": {} + }, + "required": [ + "builtin", + "override" + ] + }, + { + "type": "string" + } + ] + }, + "color": { + "description": "question: What colour should the icon be?\nThis will only work for the default icons such as `pin`,`circle`,...\ntypes: Use a different color depending on the value of some attributes ; color", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "builtin": { + "type": "string" + }, + "override": {} + }, + "required": [ + "builtin", + "override" + ] + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "icon" + ], + "additionalProperties": false + }, + "TagRenderingConfigJson": { + "description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one", + "type": "object", + "properties": { + "render": { + "description": "question: What text should be rendered?\n\nThis piece of text will be shown in the infobox.\nIn this text, values within braces (such as {braced(key)}) are replaced by the corresponding `value` in the object.\nFor example, if the object contains tags `amenity=school; name=Windy Hill School`, the render string `This school is named {name}` will be shown to the user as `This school is named Windy Hill School`\n\nThis text will be shown if:\n- there is no mapping which matches (or there are no matches)\n- no question, no mappings and no 'freeform' is set\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`\ntype: rendered\nifunset: No text is shown if no predefined options match.", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "object", + "properties": { + "special": { + "allOf": [ + { + "$ref": "#/definitions/Record>" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ] + } + ] + } + }, + "required": [ + "special" + ] + }, + { + "type": "string" + } + ] + }, + "icon": { + "description": "question: what icon should be shown next to the 'render' value?\nAn icon shown next to the rendering; typically shown pretty small\nThis is only shown next to the \"render\" value\nType: icon\nifunset: No additional icon is shown next to the always shown text", + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "path" + ] + }, + { + "type": "string" + } + ] + }, + "condition": { + "description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "metacondition": { + "description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the usersettings/application state; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "freeform": { + "description": "question: Should a freeform text field be shown?\nAllow freeform text input from the user\nifunset: Do not add a freeform text field", + "type": "object", + "properties": { + "key": { + "description": "What attribute should be filled out\nIf this key is present in the feature, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown", + "type": "string" + } + } + }, + "mappings": { + "description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes", + "type": "array", + "items": { + "type": "object", + "properties": { + "if": { + "$ref": "#/definitions/TagConfigJson", + "description": "question: When should this single mapping match?\n\nIf this condition is met, then the text under `then` will be shown.\nIf no value matches, and the user selects this mapping as an option, then these tags will be uploaded to OSM.\n\nFor example: {'if': 'diet:vegetarion=yes', 'then':'A vegetarian option is offered here'}\n\nThis can be an substituting-tag as well, e.g. {'if': 'addr:street:={_calculated_nearby_streetname}', 'then': '{_calculated_nearby_streetname}'}" + }, + "then": { + "description": "question: What text should be shown?\n\nIf the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "icon": { + "description": "question: What icon should be added to this mapping?\nifunset: Do not show an extra icon next to the render value\n\nAn icon supporting this mapping; typically shown pretty small.\nThis can be used to show a e.g. 'phone'-icon next to the phone number\n\nThis supports patterns, you can e.g. have `close:red;some/other/icon.svg`\n\ninline: {icon}\nType: icon", + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "path" + ] + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "if", + "then" + ] + } + }, + "description": { + "description": "A human-readable text explaining what this tagRendering does.\nMostly used for the shared tagrenderings", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "classes": { + "description": "question: What css-classes should be applied to showing this attribute?\n\nA list of css-classes to apply to the entire tagRendering.\nThese classes are applied in 'answer'-mode, not in question mode\nThis is only for advanced users.\n\nValues are split on ` ` (space)", + "type": "string" + }, + "filter": { + "description": "This tagRendering can introduce this builtin filter", + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] + } + }, + "additionalProperties": false + }, + "MappingConfigJson": { + "type": "object", + "properties": { + "if": { + "$ref": "#/definitions/TagConfigJson", + "description": "question: What tags should be matched to show this option?\n\nIf in 'question'-mode and the contributor selects this option, these tags will be applied to the object" + }, + "then": { + "description": "Question: What corresponding text should be shown?\nShown if the `if` is fulfilled\nType: rendered", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "icon": { + "description": "question: What icon should be shown next to this mapping?\n\nThis icon will only be shown if the value is known, it is not displayed in the options (but might be in the future)\n\nifunset: Show no icon\nType: icon", + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "Size of the image", + "type": "string" + } + }, + "required": [ + "path" + ] + }, + { + "type": "string" + } + ] + }, + "hideInAnswer": { + "description": "question: Under what circumstances should this mapping be hidden from the possibilities a contributor can pick?\niftrue: Never show this mapping as option to pick\nifunset: Always show this mapping as option to pick\ntype: tag\n\nIn some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n\n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": [ + "string", + "boolean" + ] + } + ] + }, + "alsoShowIf": { + "description": "question: In what other cases should this item be rendered?\n\nAlso show this 'then'-option if the feature matches these tags.\nIdeal for outdated tags or default assumptions. The tags from this options will not be set if the option is chosen!\n\nifunset: No other cases when this text is shown", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "ifnot": { + "description": "question: What tags should be applied if this mapping is _not_ chosen?\n\nOnly applicable if 'multiAnswer' is set.\nThis is for situations such as:\n`accepts:coins=no` where one can select all the possible payment methods. However, we want to make explicit that some options _were not_ selected.\nThis can be done with `ifnot`\nNote that we can not explicitly render this negative case to the user, we cannot show `does _not_ accept coins`.\nIf this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer`\n\nifunset: Do not apply a tag if a different mapping is chosen.", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "addExtraTags": { + "description": "question: What extra tags should be added to the object if this object is chosen?\ntype: simple_tag\n\nIf chosen as answer, these tags will be applied onto the object, together with the tags from the `if`.\nNote that if the contributor picks this mapping, saves and then changes their mind and uses a different mapping,\nthe extraTags will reside.\nE.g. when picking `memorial:type=bench`, then `amenity=bench` will also be applied.\nIf someone later on changes the type to `memorial:statue`, `amenity=bench` will stay onto the object\n(which is the desired behaviour, see e.g. for https://www.openstreetmap.org/node/5620038478)\nUse 'ifNot' to explicitly remove an tag if this is important\n\nIf someone marks the question as 'unknown', the extra tags will not be erased\n\nNot compatible with multiAnswer.\n\nThis can be used e.g. to erase other keys which indicate the 'not' value:\n```json\n{\n \"if\": \"crossing:marking=rainbow\",\n \"then\": \"This is a rainbow crossing\",\n \"addExtraTags\": [\"not:crossing:marking=\"]\n}\n```", + "type": "array", + "items": { + "type": "string" + } + }, + "searchTerms": { + "description": "question: If there are many options, what search terms match too?\nIf there are many options, the mappings-radiobuttons will be replaced by an element with a searchfunction\n\nSearchterms (per language) allow to easily find an option if there are many options\ngroup: hidden", + "$ref": "#/definitions/Record" + }, + "priorityIf": { + "description": "If the searchable selector is picked, mappings with this item will have priority and show up even if the others are hidden\nUse this sparingly\ngroup: hidden", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "#": { + "description": "Used for comments or to disable a validation\n\ngroup: hidden\nignore-image-in-then: normally, a `then`-clause is not allowed to have an `img`-html-element as icons are preferred. In some cases (most notably title-icons), this is allowed", + "type": "string" + } + }, + "required": [ + "if", + "then" + ], + "additionalProperties": false + }, + "T": { + "type": "object", + "additionalProperties": false + }, + "default_4": { + "description": "The PointRenderingConfig gives all details onto how to render a single point of a feature.\n\nThis can be used if:\n\n- The feature is a point\n- To render something at the centroid of an area, or at the start, end or projected centroid of a way", + "type": "object", + "properties": { + "location": { + "description": "question: At what location should this icon be shown?\nmultianswer: true\nsuggestions: return [{if: \"value=point\",then: \"Show an icon for point (node) objects\"},{if: \"value=centroid\",then: \"Show an icon for line or polygon (way) objects at their centroid location\"}, {if: \"value=start\",then: \"Show an icon for line (way) objects at the start\"},{if: \"value=end\",then: \"Show an icon for line (way) object at the end\"},{if: \"value=projected_centerpoint\",then: \"Show an icon for line (way) object near the centroid location, but moved onto the line. Does not show an item on polygons\"}, {if: \"value=polygon_centroid\",then: \"Show an icon at a polygon centroid (but not if it is a way)\"}]", + "type": "array", + "items": { + "type": "string" + } + }, + "marker": { + "description": "The marker for an element.\nNote that this also defines the icon for this layer (rendered with the overpass-tags) and the icon in the presets.\n\nThe result of the icon is rendered as follows:\n- The first icon is rendered on the map\n- The second entry is overlayed on top of it\n- ...\n\nAs a result, on could use a generic icon (`pin`, `circle`, `square`) with a color, then overlay it with a specific icon.\nicon: value\ntitle: value.icon", + "type": "array", + "items": { + "$ref": "#/definitions/IconConfigJson" + } + }, + "iconBadges": { + "description": "A list of extra badges to show next to the icon as small badge\nThey will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout.\n\nNote: strings are interpreted as icons, so layering and substituting is supported. You can use `circle:white;./my_icon.svg` to add a background circle\ngroup: hidden", + "type": "array", + "items": { + "type": "object", + "properties": { + "if": { + "$ref": "#/definitions/TagConfigJson", + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag" + }, + "then": { + "description": "Badge to show\nType: icon", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "if", + "then" + ] + } + }, + "iconSize": { + "description": "question: What size should the marker be on the map?\nA string containing \",\" in pixels\nifunset: Use the default size (40,40 px)", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "anchor": { + "description": "question: What is the anchorpoint of the icon?\n\nThis matches the geographical point with a location on the icon.\n\nifunset: Use MapComplete-default (center)\nsuggestions: return [{if: \"value=center\", then: \"Place the center of the icon on the geographical location\"},{if: \"value=top\", then: \"Place the top of the icon on the geographical location\"},{if: \"value=bottom\", then: \"Place the bottom of the icon on the geographical location\"},{if: \"value=left\", then: \"Place the left of the icon on the geographical location\"},{if: \"value=right\", then: \"Place the right of the icon on the geographical location\"}]", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "rotation": { + "description": "question: What rotation should be applied on the icon?\nThis is mostly useful for items that face a specific direction, such as surveillance cameras\nThis is interpreted as css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)`\n\nIf the icon is shown on the projected centerpoint of a way, one can also use `_direction:centerpoint`\n\ntypes: Dynamic value ; string\nsuggestions: return [{if: \"value={_direction:centerpoint}deg\", then: \"Point the top of the icon towards the end of the way\"}, {if: \"value=calc( {_direction:centerpoint}deg + 90deg )\", then: \"Point the left of the icon towards the end of the way\"}, {if: \"value=calc( {_direction:centerpoint}deg - 90deg )\", then: \"Point the right of the icon towards the end of the way\"}, {if: \"value=calc( {_direction:centerpoint}deg + 180deg )\", then: \"Point the bottom of the icon towards the end of the way\"}]\nifunset: Do not rotate", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "label": { + "description": "question: What label should be shown beneath the marker?\nFor example: `<div style=\"background: white\">{name}</div>`\n\nIf the icon is undefined, then the label is shown in the center of the feature.\ntypes: Dynamic value based on the attributes ; string\ninline: Always show label {value} beneath the marker\nifunset: Do not show a label beneath the marker", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "labelCss": { + "description": "question: What CSS should be applied to the label?\nYou can set the css-properties here, e.g. `background: red; font-size: 12px; `\ninline: Apply CSS-style {value} to the label\ntypes: Dynamic value ; string\nifunset: Do not apply extra CSS-labels to the label\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "labelCssClasses": { + "description": "question: Which CSS-classes should be applied to the label?\n\nThe classes should be separated by a space (` `)\nYou can use most Tailwind-css classes, see https://tailwindcss.com/ for more information\nFor example: `center bg-gray-500 mx-2 my-1 rounded-full`\ninline: Apply CSS-classes {value} to the label\ntypes: Dynamic value ; string\nifunset: Do not apply extra CSS-classes to the label\nsuggestions: return [{if: \"value=bg-white rounded px-2\", then: \"Draw on a white background\"}]", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "css": { + "description": "question: What CSS should be applied to the entire marker?\nYou can set the css-properties here, e.g. `background: red; font-size: 12px; `\nThis will be applied to the _container_ containing both the marker and the label\ninline: Apply CSS-style {value} to the _entire marker_\ntypes: Dynamic value ; string\nifunset: Do not apply extra CSS element to the entire marker\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "cssClasses": { + "description": "question: Which CSS-classes should be applied to the entire marker?\nThis will be applied to the _container_ containing both the marker and the label\n\nThe classes should be separated by a space (` `)\nYou can use most Tailwind-css classes, see https://tailwindcss.com/ for more information\nFor example: `center bg-gray-500 mx-2 my-1 rounded-full`\ninline: Apply CSS-classes {value} to the entire container\ntypes: Dynamic value ; string\nifunset: Do not apply extra CSS-classes to the entire marker\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "pitchAlignment": { + "description": "question: If the map is pitched, should the icon stay parallel to the screen or to the groundplane?\nsuggestions: return [{if: \"value=canvas\", alsoShowIf: \"value=\", then: \"The icon will stay upward and not be transformed as if it sticks to the screen\"}, {if: \"value=map\", then: \"The icon will be transformed as if it were painted onto the ground. (Automatically sets rotationAlignment)\"}]\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "enum": [ + "canvas", + "map" + ], + "type": "string" + } + ] + }, + "rotationAlignment": { + "description": "question: Should the icon be rotated if the map is rotated?\nifunset: Do not rotate or tilt icons. Always keep the icons straight\nsuggestions: return [{if: \"value=canvas\", then: \"Never rotate the icon\"}, {if: \"value=map\", then: \"If the map is rotated, rotate the icon as well. This gives the impression of an icon that floats perpendicular above the ground.\"}]\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "enum": [ + "canvas", + "map" + ], + "type": "string" + } + ] + } + }, + "required": [ + "location" + ], + "additionalProperties": false + }, + "default_5": { + "description": "The LineRenderingConfig gives all details onto how to render a single line of a feature.\n\nThis can be used if:\n\n- The feature is a line\n- The feature is an area", + "type": "object", + "properties": { + "color": { + "description": "question: What color should lines be drawn in?\n\nFor an area, this will be the colour of the outside line.\nIf the value starts with \"--\", the style of the body element will be queried for the corresponding variable instead\n\ntypes: dynamic value ; string\ntitle: Line Colour\ninline: The line colour always is {value}\ntype: color", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "width": { + "description": "question: How wide should the line be?\nThe stroke-width for way-elements\n\ntypes: dynamic value ; string\ntitle: Line width\ninline: The line width is {value} pixels\ntype: pnat\nifunset: Use the default-linewidth of 7 pixels", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": [ + "string", + "number" + ] + } + ] + }, + "dashArray": { + "description": "question: Should a dasharray be used to render the lines?\nThe dasharray defines 'pixels of line, pixels of gap, pixels of line, pixels of gap, ...'. For example, `5 6` will be 5 pixels of line followed by a 6 pixel gap.\nCannot be a dynamic property due to a MapLibre limitation (see https://github.com/maplibre/maplibre-gl-js/issues/1235)\nifunset: Ways are rendered with a full line", + "type": "string" + }, + "lineCap": { + "description": "question: What form should the line-ending have?\nsuggestions: return [{if:\"value=round\",then:\"Round endings\"}, {if: \"value=square\", then: \"square endings\"}, {if: \"value=butt\", then: \"no ending (square ending at the end, without padding)\"}]\ntypes: dynamic value ; string\ntitle: Line Cap\nifunset: Use the default value (round ending)", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "fillColor": { + "description": "question: What colour should be used as fill colour for polygons?\nifunset: The polygon fill colour will be a more transparent version of the stroke colour\nsuggestions: return [{if: \"value=#00000000\", then: \"Use a transparent fill (only render the outline)\"}]\ninline: The fill colour is {value}\ntypes: dynamic value ; string\ntype: color", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "offset": { + "description": "question: Should the lines be moved (offsetted) with a number of pixels against the geographical lines?\nThe number of pixels this line should be moved.\nUse a positive number to move to the right in the drawing direction or a negative to move to the left (left/right as defined by the drawing direction of the line).\n\nIMPORTANT: MapComplete will already normalize 'key:both:property' and 'key:both' into the corresponding 'key:left' and 'key:right' tagging (same for 'sidewalk=left/right/both' which is rewritten to 'sidewalk:left' and 'sidewalk:right')\nThis simplifies programming. Refer to the CalculatedTags.md-documentation for more details\nifunset: don't offset lines on the map\ninline: Pixel offset by {value} pixels\ntypes: dynamic value ; number\ntype: int", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "number" + } + ] + }, + "imageAlongWay": { + "description": "question: What PNG-image should be shown along the way?\n\nifunset: no image is shown along the way\nsuggestions: [{if: \"./assets/png/oneway.png\", then: \"Show a oneway error\"}]\ntype: image", + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "if": { + "$ref": "#/definitions/TagConfigJson", + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag" + }, + "then": { + "type": "string" + } + }, + "required": [ + "if", + "then" + ] + } + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false + }, + "QuestionableTagRenderingConfigJson": { + "description": "A QuestionableTagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nIf the desired tags are missing and a question is defined, a question will be shown instead.", + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "mappings": { + "description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes\n\nquestion: What are common options?", + "type": "array", + "items": { + "$ref": "#/definitions/MappingConfigJson" + } + }, + "multiAnswer": { + "description": "question: Should a contributor be allowed to select multiple mappings?\n\nIf true, use checkboxes instead of radio buttons when asking the question.\n\niftrue: allow to select multiple mappings (and show a freeform-value as list if ';'-separated)\niffalse: only allow to select a single mapping\nifunset: only allow to select a single mapping", + "type": "boolean" + }, + "freeform": { + "description": "Allow freeform text input from the user", + "type": "object", + "properties": { + "key": { + "description": "question: What is the name of the attribute that should be written to?\nThis is the OpenStreetMap-key that that value will be written to\nifunset: do not offer a freeform textfield as answer option", + "type": "string" + }, + "type": { + "description": "question: What is the input type?\nThe type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values\nifunset: use an unconstrained string as input (default)\nsuggestions: return validators.AllValidators.filter(type => !type.isMeta).map((type) => ({if: \"value=\"+type.name, then: \"\"+type.name+\" \"+type.explanation.split(\"\\n\")[0]}))", + "type": "string" + }, + "placeholder": { + "description": "question: What placeholder text should be shown in the input-element if there is no input?\nA (translated) text that is shown (as gray text) within the textfield\ntype: translation\ngroup: expert\nifunset: No specific placeholder is set, show the type of the textfield", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "addExtraTags": { + "description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'\ngroup: expert", + "type": "array", + "items": { + "type": "string" + } + }, + "inline": { + "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question\ngroup: expert", + "type": "boolean" + }, + "default": { + "description": "question: What value should be entered in the text field if no value is set?\nThis can help people to quickly enter the most common option\nifunset: do not prefill the textfield\ngroup: expert", + "type": "string" + }, + "invalidValues": { + "description": "question: What values of the freeform key should be interpreted as 'unknown'?\nFor example, if a feature has `shop=yes`, the question 'what type of shop is this?' should still asked\nifunset: The question will be considered answered if any value is set for the key\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "postfixDistinguished": { + "description": "question: If this key shared and distinguished by a postfix, what is the postfix?\nThis option is used specifically for `charge`, where the cost is indicated with `/item`.\n\nFor example, a vending machine might sell `bicycle_tube`.\nSetting this value to `bicycle_tube`, then answering this question will set `charge= €XX/bicycle_tube`.\nIf charge did already contain another value, e.g. `charge= €YY/some_item; €ZZ/other_item`, then `€XX/bicycle_tube`will be added.\nNote: those values are sorted alphabetically\nNote: no need to add the `/`\n\nifunset: Don't distinguish by postfix\ngroup: expert", + "type": "string" + }, + "helperArgs": { + "description": "Extra arguments to configure the input element\ngroup: hidden" + } + }, + "required": [ + "helperArgs" + ] + }, + "question": { + "description": "question: What question should be shown to the contributor?\n\nA question is presented ot the user if no mapping matches and the 'freeform' key is not set as well.\n\nifunset: This tagrendering will be shown if it is known, but cannot be edited by the contributor, effectively resutling in a read-only rendering", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "questionHint": { + "description": "question: Should some extra information be shown to the contributor, alongside the question?\nThis hint is shown in subtle text under the question.\nThis can give some extra information on what the answer should ook like\nifunset: No extra hint is given", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "editButtonAriaLabel": { + "description": "When using a screenreader and selecting the 'edit' button, the current rendered value is read aloud in normal circumstances.\nIn some rare cases, this is not desirable. For example, if the rendered value is a link to a website, this link can be selected (and will be read aloud).\nIf the user presses _tab_ again, they'll select the button and have the link read aloud a second time.", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "labels": { + "description": "What labels should be applied on this tagRendering?\n\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\n\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search", + "type": "array", + "items": { + "type": "string" + } + }, + "render": { + "description": "question: What text should be rendered?\n\nThis piece of text will be shown in the infobox.\nIn this text, values within braces (such as {braced(key)}) are replaced by the corresponding `value` in the object.\nFor example, if the object contains tags `amenity=school; name=Windy Hill School`, the render string `This school is named {name}` will be shown to the user as `This school is named Windy Hill School`\n\nThis text will be shown if:\n- there is no mapping which matches (or there are no matches)\n- no question, no mappings and no 'freeform' is set\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`\ntype: rendered\nifunset: No text is shown if no predefined options match.", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "object", + "properties": { + "special": { + "allOf": [ + { + "$ref": "#/definitions/Record>" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ] + } + ] + } + }, + "required": [ + "special" + ] + }, + { + "type": "string" + } + ] + }, + "icon": { + "description": "question: what icon should be shown next to the 'render' value?\nAn icon shown next to the rendering; typically shown pretty small\nThis is only shown next to the \"render\" value\nType: icon\nifunset: No additional icon is shown next to the always shown text", + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "path" + ] + }, + { + "type": "string" + } + ] + }, + "condition": { + "description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "metacondition": { + "description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the usersettings/application state; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "description": { + "description": "A human-readable text explaining what this tagRendering does.\nMostly used for the shared tagrenderings", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "classes": { + "description": "question: What css-classes should be applied to showing this attribute?\n\nA list of css-classes to apply to the entire tagRendering.\nThese classes are applied in 'answer'-mode, not in question mode\nThis is only for advanced users.\n\nValues are split on ` ` (space)", + "type": "string" + }, + "filter": { + "description": "This tagRendering can introduce this builtin filter", + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] + } + }, + "required": [ + "id" + ], + "additionalProperties": false + }, + "Partial": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "mappings": { + "description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes\n\nquestion: What are common options?", + "type": "array", + "items": { + "$ref": "#/definitions/MappingConfigJson" + } + }, + "multiAnswer": { + "description": "question: Should a contributor be allowed to select multiple mappings?\n\nIf true, use checkboxes instead of radio buttons when asking the question.\n\niftrue: allow to select multiple mappings (and show a freeform-value as list if ';'-separated)\niffalse: only allow to select a single mapping\nifunset: only allow to select a single mapping", + "type": "boolean" + }, + "freeform": { + "description": "Allow freeform text input from the user", + "type": "object", + "properties": { + "key": { + "description": "question: What is the name of the attribute that should be written to?\nThis is the OpenStreetMap-key that that value will be written to\nifunset: do not offer a freeform textfield as answer option", + "type": "string" + }, + "type": { + "description": "question: What is the input type?\nThe type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values\nifunset: use an unconstrained string as input (default)\nsuggestions: return validators.AllValidators.filter(type => !type.isMeta).map((type) => ({if: \"value=\"+type.name, then: \"\"+type.name+\" \"+type.explanation.split(\"\\n\")[0]}))", + "type": "string" + }, + "placeholder": { + "description": "question: What placeholder text should be shown in the input-element if there is no input?\nA (translated) text that is shown (as gray text) within the textfield\ntype: translation\ngroup: expert\nifunset: No specific placeholder is set, show the type of the textfield", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "addExtraTags": { + "description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'\ngroup: expert", + "type": "array", + "items": { + "type": "string" + } + }, + "inline": { + "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question\ngroup: expert", + "type": "boolean" + }, + "default": { + "description": "question: What value should be entered in the text field if no value is set?\nThis can help people to quickly enter the most common option\nifunset: do not prefill the textfield\ngroup: expert", + "type": "string" + }, + "invalidValues": { + "description": "question: What values of the freeform key should be interpreted as 'unknown'?\nFor example, if a feature has `shop=yes`, the question 'what type of shop is this?' should still asked\nifunset: The question will be considered answered if any value is set for the key\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "postfixDistinguished": { + "description": "question: If this key shared and distinguished by a postfix, what is the postfix?\nThis option is used specifically for `charge`, where the cost is indicated with `/item`.\n\nFor example, a vending machine might sell `bicycle_tube`.\nSetting this value to `bicycle_tube`, then answering this question will set `charge= €XX/bicycle_tube`.\nIf charge did already contain another value, e.g. `charge= €YY/some_item; €ZZ/other_item`, then `€XX/bicycle_tube`will be added.\nNote: those values are sorted alphabetically\nNote: no need to add the `/`\n\nifunset: Don't distinguish by postfix\ngroup: expert", + "type": "string" + }, + "helperArgs": { + "description": "Extra arguments to configure the input element\ngroup: hidden" + } + }, + "required": [ + "helperArgs" + ] + }, + "question": { + "description": "question: What question should be shown to the contributor?\n\nA question is presented ot the user if no mapping matches and the 'freeform' key is not set as well.\n\nifunset: This tagrendering will be shown if it is known, but cannot be edited by the contributor, effectively resutling in a read-only rendering", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "questionHint": { + "description": "question: Should some extra information be shown to the contributor, alongside the question?\nThis hint is shown in subtle text under the question.\nThis can give some extra information on what the answer should ook like\nifunset: No extra hint is given", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "editButtonAriaLabel": { + "description": "When using a screenreader and selecting the 'edit' button, the current rendered value is read aloud in normal circumstances.\nIn some rare cases, this is not desirable. For example, if the rendered value is a link to a website, this link can be selected (and will be read aloud).\nIf the user presses _tab_ again, they'll select the button and have the link read aloud a second time.", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "labels": { + "description": "What labels should be applied on this tagRendering?\n\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\n\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search", + "type": "array", + "items": { + "type": "string" + } + }, + "render": { + "description": "question: What text should be rendered?\n\nThis piece of text will be shown in the infobox.\nIn this text, values within braces (such as {braced(key)}) are replaced by the corresponding `value` in the object.\nFor example, if the object contains tags `amenity=school; name=Windy Hill School`, the render string `This school is named {name}` will be shown to the user as `This school is named Windy Hill School`\n\nThis text will be shown if:\n- there is no mapping which matches (or there are no matches)\n- no question, no mappings and no 'freeform' is set\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`\ntype: rendered\nifunset: No text is shown if no predefined options match.", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "object", + "properties": { + "special": { + "allOf": [ + { + "$ref": "#/definitions/Record>" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ] + } + ] + } + }, + "required": [ + "special" + ] + }, + { + "type": "string" + } + ] + }, + "icon": { + "description": "question: what icon should be shown next to the 'render' value?\nAn icon shown next to the rendering; typically shown pretty small\nThis is only shown next to the \"render\" value\nType: icon\nifunset: No additional icon is shown next to the always shown text", + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "path" + ] + }, + { + "type": "string" + } + ] + }, + "condition": { + "description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "metacondition": { + "description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the usersettings/application state; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "description": { + "description": "A human-readable text explaining what this tagRendering does.\nMostly used for the shared tagrenderings", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "classes": { + "description": "question: What css-classes should be applied to showing this attribute?\n\nA list of css-classes to apply to the entire tagRendering.\nThese classes are applied in 'answer'-mode, not in question mode\nThis is only for advanced users.\n\nValues are split on ` ` (space)", + "type": "string" + }, + "filter": { + "description": "This tagRendering can introduce this builtin filter", + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] + } + }, + "additionalProperties": false + }, + "default<(string|QuestionableTagRenderingConfigJson|{builtin:string;override:Partial;})[]>": { + "description": "Rewrites and multiplies the given renderings of type T.\n\nThis can be used for introducing many similar questions automatically,\nwhich also makes translations easier.\n\n(Note that the key does _not_ need to be wrapped in {}.\nHowever, we recommend to use them if the key is used in a translation, as missing keys will be picked up and warned for by the translation scripts)\n\nFor example:\n\n```\n{\n rewrite: {\n sourceString: [\"key\", \"a|b|c\"],\n into: [\n [\"X\", 0]\n [\"Y\", 1],\n [\"Z\", 2]\n ],\n renderings: [{\n \"key\":\"a|b|c\"\n }]\n }\n}\n```\nwill result in _three_ copies (as the values to rewrite into have three values, namely:\n\n[\n {\n # The first pair: key --> X, a|b|c --> 0\n \"X\": 0\n },\n {\n \"Y\": 1\n },\n {\n \"Z\": 2\n }\n\n]", + "type": "object", + "properties": { + "rewrite": { + "type": "object", + "properties": { + "sourceString": { + "type": "array", + "items": { + "type": "string" + } + }, + "into": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + } + }, + "required": [ + "into", + "sourceString" + ] + }, + "subexpand": { + "description": "Used to expand a sublist.\nE.g. a target `rendering` is:\n\ne.g.\n{\n rewrite: [\"{{x}}\", \"{{y}}\"],\n into:[\n [\"{{x}}\": \"some X\"],\n [\"{{y}}\", [\"option 1\", \"option 2\"]]\n ],\n renderings:[\n {\n \"question\":\"Is {{x}}\",\n \"mappings\": [\"if={{y}}\",then: \"...\"]\n }\n ]\n subExpand: {\n // The list with the key\n \"mappings\":\n // will be taken and multiplied by all possible values of\n \"{{y}}\"\n // Note that this implies that `into.[*].[{{y}}]` should be a list of items\n }\n}\n\nExpansion will result in:\n{\n question: \"Is some X\",\n mappings: [{\"if=option 1\", then: \"...\"}, {\"if=option 2\", then: \"...\"}]\n}", + "$ref": "#/definitions/Record" + }, + "renderings": { + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/QuestionableTagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "builtin": { + "type": "string" + }, + "override": { + "$ref": "#/definitions/Partial" + } + }, + "required": [ + "builtin", + "override" + ] + }, + { + "type": "string" + } + ] + } + }, + { + "type": "array", + "items": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/QuestionableTagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "builtin": { + "type": "string" + }, + "override": { + "$ref": "#/definitions/Partial" + } + }, + "required": [ + "builtin", + "override" + ] + }, + { + "type": "string" + } + ] + } + } + } + ] + } + }, + "required": [ + "renderings", + "rewrite" + ], + "additionalProperties": false + }, + "default_1": { + "type": "object", + "properties": { + "id": { + "description": "An id/name for this filter, used to set the URL parameters", + "type": "string" + }, + "options": { + "description": "The options for a filter\nIf there are multiple options these will be a list of radio buttons\nIf there is only one option this will be a checkbox\nFiltering is done based on the given osmTags that are compared to the objects in that layer.\n\nAn example which searches by name:\n\n```\n{\n \"id\": \"shop-name\",\n \"options\": [\n {\n \"fields\": [\n {\n \"name\": \"search\",\n \"type\": \"string\"\n }\n ],\n \"osmTags\": \"name~i~.*{search}.*\",\n \"question\": {\n \"en\": \"Only show shops with name {search}\",\n }\n }\n ]\n }\n ```", + "type": "array", + "items": { + "$ref": "#/definitions/FilterConfigOptionJson" + } + }, + "#": { + "description": "Used for comments or to disable a check\n\n\"ignore-possible-duplicate\": disables a check in `DetectDuplicateFilters` which complains that a filter can be replaced by a filter from the `filters`-library-layer", + "type": "string" + } + }, + "required": [ + "id", + "options" + ], + "additionalProperties": false + }, + "DeleteConfigJson": { + "type": "object", + "properties": { + "neededChangesets": { + "description": "*\nBy default, the contributor needs 20 previous changesets to delete points edited by others.\nFor some small features (e.g. bicycle racks) this is too much and this requirement can be lowered or dropped, which can be done here.\n\ntype: nat\nquestion: How many changesets must a contributor have before being allowed to delete a point?", + "type": "number" + }, + "extraDeleteReasons": { + "description": "*\nBy default, three reasons to delete a point are shown:\n\n- The point does not exist anymore\n- The point was a testing point\n- THe point could not be found\n\nHowever, for some layers, there might be different or more specific reasons for deletion which can be user friendly to set, e.g.:\n\n- the shop has closed\n- the climbing route has been closed of for nature conservation reasons\n- ...\n\nThese reasons can be stated here and will be shown in the list of options the user can choose from", + "type": "array", + "items": { + "type": "object", + "properties": { + "explanation": { + "description": "The text that will be shown to the user as option for why this point does not exist anymore.\nNote that the most common reasons (test point, does not exist anymore, cannot be found) are already offered by default\n\nquestion: For what extra reason might this feature be removed in real-life?" + }, + "changesetMessage": { + "description": "The text that will be uploaded into the changeset or will be used in the fixme in case of a soft deletion\nShould be a few words, in english\n\nquestion: What should be added to the changeset as delete reason?", + "type": "string" + } + }, + "required": [ + "changesetMessage", + "explanation" + ] + } + }, + "nonDeleteMappings": { + "description": "In some cases, a (starting) contributor might wish to delete a feature even though deletion is not appropriate.\n(The most relevant case are small paths running over private property. These should be marked as 'private' instead of deleted, as the community might trace the path again from aerial imagery, gettting us back to the original situation).\n\nBy adding a 'nonDeleteMapping', an option can be added into the list which will retag the feature.\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!", + "type": "array", + "items": { + "type": "object", + "properties": { + "if": { + "$ref": "#/definitions/TagConfigJson", + "description": "The tags that will be given to the object.\nThis must remove tags so that the 'source/osmTags' won't match anymore\n\nquestion: What tags should be applied to the object?" + }, + "then": { + "description": "The human explanation for the options\n\nquestion: What text should be shown to the contributor for this reason?", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "if", + "then" + ] + } + }, + "softDeletionTags": { + "description": "In some cases, the contributor is not allowed to delete the current feature (e.g. because it isn't a point, the point is referenced by a relation or the user isn't experienced enough).\nTo still offer the user a 'delete'-option, the feature is retagged with these tags. This is a soft deletion, as the point isn't actually removed from OSM but rather marked as 'disused'\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!\n\nExample (note that \"amenity=\" erases the 'amenity'-key alltogether):\n\n```\n{\n \"and\": [\"disussed:amenity=public_bookcase\", \"amenity=\"]\n}\n```\n\nor (notice the use of the ':='-tag to copy the old value of 'shop=*' into 'disused:shop='):\n\n```\n{\n \"and\": [\"disused:shop:={shop}\", \"shop=\"]\n}\n```\n\nquestion: If a hard delete is not possible, what tags should be applied to mark this feature as deleted?\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "omitDefaultDeleteReasons": { + "description": "Set this flag if the default delete reasons should be omitted from the dialog.\nThis requires at least one extraDeleteReason or nonDeleteMapping\n\nquestion: Should the default delete reasons be hidden?\niftrue: Hide the default delete reasons\niffalse: Show the default delete reasons\nifunset: Show the default delete reasons (default behaviour)", + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "boolean" + } + ] + } + }, + "additionalProperties": false + }, + "default_3": { + "type": "object", + "properties": { + "enableImproveAccuracy": { + "description": "question: Should moving this type of point to improve the accuracy be allowed?\niftrue: This point can be moved to improve the accuracy\nifunset: (default) This point can be moved to improve the accuracy\niffalse: This point cannot be moved to improve the accuracy", + "type": "boolean" + }, + "enableRelocation": { + "description": "question: Should moving this type of point due to a relocation be allowed?\n\nThis will erase the attributes `addr:street`, `addr:housenumber`, `addr:city` and `addr:postcode`\n\niftrue: This type of point can be moved due to a relocation (and will remove address information when this is done)\nifunset: (default) This type of point can be moved due to a relocation (and will remove address information when this is done)\niffalse: This type of point cannot be moved due to a relocation", + "type": "boolean" + } + }, + "additionalProperties": false + }, + "default_2": { + "description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given", + "type": "object", + "properties": { + "quantity": { + "description": "What is quantified? E.g. 'speed', 'length' (including width, diameter, ...), 'electric tension', 'electric current', 'duration'", + "type": "string" + }, + "appliesToKey": { + "description": "Every key from this list will be normalized.\n\nTo render the value properly (with a human readable denomination), use `{canonical()}`", + "type": "array", + "items": { + "type": "string" + } + }, + "eraseInvalidValues": { + "description": "If set, invalid values will be erased in the MC application (but not in OSM of course!)\nBe careful with setting this", + "type": "boolean" + }, + "applicableUnits": { + "description": "The possible denominations for this unit.\nFor length, denominations could be \"meter\", \"kilometer\", \"miles\", \"foot\"", + "type": "array", + "items": { + "$ref": "#/definitions/DenominationConfigJson" + } + }, + "defaultInput": { + "description": "In some cases, the default denomination is not the most user friendly to input.\nE.g., when measuring kerb heights, it is illogical to ask contributors to input an amount in meters.\n\nWhen a default input method should be used, this can be specified by setting the canonical denomination here, e.g.\n`defaultInput: \"cm\"`. This must be a denomination which appears in the applicableUnits", + "type": "string" + } + }, + "required": [ + "applicableUnits" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, + "default": { + "type": "object", + "properties": { + "icon": { + "description": "question: What icon should be shown in the link button?\nifunset: do not show an icon\ntype: icon", + "type": "string" + }, + "text": { + "description": "question: What text should be shown in the link icon?\n\nNote that {lat},{lon},{zoom}, {language} and {theme} will be replaced\n\nifunset: do not show a text", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "href": { + "description": "question: if clicked, what webpage should open?\nNote that {lat},{lon},{zoom}, {language} and {theme} will be replaced\n\ntype: url", + "type": "string" + }, + "newTab": { + "description": "question: Should the link open in a new tab?\niftrue: Open in a new tab\niffalse: do not open in a new tab\nifunset: do not open in a new tab", + "type": "boolean" + }, + "requirements": { + "description": "question: When should the extra button be shown?\nsuggestions: return [{if: \"value=iframe\", then: \"When shown in an iframe\"}, {if: \"value=no-iframe\", then: \"When shown as stand-alone webpage\"}, {if: \"value=welcome-message\", then: \"When the welcome messages are enabled\"}, {if: \"value=iframe\", then: \"When the welcome messages are disabled\"}]", + "type": "array", + "items": { + "enum": [ + "iframe", + "no-iframe", + "no-welcome-message", + "welcome-message" + ], + "type": "string" + } + } + }, + "required": [ + "href" + ], + "additionalProperties": false + }, + "LayerConfigJson": { + "description": "Configuration for a single layer", + "type": "object", + "properties": { + "id": { + "description": "question: What is the identifier of this layer?\n\nThis should be a simple, lowercase, human readable string that is used to identify the layer.\n A good ID is:\n - a noun\n - written in singular\n - describes the object\n - in english\n - only has lowercase letters, numbers or underscores. Do not use a space or a dash\n\ntype: id\ngroup: Basic", + "type": "string" + }, + "name": { + "description": "Used in the layer control panel to toggle a layer on and of.\n\nifunset: This will hide the layer in the layer control, making it not filterable and not toggleable\n\ngroup: Basic\nquestion: What is the name of this layer?", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "description": { + "description": "question: How would you describe the features that are shown on this layer?\n\nA description for the features shown in this layer.\nThis often resembles the introduction of the wiki.osm.org-page for this feature.\n\ngroup: Basic", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "description": "question: What are some other terms used to describe these objects?\n\nThis is used in the search functionality", + "$ref": "#/definitions/Record" + }, + "source": { + "description": "Question: Where should the data be fetched from?\ntitle: Data Source\n\nThis determines where the data for the layer is fetched: from OSM or from an external geojson dataset.\n\nIf no 'geojson' is defined, data will be fetched from overpass and the OSM-API.\n\nEvery source _must_ define which tags _must_ be present in order to be picked up.\n\nNote: a source must always be defined. 'special' is only allowed if this is a builtin-layer\n\ntypes: Load data with specific tags from OpenStreetMap ; Load data from an external geojson source ;\ntypesdefault: 0\nifunset: Determine the tags automatically based on the presets\ngroup: Basic", + "anyOf": [ + { + "type": "object", + "properties": { + "osmTags": { + "$ref": "#/definitions/TagConfigJson", + "description": "question: Which tags must be present on the feature to show it in this layer?\nEvery source must set which tags have to be present in order to load the given layer." + } + }, + "required": [ + "osmTags" + ] + }, + { + "type": "object", + "properties": { + "geoJson": { + "description": "The actual source of the data to load, if loaded via geojson.\n\n# A single geojson-file\nsource: {geoJson: \"https://my.source.net/some-geo-data.geojson\"}\n fetches a geojson from a third party source\n\n# A tiled geojson source\nsource: {geoJson: \"https://my.source.net/some-tile-geojson-{layer}-{z}-{x}-{y}.geojson\", geoJsonZoomLevel: 14}\n to use a tiled geojson source. The web server must offer multiple geojsons. {z}, {x} and {y} are substituted by the location; {layer} is substituted with the id of the loaded layer\n\nSome API's use a BBOX instead of a tile, this can be used by specifying {y_min}, {y_max}, {x_min} and {x_max}\n\nquestion: What is the URL of the geojson?\ntype: url", + "type": "string" + }, + "geoJsonZoomLevel": { + "description": "To load a tiled geojson layer, set the zoomlevel of the tiles\n\nquestion: If using a tiled geojson, what is the zoomlevel of the tiles?\nifunset: This is not a tiled geojson", + "type": "number" + }, + "mercatorCrs": { + "description": "Some API's use a mercator-projection (EPSG:900913) instead of WGS84. Set the flag `mercatorCrs: true` in the source for this\n\nquestion: Does this geojson use EPSG:900913 instead of WGS84 as projection?\niftrue: This geojson uses EPSG:900913 instead of WGS84\nifunset: This geojson uses WGS84 just like most geojson (default)", + "type": "boolean" + }, + "idKey": { + "description": "Some API's have an id-field, but give it a different name.\nSetting this key will rename this field into 'id'\n\nifunset: An id with key `id` will be assigned automatically if no attribute `id` is set\ninline: This geojson uses {value} as attribute to set the id\nquestion: What is the name of the attribute containing the ID of the object?", + "type": "string" + } + }, + "required": [ + "geoJson" + ] + }, + { + "enum": [ + "special", + "special:library" + ], + "type": "string" + } + ] + }, + "calculatedTags": { + "description": "A list of extra tags to calculate, specified as \"keyToAssignTo=javascript-expression\".\nThere are a few extra functions available. Refer to Docs/CalculatedTags.md for more information\nThe functions will be run in order, e.g.\n[\n \"_max_overlap_m2=Math.max(...feat.overlapsWith(\"someOtherLayer\").map(o => o.overlap))\n \"_max_overlap_ratio=Number(feat._max_overlap_m2)/feat.area\n]\n\nThe specified tags are evaluated lazily. E.g. if a calculated tag is only used in the popup (e.g. the number of nearby features),\nthe expensive calculation will only be performed then for that feature. This avoids clogging up the contributors PC when all features are loaded.\n\nIf a tag has to be evaluated strictly, use ':=' instead:\n\n[\n\"_some_key:=some_javascript_expression\"\n]\n\nSee the full documentation on [https://github.com/pietervdvn/MapComplete/blob/master/Docs/CalculatedTags.md]\n\ngroup: expert\nquestion: What extra attributes should be calculated with javascript?", + "type": "array", + "items": { + "type": "string" + } + }, + "isShown": { + "description": "If set, only features matching this extra tag will be shown.\nThis is useful to hide certain features from view based on a calculated tag or if the features are provided by a different layer.\n\nquestion: What other tags should features match for being shown?\ngroup: advanced\nifunset: all features which match the 'source'-specification are shown.", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "isCounted": { + "description": "question: should this layer be included in the summary counts?\n\nThe layer server can give summary counts for a tile.\nThis should however be disabled for some layers, e.g. because there are too many features (walls_and_buildings) or because the count is irrelevant.\n\nifunset: Do count\niffalse: Do not include the counts\niftrue: Do include the count", + "type": "boolean" + }, + "minzoom": { + "description": "The minimum needed zoomlevel required to start loading and displaying the data.\nThis can be used to only show common features (e.g. a bicycle parking) only when the map is zoomed in very much (17).\nThis prevents cluttering the map with thousands of parkings if one is looking to an entire city.\n\nDefault: 0\ngroup: Basic\ntype: nat\nquestion: At what zoom level should features of the layer be shown?\nifunset: Always load this layer, even if the entire world is in view.", + "type": "number" + }, + "shownByDefault": { + "description": "Indicates if this layer is shown by default;\ncan be used to hide a layer from start, or to load the layer but only to show it when appropriate (e.g. for advanced users)\n\nquestion: Should this layer be enabled when opening the map for the first time?\niftrue: the layer is enabled when opening the map\niffalse: the layer is hidden until the contributor enables it. (If the filter-popup is disabled, this might never get enabled nor loaded)\ndefault: true\ngroup: advanced", + "type": "boolean" + }, + "minzoomVisible": { + "description": "The zoom level at which point the data is hidden again\nDefault: 100 (thus: always visible\n\ngroup: expert", + "type": "number" + }, + "title": { + "description": "question: Edit the popup title\nThe title shown in a popup for elements of this layer.\n\ngroup: title\ntypes: use a fixed translation ; Use a dynamic tagRendering ; hidden\ntypesdefault: 1\ntype: translation\ninline: {translated{value}}", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "popupInFloatover": { + "description": "Question: Should the information for this layer be shown in the sidebar or in a splash screen?\n\nIf set, open the selectedElementView in a floatOver instead of on the right.\n\niftrue: show the infobox in the splashscreen floating over the entire UI; hide the title bar\niffalse: show the infobox in a sidebar on the right\nsuggestions: return [{if: \"value=title\", then: \"Show in a floatover and show the title bar\"}]\ngroup: advanced\ndefault: sidebar", + "type": [ + "string", + "boolean" + ] + }, + "titleIcons": { + "description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons (which are added automatically)\n\nUse `auto:` to automatically create an icon based on a tagRendering which has icons\n\nType: icon[]\ngroup: infobox", + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "allOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + } + ] + }, + { + "type": "string" + } + ] + } + }, + { + "type": "array", + "items": [ + { + "type": "string", + "enum": [ + "defaults" + ] + } + ], + "minItems": 1, + "maxItems": 1 + } + ] + }, + "pointRendering": { + "description": "Creates points to render on the map.\nThis can render points for point-objects, lineobjects or areaobjects; use 'location' to indicate where it should be rendered.\n\nNote that all attributes - including [the calculated tags](https://github.com/pietervdvn/MapComplete/blob/develop/Docs/CalculatedTags.md) can be used to create the markers and lines\n\ngroup: pointrendering", + "type": "array", + "items": { + "$ref": "#/definitions/default_4" + } + }, + "lineRendering": { + "description": "Creates lines and areas to render on the map\ngroup: linerendering", + "type": "array", + "items": { + "$ref": "#/definitions/default_5" + } + }, + "passAllFeatures": { + "description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert", + "type": "boolean" + }, + "doNotDownload": { + "description": "If set, this layer will not query overpass; but it'll still match the tags above which are by chance returned by other layers.\nWorks well together with 'passAllFeatures', to add decoration\nThe opposite of `forceLoad`\n\niftrue: Do not attempt to query the data for this layer from overpass/the OSM API\niffalse: download the data as usual\ngroup: expert\nquestion: Should this layer be downloaded or is the data provided by a different layer (which has 'passAllFeatures'-set)?\ndefault: false", + "type": "boolean" + }, + "forceLoad": { + "description": "Advanced option - might be set by the theme compiler\n\nIf true, this data will _always_ be loaded, even if the theme is disabled by a filter or hidden.\nThe opposite of `doNotDownload`\n\nquestion: Should this layer be forcibly loaded?\nifftrue: always download this layer, even if it is disabled\niffalse: only download data for this layer when needed (default)\ndefault: false\ngroup: expert", + "type": "boolean" + }, + "presets": { + "description": "
\n
\nPresets for this layer.\n\nA preset consists of one or more attributes (tags), a title and optionally a description and optionally example images.\n\nWhen the contributor wishes to add a point to OpenStreetMap, they'll:\n\n1. Press the 'add new point'-button\n2. Choose a preset from the list of all presets\n3. Confirm the choice. In this step, the `description` (if set) and `exampleImages` (if given) will be shown\n4. Confirm the location\n5. A new point will be created with the attributes that were defined in the preset\n\nIf no presets are defined, the button which invites to add a new preset will not be shown.\n
\n
\n\ngroup: presets\ntitle: value.title", + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "description": "The title - shown on the 'add-new'-button.\n\nThis should include the article of the noun, e.g. 'a hydrant', 'a bicycle pump'.\nThis text will be inserted into `Add {category} here`, becoming `Add a hydrant here`.\n\nDo _not_ indicate 'new': 'add a new shop here' is incorrect, as the shop might have existed forever, it could just be unmapped!\n\nquestion: What is the word to describe this object?\ninline: Add {translated(value)::font-bold} here", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "tags": { + "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag\ntypeHelper: uploadableOnly", + "type": "array", + "items": { + "type": "string" + } + }, + "description": { + "description": "An extra explanation of what the feature is, if it is not immediately clear from the title alone.\n\nThe _first sentence_ of the description is shown on the button of the `add` menu.\nThe full description is shown in the confirmation dialog.\n\n(The first sentence is until the first '.'-character in the description)\n\nquestion: How would you describe this feature?\nifunset: No extra description is given. This can be used to further explain the preset", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "exampleImages": { + "description": "The URL of an example image which shows a real-life example of what such a feature might look like.\n\nType: image\nquestion: What is the URL of an image showing such a feature?", + "type": "array", + "items": { + "type": "string" + } + }, + "snapToLayer": { + "description": "question: Should the created point be snapped to a line layer?\n\nIf specified, these layers will be shown in the precise location picker and the new point will be snapped towards it.\nFor example, this can be used to snap against `walls_and_buildings` (e.g. to attach a defibrillator, an entrance, an artwork, ... to the wall)\nor to snap an obstacle (such as a bollard) to the `cycleways_and_roads`.\n\nsuggestions: return Array.from(layers.keys()).map(key => ({if: \"value=\"+key, then: key+\" - \"+layers.get(key).description}))", + "type": "array", + "items": { + "type": "string" + } + }, + "maxSnapDistance": { + "description": "question: What is the maximum distance in the location-input that a point can be moved to be snapped to a way?\n\ninline: a point is snapped if the location input is at most {value}m away from an object\n\nIf specified, a new point will only be snapped if it is within this range.\nIf further away, it'll be placed in the center of the location input\nDistance in meter\n\nifunset: Do not snap to a layer", + "type": "number" + } + }, + "required": [ + "tags", + "title" + ] + } + }, + "tagRenderings": { + "description": "question: Edit this way this attributed is displayed or queried\n\nA tag rendering is a block that either shows the known value or asks a question.\n\nRefer to the class `TagRenderingConfigJson` to see the possibilities.\n\nNote that we can also use a string here - where the string refers to a tag rendering defined in `assets/questions/questions.json`,\nwhere a few very general questions are defined e.g. website, phone number, ...\nFurthermore, _all_ the questions of another layer can be reused with `otherlayer.*`\nIf you need only a single of the tagRenderings, use `otherlayer.tagrenderingId`\nIf one or more questions have a 'group' or 'label' set, select all the entries with the corresponding group or label with `otherlayer.*group`\nRemark: if a tagRendering is 'lent' from another layer, the 'source'-tags are copied and added as condition.\nIf they are not wanted, remove them with an override\n\nA special value is 'questions', which indicates the location of the questions box. If not specified, it'll be appended to the bottom of the featureInfobox.\n\nAt last, one can define a group of renderings where parts of all strings will be replaced by multiple other strings.\nThis is mainly create questions for a 'left' and a 'right' side of the road.\nThese will be grouped and questions will be asked together\n\ntype: tagrendering[]\ngroup: tagrenderings", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/QuestionableTagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "builtin": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "override": { + "$ref": "#/definitions/Partial" + } + }, + "required": [ + "builtin", + "override" + ] + }, + { + "allOf": [ + { + "$ref": "#/definitions/default<(string|QuestionableTagRenderingConfigJson|{builtin:string;override:Partial;})[]>" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ] + } + ] + }, + { + "type": "string" + } + ] + } + }, + "filter": { + "description": "All the extra questions for filtering.\nIf a string is given, mapComplete will search in\n1. The tagrenderings for a match on ID and use the mappings as options\n2. search 'filters.json' for the appropriate filter or\n3. will try to parse it as `layername.filterid` and us that one.\n\nNote: adding \"#filter\":\"no-auto\" will disable the filters added by tagRenderings\n\ngroup: filters", + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/default_1" + }, + { + "type": "string" + } + ] + } + }, + { + "type": "object", + "properties": { + "sameAs": { + "type": "string" + } + }, + "required": [ + "sameAs" + ] + } + ] + }, + "#filter": { + "description": "Set this to disable the feature that tagRenderings can introduce filters", + "enum": [ + "no-auto" + ], + "type": "string" + }, + "deletion": { + "description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: Use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion\nifunset: Do not allow deletion", + "anyOf": [ + { + "$ref": "#/definitions/DeleteConfigJson" + }, + { + "type": "boolean" + } + ] + }, + "allowMove": { + "description": "Indicates if a point can be moved and why.\n\nA feature can be moved by MapComplete if:\n\n- It is a point\n- The point is _not_ part of a way or a a relation.\n\ntypes: use an advanced move configuration ; boolean\ngroup: editing\nquestion: Is deleting a point allowed?\niftrue: Allow contributors to move a point (for accuracy or a relocation)\niffalse: Don't allow contributors to move points\nifunset: Don't allow contributors to move points (default)", + "anyOf": [ + { + "$ref": "#/definitions/default_3" + }, + { + "type": "boolean" + } + ] + }, + "allowSplit": { + "description": "If set, a 'split this way' button is shown on objects rendered as LineStrings, e.g. highways.\n\nIf the way is part of a relation, MapComplete will attempt to update this relation as well\nquestion: Should the contributor be able to split ways using this layer?\niftrue: enable the 'split-roads'-component\niffalse: don't enable the split-roads component\nifunset: don't enable the split-roads component\ngroup: editing", + "type": "boolean" + }, + "units": { + "description": "Either a list with [{\"key\": \"unitname\", \"key2\": {\"quantity\": \"unitname\", \"denominations\": [\"denom\", \"denom\"]}}]\n\nUse `\"inverted\": true` if the amount should be _divided_ by the denomination, e.g. for charge over time (`€5/day`)", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/default_2" + }, + { + "$ref": "#/definitions/Record" + } + ] + } + }, + "syncSelection": { + "description": "If set, synchronizes whether this layer is enabled.\n\ngroup: advanced\nquestion: Should enabling/disabling the layer be saved (locally or in the cloud)?\nsuggestions: return [{if: \"value=no\", then: \"Don't save, always revert to the default\"}, {if: \"value=local\", then: \"Save selection in local storage\"}, {if: \"value=theme-only\", then: \"Save the state in the OSM-usersettings, but apply on this theme only (default)\"}, {if: \"value=global\", then: \"Save in OSM-usersettings and toggle on _all_ themes using this layer\"}]", + "enum": [ + "global", + "local", + "no", + "theme-only" + ], + "type": "string" + }, + "#": { + "description": "Used for comments and/or to disable some checks\n\nno-question-hint-check: disables a check in MiscTagRenderingChecks which complains about 'div', 'span' or 'class=subtle'-HTML elements in the tagRendering\n\ngroup: hidden", + "type": "string" + }, + "fullNodeDatabase": { + "description": "_Set automatically by MapComplete, please ignore_\n\ngroup: hidden", + "type": "boolean" + }, + "enableMorePrivacy": { + "description": "question: Should a theme using this layer leak some location info when making changes?\n\nWhen a changeset is made, a 'distance to object'-class is written to the changeset.\nFor some particular themes and layers, this might leak too much information, and we want to obfuscate this\n\nifunset: Write 'change_within_x_m' as usual and if GPS is enabled\niftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey", + "type": "boolean" + }, + "snapName": { + "description": "question: When a feature is snapped to this name, how should this item be called?\n\nIn the move wizard, the option `snap object onto {snapName}` is shown\n\ngroup: hidden", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "id", + "pointRendering" + ], + "additionalProperties": false + }, + "Partial": { + "type": "object", + "properties": { + "id": { + "description": "question: What is the identifier of this layer?\n\nThis should be a simple, lowercase, human readable string that is used to identify the layer.\n A good ID is:\n - a noun\n - written in singular\n - describes the object\n - in english\n - only has lowercase letters, numbers or underscores. Do not use a space or a dash\n\ntype: id\ngroup: Basic", + "type": "string" + }, + "name": { + "description": "Used in the layer control panel to toggle a layer on and of.\n\nifunset: This will hide the layer in the layer control, making it not filterable and not toggleable\n\ngroup: Basic\nquestion: What is the name of this layer?", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "description": { + "description": "question: How would you describe the features that are shown on this layer?\n\nA description for the features shown in this layer.\nThis often resembles the introduction of the wiki.osm.org-page for this feature.\n\ngroup: Basic", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "description": "question: What are some other terms used to describe these objects?\n\nThis is used in the search functionality", + "$ref": "#/definitions/Record" + }, + "source": { + "description": "Question: Where should the data be fetched from?\ntitle: Data Source\n\nThis determines where the data for the layer is fetched: from OSM or from an external geojson dataset.\n\nIf no 'geojson' is defined, data will be fetched from overpass and the OSM-API.\n\nEvery source _must_ define which tags _must_ be present in order to be picked up.\n\nNote: a source must always be defined. 'special' is only allowed if this is a builtin-layer\n\ntypes: Load data with specific tags from OpenStreetMap ; Load data from an external geojson source ;\ntypesdefault: 0\nifunset: Determine the tags automatically based on the presets\ngroup: Basic", + "anyOf": [ + { + "type": "object", + "properties": { + "osmTags": { + "$ref": "#/definitions/TagConfigJson", + "description": "question: Which tags must be present on the feature to show it in this layer?\nEvery source must set which tags have to be present in order to load the given layer." + } + }, + "required": [ + "osmTags" + ] + }, + { + "type": "object", + "properties": { + "geoJson": { + "description": "The actual source of the data to load, if loaded via geojson.\n\n# A single geojson-file\nsource: {geoJson: \"https://my.source.net/some-geo-data.geojson\"}\n fetches a geojson from a third party source\n\n# A tiled geojson source\nsource: {geoJson: \"https://my.source.net/some-tile-geojson-{layer}-{z}-{x}-{y}.geojson\", geoJsonZoomLevel: 14}\n to use a tiled geojson source. The web server must offer multiple geojsons. {z}, {x} and {y} are substituted by the location; {layer} is substituted with the id of the loaded layer\n\nSome API's use a BBOX instead of a tile, this can be used by specifying {y_min}, {y_max}, {x_min} and {x_max}\n\nquestion: What is the URL of the geojson?\ntype: url", + "type": "string" + }, + "geoJsonZoomLevel": { + "description": "To load a tiled geojson layer, set the zoomlevel of the tiles\n\nquestion: If using a tiled geojson, what is the zoomlevel of the tiles?\nifunset: This is not a tiled geojson", + "type": "number" + }, + "mercatorCrs": { + "description": "Some API's use a mercator-projection (EPSG:900913) instead of WGS84. Set the flag `mercatorCrs: true` in the source for this\n\nquestion: Does this geojson use EPSG:900913 instead of WGS84 as projection?\niftrue: This geojson uses EPSG:900913 instead of WGS84\nifunset: This geojson uses WGS84 just like most geojson (default)", + "type": "boolean" + }, + "idKey": { + "description": "Some API's have an id-field, but give it a different name.\nSetting this key will rename this field into 'id'\n\nifunset: An id with key `id` will be assigned automatically if no attribute `id` is set\ninline: This geojson uses {value} as attribute to set the id\nquestion: What is the name of the attribute containing the ID of the object?", + "type": "string" + } + }, + "required": [ + "geoJson" + ] + }, + { + "enum": [ + "special", + "special:library" + ], + "type": "string" + } + ] + }, + "calculatedTags": { + "description": "A list of extra tags to calculate, specified as \"keyToAssignTo=javascript-expression\".\nThere are a few extra functions available. Refer to Docs/CalculatedTags.md for more information\nThe functions will be run in order, e.g.\n[\n \"_max_overlap_m2=Math.max(...feat.overlapsWith(\"someOtherLayer\").map(o => o.overlap))\n \"_max_overlap_ratio=Number(feat._max_overlap_m2)/feat.area\n]\n\nThe specified tags are evaluated lazily. E.g. if a calculated tag is only used in the popup (e.g. the number of nearby features),\nthe expensive calculation will only be performed then for that feature. This avoids clogging up the contributors PC when all features are loaded.\n\nIf a tag has to be evaluated strictly, use ':=' instead:\n\n[\n\"_some_key:=some_javascript_expression\"\n]\n\nSee the full documentation on [https://github.com/pietervdvn/MapComplete/blob/master/Docs/CalculatedTags.md]\n\ngroup: expert\nquestion: What extra attributes should be calculated with javascript?", + "type": "array", + "items": { + "type": "string" + } + }, + "isShown": { + "description": "If set, only features matching this extra tag will be shown.\nThis is useful to hide certain features from view based on a calculated tag or if the features are provided by a different layer.\n\nquestion: What other tags should features match for being shown?\ngroup: advanced\nifunset: all features which match the 'source'-specification are shown.", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "isCounted": { + "description": "question: should this layer be included in the summary counts?\n\nThe layer server can give summary counts for a tile.\nThis should however be disabled for some layers, e.g. because there are too many features (walls_and_buildings) or because the count is irrelevant.\n\nifunset: Do count\niffalse: Do not include the counts\niftrue: Do include the count", + "type": "boolean" + }, + "minzoom": { + "description": "The minimum needed zoomlevel required to start loading and displaying the data.\nThis can be used to only show common features (e.g. a bicycle parking) only when the map is zoomed in very much (17).\nThis prevents cluttering the map with thousands of parkings if one is looking to an entire city.\n\nDefault: 0\ngroup: Basic\ntype: nat\nquestion: At what zoom level should features of the layer be shown?\nifunset: Always load this layer, even if the entire world is in view.", + "type": "number" + }, + "shownByDefault": { + "description": "Indicates if this layer is shown by default;\ncan be used to hide a layer from start, or to load the layer but only to show it when appropriate (e.g. for advanced users)\n\nquestion: Should this layer be enabled when opening the map for the first time?\niftrue: the layer is enabled when opening the map\niffalse: the layer is hidden until the contributor enables it. (If the filter-popup is disabled, this might never get enabled nor loaded)\ndefault: true\ngroup: advanced", + "type": "boolean" + }, + "minzoomVisible": { + "description": "The zoom level at which point the data is hidden again\nDefault: 100 (thus: always visible\n\ngroup: expert", + "type": "number" + }, + "title": { + "description": "question: Edit the popup title\nThe title shown in a popup for elements of this layer.\n\ngroup: title\ntypes: use a fixed translation ; Use a dynamic tagRendering ; hidden\ntypesdefault: 1\ntype: translation\ninline: {translated{value}}", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "popupInFloatover": { + "description": "Question: Should the information for this layer be shown in the sidebar or in a splash screen?\n\nIf set, open the selectedElementView in a floatOver instead of on the right.\n\niftrue: show the infobox in the splashscreen floating over the entire UI; hide the title bar\niffalse: show the infobox in a sidebar on the right\nsuggestions: return [{if: \"value=title\", then: \"Show in a floatover and show the title bar\"}]\ngroup: advanced\ndefault: sidebar", + "type": [ + "string", + "boolean" + ] + }, + "titleIcons": { + "description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons (which are added automatically)\n\nUse `auto:` to automatically create an icon based on a tagRendering which has icons\n\nType: icon[]\ngroup: infobox", + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "allOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + } + ] + }, + { + "type": "string" + } + ] + } + }, + { + "type": "array", + "items": [ + { + "type": "string", + "enum": [ + "defaults" + ] + } + ], + "minItems": 1, + "maxItems": 1 + } + ] + }, + "pointRendering": { + "description": "Creates points to render on the map.\nThis can render points for point-objects, lineobjects or areaobjects; use 'location' to indicate where it should be rendered.\n\nNote that all attributes - including [the calculated tags](https://github.com/pietervdvn/MapComplete/blob/develop/Docs/CalculatedTags.md) can be used to create the markers and lines\n\ngroup: pointrendering", + "type": "array", + "items": { + "$ref": "#/definitions/default_4" + } + }, + "lineRendering": { + "description": "Creates lines and areas to render on the map\ngroup: linerendering", + "type": "array", + "items": { + "$ref": "#/definitions/default_5" + } + }, + "passAllFeatures": { + "description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert", + "type": "boolean" + }, + "doNotDownload": { + "description": "If set, this layer will not query overpass; but it'll still match the tags above which are by chance returned by other layers.\nWorks well together with 'passAllFeatures', to add decoration\nThe opposite of `forceLoad`\n\niftrue: Do not attempt to query the data for this layer from overpass/the OSM API\niffalse: download the data as usual\ngroup: expert\nquestion: Should this layer be downloaded or is the data provided by a different layer (which has 'passAllFeatures'-set)?\ndefault: false", + "type": "boolean" + }, + "forceLoad": { + "description": "Advanced option - might be set by the theme compiler\n\nIf true, this data will _always_ be loaded, even if the theme is disabled by a filter or hidden.\nThe opposite of `doNotDownload`\n\nquestion: Should this layer be forcibly loaded?\nifftrue: always download this layer, even if it is disabled\niffalse: only download data for this layer when needed (default)\ndefault: false\ngroup: expert", + "type": "boolean" + }, + "presets": { + "description": "
\n
\nPresets for this layer.\n\nA preset consists of one or more attributes (tags), a title and optionally a description and optionally example images.\n\nWhen the contributor wishes to add a point to OpenStreetMap, they'll:\n\n1. Press the 'add new point'-button\n2. Choose a preset from the list of all presets\n3. Confirm the choice. In this step, the `description` (if set) and `exampleImages` (if given) will be shown\n4. Confirm the location\n5. A new point will be created with the attributes that were defined in the preset\n\nIf no presets are defined, the button which invites to add a new preset will not be shown.\n
\n
\n\ngroup: presets\ntitle: value.title", + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "description": "The title - shown on the 'add-new'-button.\n\nThis should include the article of the noun, e.g. 'a hydrant', 'a bicycle pump'.\nThis text will be inserted into `Add {category} here`, becoming `Add a hydrant here`.\n\nDo _not_ indicate 'new': 'add a new shop here' is incorrect, as the shop might have existed forever, it could just be unmapped!\n\nquestion: What is the word to describe this object?\ninline: Add {translated(value)::font-bold} here", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "tags": { + "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag\ntypeHelper: uploadableOnly", + "type": "array", + "items": { + "type": "string" + } + }, + "description": { + "description": "An extra explanation of what the feature is, if it is not immediately clear from the title alone.\n\nThe _first sentence_ of the description is shown on the button of the `add` menu.\nThe full description is shown in the confirmation dialog.\n\n(The first sentence is until the first '.'-character in the description)\n\nquestion: How would you describe this feature?\nifunset: No extra description is given. This can be used to further explain the preset", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "exampleImages": { + "description": "The URL of an example image which shows a real-life example of what such a feature might look like.\n\nType: image\nquestion: What is the URL of an image showing such a feature?", + "type": "array", + "items": { + "type": "string" + } + }, + "snapToLayer": { + "description": "question: Should the created point be snapped to a line layer?\n\nIf specified, these layers will be shown in the precise location picker and the new point will be snapped towards it.\nFor example, this can be used to snap against `walls_and_buildings` (e.g. to attach a defibrillator, an entrance, an artwork, ... to the wall)\nor to snap an obstacle (such as a bollard) to the `cycleways_and_roads`.\n\nsuggestions: return Array.from(layers.keys()).map(key => ({if: \"value=\"+key, then: key+\" - \"+layers.get(key).description}))", + "type": "array", + "items": { + "type": "string" + } + }, + "maxSnapDistance": { + "description": "question: What is the maximum distance in the location-input that a point can be moved to be snapped to a way?\n\ninline: a point is snapped if the location input is at most {value}m away from an object\n\nIf specified, a new point will only be snapped if it is within this range.\nIf further away, it'll be placed in the center of the location input\nDistance in meter\n\nifunset: Do not snap to a layer", + "type": "number" + } + }, + "required": [ + "tags", + "title" + ] + } + }, + "tagRenderings": { + "description": "question: Edit this way this attributed is displayed or queried\n\nA tag rendering is a block that either shows the known value or asks a question.\n\nRefer to the class `TagRenderingConfigJson` to see the possibilities.\n\nNote that we can also use a string here - where the string refers to a tag rendering defined in `assets/questions/questions.json`,\nwhere a few very general questions are defined e.g. website, phone number, ...\nFurthermore, _all_ the questions of another layer can be reused with `otherlayer.*`\nIf you need only a single of the tagRenderings, use `otherlayer.tagrenderingId`\nIf one or more questions have a 'group' or 'label' set, select all the entries with the corresponding group or label with `otherlayer.*group`\nRemark: if a tagRendering is 'lent' from another layer, the 'source'-tags are copied and added as condition.\nIf they are not wanted, remove them with an override\n\nA special value is 'questions', which indicates the location of the questions box. If not specified, it'll be appended to the bottom of the featureInfobox.\n\nAt last, one can define a group of renderings where parts of all strings will be replaced by multiple other strings.\nThis is mainly create questions for a 'left' and a 'right' side of the road.\nThese will be grouped and questions will be asked together\n\ntype: tagrendering[]\ngroup: tagrenderings", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/QuestionableTagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "builtin": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "override": { + "$ref": "#/definitions/Partial" + } + }, + "required": [ + "builtin", + "override" + ] + }, + { + "allOf": [ + { + "$ref": "#/definitions/default<(string|QuestionableTagRenderingConfigJson|{builtin:string;override:Partial;})[]>" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ] + } + ] + }, + { + "type": "string" + } + ] + } + }, + "filter": { + "description": "All the extra questions for filtering.\nIf a string is given, mapComplete will search in\n1. The tagrenderings for a match on ID and use the mappings as options\n2. search 'filters.json' for the appropriate filter or\n3. will try to parse it as `layername.filterid` and us that one.\n\nNote: adding \"#filter\":\"no-auto\" will disable the filters added by tagRenderings\n\ngroup: filters", + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/default_1" + }, + { + "type": "string" + } + ] + } + }, + { + "type": "object", + "properties": { + "sameAs": { + "type": "string" + } + }, + "required": [ + "sameAs" + ] + } + ] + }, + "#filter": { + "description": "Set this to disable the feature that tagRenderings can introduce filters", + "enum": [ + "no-auto" + ], + "type": "string" + }, + "deletion": { + "description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: Use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion\nifunset: Do not allow deletion", + "anyOf": [ + { + "$ref": "#/definitions/DeleteConfigJson" + }, + { + "type": "boolean" + } + ] + }, + "allowMove": { + "description": "Indicates if a point can be moved and why.\n\nA feature can be moved by MapComplete if:\n\n- It is a point\n- The point is _not_ part of a way or a a relation.\n\ntypes: use an advanced move configuration ; boolean\ngroup: editing\nquestion: Is deleting a point allowed?\niftrue: Allow contributors to move a point (for accuracy or a relocation)\niffalse: Don't allow contributors to move points\nifunset: Don't allow contributors to move points (default)", + "anyOf": [ + { + "$ref": "#/definitions/default_3" + }, + { + "type": "boolean" + } + ] + }, + "allowSplit": { + "description": "If set, a 'split this way' button is shown on objects rendered as LineStrings, e.g. highways.\n\nIf the way is part of a relation, MapComplete will attempt to update this relation as well\nquestion: Should the contributor be able to split ways using this layer?\niftrue: enable the 'split-roads'-component\niffalse: don't enable the split-roads component\nifunset: don't enable the split-roads component\ngroup: editing", + "type": "boolean" + }, + "units": { + "description": "Either a list with [{\"key\": \"unitname\", \"key2\": {\"quantity\": \"unitname\", \"denominations\": [\"denom\", \"denom\"]}}]\n\nUse `\"inverted\": true` if the amount should be _divided_ by the denomination, e.g. for charge over time (`€5/day`)", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/default_2" + }, + { + "$ref": "#/definitions/Record" + } + ] + } + }, + "syncSelection": { + "description": "If set, synchronizes whether this layer is enabled.\n\ngroup: advanced\nquestion: Should enabling/disabling the layer be saved (locally or in the cloud)?\nsuggestions: return [{if: \"value=no\", then: \"Don't save, always revert to the default\"}, {if: \"value=local\", then: \"Save selection in local storage\"}, {if: \"value=theme-only\", then: \"Save the state in the OSM-usersettings, but apply on this theme only (default)\"}, {if: \"value=global\", then: \"Save in OSM-usersettings and toggle on _all_ themes using this layer\"}]", + "enum": [ + "global", + "local", + "no", + "theme-only" + ], + "type": "string" + }, + "#": { + "description": "Used for comments and/or to disable some checks\n\nno-question-hint-check: disables a check in MiscTagRenderingChecks which complains about 'div', 'span' or 'class=subtle'-HTML elements in the tagRendering\n\ngroup: hidden", + "type": "string" + }, + "fullNodeDatabase": { + "description": "_Set automatically by MapComplete, please ignore_\n\ngroup: hidden", + "type": "boolean" + }, + "enableMorePrivacy": { + "description": "question: Should a theme using this layer leak some location info when making changes?\n\nWhen a changeset is made, a 'distance to object'-class is written to the changeset.\nFor some particular themes and layers, this might leak too much information, and we want to obfuscate this\n\nifunset: Write 'change_within_x_m' as usual and if GPS is enabled\niftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey", + "type": "boolean" + }, + "snapName": { + "description": "question: When a feature is snapped to this name, how should this item be called?\n\nIn the move wizard, the option `snap object onto {snapName}` is shown\n\ngroup: hidden", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + } + }, + "additionalProperties": false + }, + "Partial": { + "type": "object", + "additionalProperties": false + }, + "RasterLayerProperties": { + "description": "This class has grown beyond the point of only containing Raster Layers", + "type": "object", + "properties": { + "name": { + "description": "The name of the imagery source", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "isOverlay": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "url": { + "type": "string" + }, + "category": { + "type": "string" + }, + "type": { + "type": "string" + }, + "style": { + "type": "string" + }, + "attribution": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "text": { + "type": "string" + }, + "html": { + "type": "string" + }, + "required": { + "type": "boolean" + } + } + }, + "min_zoom": { + "type": "number" + }, + "max_zoom": { + "type": "number" + }, + "best": { + "type": "boolean" + } + }, + "required": [ + "id", + "name", + "url" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false +} \ No newline at end of file diff --git a/Docs/Schemas/ThemeConfigJsonJSC.ts b/Docs/Schemas/ThemeConfigJsonJSC.ts new file mode 100644 index 000000000..ad1a859c5 --- /dev/null +++ b/Docs/Schemas/ThemeConfigJsonJSC.ts @@ -0,0 +1,2930 @@ +export default { + "description": "Defines the entire theme.\n\nA theme is the collection of the layers that are shown; the intro text, the icon, ...\nIt more or less defines the entire experience.\n\nMost of the fields defined here are metadata about the theme, such as its name, description, supported languages, default starting location, ...\n\nThe main chunk of the json will however be the 'layers'-array, where the details of your layers are.\n\nGeneral remark: a type (string | any) indicates either a fixed or a translatable string.", + "type": "object", + "properties": { + "id": { + "description": "question: What is the id of this layout?\n\nThe id is a unique string to identify the theme\n\nIt should be\n- in english\n- describe the theme in a single word (or a few words)\n- all lowercase and with only [a-z] or underscores (_)\n\nThis is used as hashtag in the changeset message, which will read something like \"Adding data with #mapcomplete for theme #\"\n\nOn official themes, it'll become the name of the page, e.g.\n'cyclestreets' which become 'cyclestreets.html'\n\ntype: id\ngroup: basic", + "type": "string" + }, + "title": { + "description": "question: What is the title of this theme?\n\nThe human-readable title, as shown in the welcome message and the index page\ngroup: basic", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "mustHaveLanguage": { + "description": "Only used in 'generateLayerOverview': if present, every translation will be checked to make sure it is fully translated.\n\nThis must be a list of two-letter, lowercase codes which identifies the language, e.g. \"en\", \"nl\", ...", + "type": "array", + "items": { + "type": "string" + } + }, + "description": { + "description": "question: How would you describe this theme?\nThe description, as shown in the welcome message and the more-screen\ngroup: basic", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "shortDescription": { + "description": "A short description, showed as social description and in the 'more theme'-buttons.\nNote that if this one is not defined, the first sentence of 'description' is used\ngroup: hidden", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "descriptionTail": { + "description": "A part of the description, shown under the login-button.\ngroup: hidden", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "icon": { + "description": "question: What icon should be used to represent this theme?\n\nUsed as logo in the more-screen and (for official themes) as favicon, webmanifest logo, ...\n\nEither a URL or a base64 encoded value (which should include 'data:image/svg+xml;base64)\n\nType: icon\nsuggestions: return Constants.defaultPinIcons.map(i => ({if: \"value=\"+i, then: i, icon: i}))\ngroup: basic", + "type": "string" + }, + "socialImage": { + "description": "question: What image should be used as social image preview?\nThis is included as og:image-tag on official themes.\n\nSee https://www.h3xed.com/web-and-internet/how-to-use-og-image-meta-tag-facebook-reddit for more information\nifunset: use the default social image of mapcomplete (or generate one based on the icon)\nType: image\ngroup: basic", + "type": "string" + }, + "extraLink": { + "description": "question: should an extra help button be shown in certain circumstances?\nAdds an additional button on the top-left of the application.\nThis can link to an arbitrary location.\n\nFor example {icon: \"./assets/svg/pop-out.svg\", href: 'https://mapcomplete.org/{theme}.html?lat={lat}&lon={lon}&z={zoom}, requirements: [\"iframe\",\"no-welcome-message]},\n\ngroup: advanced\nifunset: show a link to open MapComplete full screen if used in an iframe", + "$ref": "#/definitions/default" + }, + "startZoom": { + "description": "question: At what zoomlevel should this theme open?\nDefault location and zoom to start.\nNote that this is barely used. Once the user has visited mapcomplete at least once, the previous location of the user will be used\nifunset: Use the default startzoom (0)\ntype: float\ngroup: start_location", + "type": "number" + }, + "startLat": { + "description": "question: At what start latitude should this theme open?\nDefault location and zoom to start.\nNote that this is barely used. Once the user has visited mapcomplete at least once, the previous location of the user will be used\nifunset: Use 0 as start latitude\ntype: float\ngroup: start_location", + "type": "number" + }, + "startLon": { + "description": "question: At what start longitude should this theme open?\nDefault location and zoom to start.\nNote that this is barely used. Once the user has visited mapcomplete at least once, the previous location of the user will be used\nifunset: Use 0 as start longitude\ntype: float\ngroup: start_location", + "type": "number" + }, + "defaultBackgroundId": { + "description": "The id of the default background. BY default: vanilla OSM", + "type": "string" + }, + "credits": { + "description": "Who helped to create this theme and should be attributed?", + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "hideFromOverview": { + "description": "If set to true, this layout will not be shown in the overview with more themes", + "type": "boolean" + }, + "layers": { + "description": "question: What layers should this map show?\ntype: layer[]\ntypes: hidden | layer | hidden\ngroup: layers\ntitle: value[\"builtin\"] ?? value[\"id\"] ?? value\nsuggestions: return Array.from(layers.keys()).map(key => ({if: \"value=\"+key, then: \"\"+key+\" (builtin) - \"+layers.get(key).description}))\n\nA theme must contain at least one layer.\n\nA layer contains all features of a single type, for example \"shops\", \"bicycle pumps\", \"benches\".\nNote that every layer contains a specification of attributes that it should match. MapComplete will fetch the relevant data from either overpass, the OSM-API or the cache server.\nIf a feature can match multiple layers, the first matching layer in the list will be used.\nThis implies that the _order_ of the layers is important.\n\n\n
\nNote that builtin layers can be reused. Either put in the name of the layer to reuse, or use {builtin: \"layername\", override: ...}\n\nThe 'override'-object will be copied over the original values of the layer, which allows to change certain aspects of the layer\n\nFor example: If you would like to use layer nature reserves, but only from a specific operator (eg. Natuurpunt) you would use the following in your theme:\n\n```\n\"layer\": {\n \"builtin\": \"nature_reserve\",\n \"override\": {\"source\":\n {\"osmTags\": {\n \"+and\":[\"operator=Natuurpunt\"]\n }\n }\n }\n}\n```\n\nIt's also possible to load multiple layers at once, for example, if you would like for both drinking water and benches to start at the zoomlevel at 12, you would use the following:\n\n```\n\"layer\": {\n \"builtin\": [\"benches\", \"drinking_water\"],\n \"override\": {\"minzoom\": 12}\n}\n```\n
", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/LayerConfigJson" + }, + { + "type": "object", + "properties": { + "builtin": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "override": { + "$ref": "#/definitions/Partial" + }, + "hideTagRenderingsWithLabels": { + "description": "TagRenderings with any of these labels will be removed from the layer.\nNote that the 'id' and 'group' are considered labels too", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "builtin", + "override" + ] + }, + { + "type": "string" + } + ] + } + }, + "overrideAll": { + "description": "An override applied on all layers of the theme.\n\nE.g.: if there are two layers defined:\n```\n\"layers\":[\n {\"title\": ..., \"tagRenderings\": [...], \"osmSource\":{\"tags\": ...}},\n {\"title\", ..., \"tagRenderings\", [...], \"osmSource\":{\"tags\" ...}}\n]\n```\n\nand overrideAll is specified:\n```\n\"overrideAll\": {\n \"osmSource\":{\"geoJsonSource\":\"xyz\"}\n}\nthen the result will be that all the layers will have these properties applied and result in:\n\"layers\":[\n {\"title\": ..., \"tagRenderings\": [...], \"osmSource\":{\"tags\": ..., \"geoJsonSource\":\"xyz\"}},\n {\"title\", ..., \"tagRenderings\", [...], \"osmSource\":{\"tags\" ..., \"geoJsonSource\":\"xyz\"}}\n]\n```\n\nIf the overrideAll contains a list where the keys starts with a plus, the values will be appended (instead of discarding the old list), for example\n\n\"overrideAll\": {\n \"+tagRenderings\": [ { ... some tagrendering ... }]\n}\n\nIn the above scenario, `sometagrendering` will be added at the beginning of the tagrenderings of every layer", + "$ref": "#/definitions/Partial" + }, + "tileLayerSources": { + "description": "Define some (overlay) slippy map tilesources", + "type": "array", + "items": { + "allOf": [ + { + "$ref": "#/definitions/RasterLayerProperties" + }, + { + "type": "object", + "properties": { + "defaultState": { + "type": "boolean" + } + } + } + ] + } + }, + "customCss": { + "description": "The URL of a custom CSS stylesheet to modify the layout\ngroup: advanced", + "type": "string" + }, + "lockLocation": { + "description": "If set to true, the basemap will not scroll outside of the area visible on initial zoom.\nIf set to [[lon, lat], [lon, lat]], the map will not scroll outside of those bounds.\nOff by default, which will enable panning to the entire world", + "anyOf": [ + { + "type": "array", + "items": [ + { + "type": "array", + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "minItems": 2, + "maxItems": 2 + }, + { + "type": "array", + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "minItems": 2, + "maxItems": 2 + } + ], + "minItems": 2, + "maxItems": 2 + }, + { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + } + } + } + ] + }, + "enableUserBadge": { + "description": "question: Should a user be able to login with OpenStreetMap?\n\nIf not logged in, will not show the login buttons and hide all the editable elements.\nAs such, MapComplete will become read-only and a purely visualisation tool.\n\nifunset: Enable the possiblity to login with OpenStreetMap (default)\niffalse: Do not enable to login with OpenStreetMap, have a read-only view of MapComplete.\niftrue: Enable the possiblity to login with OpenStreetMap\ngroup: feature_switches", + "type": "boolean" + }, + "enableShareScreen": { + "description": "question: Should the tab with options to share the current screen be enabled?\n\nOn can get the iFrame embed code here\n\nifunset: Enable the sharescreen (default)\niffalse: Do not enable the share screen\niftrue: Enable the share screen\ngroup: feature_switches", + "type": "boolean" + }, + "enableMoreQuests": { + "description": "question: Should the user be able to switch to different themes?\n\nTypically enabled in iframes and/or on commisioned themes\n\niftrue: enable to go back to the index page showing all themes\niffalse: do not enable to go back to the index page showing all themes; hide the 'more themes' buttons\nifunset: mapcomplete default: enable to go back to the index page showing all themes\ngroup: feature_switches", + "type": "boolean" + }, + "enableLayers": { + "description": "question: Should the user be able to enable/disable layers and to filter the layers?\n\nThe corresponding URL-parameter is 'fs-filters' instead of 'fs-layers'\niftrue: enable the filters/layers pane\niffalse: do not enable to filter or to disable layers; hide the 'filter' tab from the overview and the button at the bottom-left\nifunset: mapcomplete default: enable to filter or to enable/disable layers\ngroup: feature_switches", + "type": "boolean" + }, + "enableSearch": { + "description": "question: Should the user be able to search for locations?\n\nifunset: MapComplete default: allow to search\niftrue: Allow to search\niffalse: Do not allow to search; hide the search-bar\ngroup: feature_switches", + "type": "boolean" + }, + "enableAddNewPoints": { + "description": "question: Should the user be able to add new points?\n\nAdding new points is only possible if the loaded layers have presets set.\nSome layers do not have presets. If the theme only has layers without presets, then adding new points will not be possible.\n\nifunset: MapComplete default: allow to create new points\niftrue: Allow to create new points\niffalse: Do not allow to create new points, even if the layers in this theme support creating new points\ngroup: feature_switches", + "type": "boolean" + }, + "enableGeolocation": { + "description": "question: Should the user be able to use their GPS to geolocate themselfes on the map?\nifunset: MapComplete default: allow to use the GPS\niftrue: Allow to use the GPS\niffalse: Do not allow to use the GPS, hide the geolocation-buttons\ngroup: feature_switches", + "type": "boolean" + }, + "enableBackgroundLayerSelection": { + "description": "Enable switching the backgroundlayer.\nIf false, the quickswitch-buttons are removed (bottom left) and the dropdown in the layer selection is removed as well\n\nquestion: Should the user be able to switch the background layer?\n\niftrue: Allow to switch the background layer\niffalse: Do not allow to switch the background layer\nifunset: MapComplete default: Allow to switch the background layer\ngroup: feature_switches", + "type": "boolean" + }, + "enableShowAllQuestions": { + "description": "question: Should the questions about a feature be presented one by one or all at once?\niftrue: Show all unanswered questions at the same time\niffalse: Show unanswered questions one by one\nifunset: MapComplete default: Use the preference of the user to show questions at the same time or one by one\ngroup: feature_switches", + "type": "boolean" + }, + "enableDownload": { + "description": "question: Should the 'download as CSV'- and 'download as Geojson'-buttons be enabled?\niftrue: Enable the option to download the map as CSV and GeoJson\niffalse: Disable the option to download the map as CSV and GeoJson\nifunset: MapComplete default: Enable the option to download the map as CSV and GeoJson\ngroup: feature_switches", + "type": "boolean" + }, + "enablePdfDownload": { + "description": "question: Should the 'download as PDF'-button be enabled?\niftrue: Enable the option to download the map as PDF\niffalse: Enable the option to download the map as PDF\nifunset: MapComplete default: Enable the option to download the map as PDF\ngroup: feature_switches", + "type": "boolean" + }, + "enableNoteImports": { + "description": "question: Should the 'notes' from OpenStreetMap be loaded and parsed for import helper notes?\nIf true, notes will be loaded and parsed. If a note is an import (as created by the import_helper.html-tool from mapcomplete),\nthese notes will be shown if a relevant layer is present.\n\nifunset: MapComplete default: do not load import notes for sideloaded themes but do load them for official themes\niftrue: Load notes and show import notes\niffalse: Do not load import notes\ngroup: advanced", + "type": "boolean" + }, + "enableTerrain": { + "description": "question: Should the map use elevation data to give a 3D-feel?\n\nThis is especially useful for hiking maps, skiing maps etc...\n\nifunset: MapComplete default: don't use terrain\niftrue: Use elevation and render 3D\niffalse: Do not use terrain\ngroup: advanced", + "type": "boolean" + }, + "overpassUrl": { + "description": "question: What overpass-api instance should be used for this layout?\n\nifunset: Use the default, builtin collection of overpass instances\ngroup: advanced", + "type": "array", + "items": { + "type": "string" + } + }, + "overpassTimeout": { + "description": "question: After how much seconds should the overpass-query stop?\nIf a query takes too long, the overpass-server will abort.\nOnce can set the amount of time before overpass gives up here.\nifunset: use the default amount of 30 seconds as timeout\ntype: pnat\ngroup: advanced", + "type": "number" + }, + "overpassMaxZoom": { + "description": "At low zoom levels, overpass is used to query features.\nAt high zoom level, the OSM api is used to fetch one or more BBOX aligning with a slippy tile.\nThe overpassMaxZoom controls the flipoverpoint: if the zoom is this or lower, overpass is used.", + "type": "number" + }, + "osmApiTileSize": { + "description": "When the OSM-api is used to fetch features, it does so in a tiled fashion.\nThese tiles are using a ceratin zoom level, that can be controlled here\nDefault: overpassMaxZoom + 1", + "type": "number" + }, + "enableNodeDatabase": { + "description": "Enables tracking of all nodes when data is loaded.\nThis is useful for the 'ImportWay' and 'ConflateWay'-buttons who need this database.\n\nNote: this flag will be automatically set and can thus be ignored.\ngroup: hidden", + "type": "boolean" + }, + "enableMorePrivacy": { + "description": "question: Should this theme leak some location info when making changes?\n\nWhen a changeset is made, a 'distance to object'-class is written to the changeset.\nFor some particular themes and layers, this might leak too much information, and we want to obfuscate this\n\nifunset: Write 'change_within_x_m' as usual and if GPS is enabled\niftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey", + "type": "boolean" + }, + "enableCache": { + "description": "question: Should this theme have the cache enabled?\n\nShould only be dissabled in highly specific cases, such as the GRB-theme\n\nifunset: Cache is enabled\niffalse: Do not cache data\ngroup: hidden", + "type": "boolean" + }, + "_usedImages": { + "description": "Set by the preprocessor\ngroup: hidden", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "description", + "icon", + "id", + "layers", + "title" + ], + "definitions": { + "TagConfigJson": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "type": "object", + "properties": { + "or": { + "type": "array", + "items": { + "$ref": "#/definitions/TagConfigJson" + } + } + }, + "required": [ + "or" + ] + }, + { + "type": "string" + } + ] + }, + "{and:TagConfigJson[];}": { + "type": "object", + "properties": { + "and": { + "type": "array", + "items": { + "$ref": "#/definitions/TagConfigJson" + } + } + }, + "required": [ + "and" + ] + }, + "Record": { + "type": "object" + }, + "{or:TagConfigJson[];}": { + "type": "object", + "properties": { + "or": { + "type": "array", + "items": { + "$ref": "#/definitions/TagConfigJson" + } + } + }, + "required": [ + "or" + ] + }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ] + }, + "Record": { + "type": "object" + }, + "Record>": { + "type": "object" + }, + "DenominationConfigJson": { + "type": "object", + "properties": { + "useIfNoUnitGiven": { + "description": "If this evaluates to true and the value to interpret has _no_ unit given, assumes that this unit is meant.\nAlternatively, a list of country codes can be given where this acts as the default interpretation\n\nE.g., a denomination using \"meter\" would probably set this flag to \"true\";\na denomination for \"mp/h\" will use the condition \"_country=gb\" to indicate that it is the default in the UK.\n\nIf none of the units indicate that they are the default, the first denomination will be used instead", + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "boolean" + } + ] + }, + "canonicalDenomination": { + "description": "The canonical value for this denomination which will be added to the value in OSM.\ne.g. \"m\" for meters\nIf the user inputs '42', the canonical value will be added and it'll become '42m'.\n\nImportant: often, _no_ canonical values are expected, e.g. in the case of 'maxspeed' where 'km/h' is the default.\nIn this case, an empty string should be used", + "type": "string" + }, + "canonicalDenominationSingular": { + "description": "The canonical denomination in the case that the unit is precisely '1'.\nUsed for display purposes only.\n\nE.g.: for duration of something in minutes: `2 minutes` but `1 minute`; the `minute` goes here", + "type": "string" + }, + "alternativeDenomination": { + "description": "A list of alternative values which can occur in the OSM database - used for parsing.\nE.g.: while 'm' is canonical, `meter`, `mtrs`, ... can occur as well", + "type": "array", + "items": { + "type": "string" + } + }, + "human": { + "description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"meter\",\n \"fr\": \"metre\"\n}", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "humanSingular": { + "description": "The value for humans in the dropdown. This should not use abbreviations and should be translated, e.g.\n{\n \"en\": \"minute\",\n \"nl\": \"minuut\"\n}", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "prefix": { + "description": "If set, then the canonical value will be prefixed instead, e.g. for '€'\nNote that if all values use 'prefix', the dropdown might move to before the text field", + "type": "boolean" + }, + "addSpace": { + "description": "If set, add a space between the quantity and the denomination.\n\nE.g.: `50 mph` instad of `50mph`", + "type": "boolean" + } + }, + "required": [ + "canonicalDenomination" + ] + }, + "MinimalTagRenderingConfigJson": { + "description": "Mostly used for lineRendering and pointRendering", + "type": "object", + "properties": { + "render": { + "description": "question: What value should be shown (if no predefined option matches)?\n\nThis piece of text will be shown in the infobox.\nNote that \"&LBRACEkey&RBRACE\"-parts are substituted by the corresponding values of the element.\n\nThis value will be used if there is no mapping which matches (or there are no matches)\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`", + "type": "string" + }, + "mappings": { + "description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes", + "type": "array", + "items": { + "type": "object", + "properties": { + "if": { + "$ref": "#/definitions/TagConfigJson", + "description": "question: When should this single mapping match?\n\nIf this condition is met, then the text under `then` will be shown.\nIf no value matches, and the user selects this mapping as an option, then these tags will be uploaded to OSM.\n\nFor example: {'if': 'diet:vegetarion=yes', 'then':'A vegetarian option is offered here'}\n\nThis can be an substituting-tag as well, e.g. {'if': 'addr:street:={_calculated_nearby_streetname}', 'then': '{_calculated_nearby_streetname}'}" + }, + "then": { + "description": "question: What text should be shown?\n\nIf the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option", + "type": "string" + } + }, + "required": [ + "if", + "then" + ] + } + } + } + }, + "IconConfigJson": { + "type": "object", + "properties": { + "icon": { + "description": "question: What icon should be used?\ntypes: Use a different icon depending on the value of some attributes ; icon\nsuggestions: return Constants.defaultPinIcons.map(i => ({if: \"value=\"+i, then: i, icon: i}))", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "builtin": { + "type": "string" + }, + "override": {} + }, + "required": [ + "builtin", + "override" + ] + }, + { + "type": "string" + } + ] + }, + "color": { + "description": "question: What colour should the icon be?\nThis will only work for the default icons such as `pin`,`circle`,...\ntypes: Use a different color depending on the value of some attributes ; color", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "builtin": { + "type": "string" + }, + "override": {} + }, + "required": [ + "builtin", + "override" + ] + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "icon" + ] + }, + "TagRenderingConfigJson": { + "description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one", + "type": "object", + "properties": { + "render": { + "description": "question: What text should be rendered?\n\nThis piece of text will be shown in the infobox.\nIn this text, values within braces (such as {braced(key)}) are replaced by the corresponding `value` in the object.\nFor example, if the object contains tags `amenity=school; name=Windy Hill School`, the render string `This school is named {name}` will be shown to the user as `This school is named Windy Hill School`\n\nThis text will be shown if:\n- there is no mapping which matches (or there are no matches)\n- no question, no mappings and no 'freeform' is set\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`\ntype: rendered\nifunset: No text is shown if no predefined options match.", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "object", + "properties": { + "special": { + "allOf": [ + { + "$ref": "#/definitions/Record>" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ] + } + ] + } + }, + "required": [ + "special" + ] + }, + { + "type": "string" + } + ] + }, + "icon": { + "description": "question: what icon should be shown next to the 'render' value?\nAn icon shown next to the rendering; typically shown pretty small\nThis is only shown next to the \"render\" value\nType: icon\nifunset: No additional icon is shown next to the always shown text", + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "path" + ] + }, + { + "type": "string" + } + ] + }, + "condition": { + "description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "metacondition": { + "description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the usersettings/application state; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "freeform": { + "description": "question: Should a freeform text field be shown?\nAllow freeform text input from the user\nifunset: Do not add a freeform text field", + "type": "object", + "properties": { + "key": { + "description": "What attribute should be filled out\nIf this key is present in the feature, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown", + "type": "string" + } + } + }, + "mappings": { + "description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes", + "type": "array", + "items": { + "type": "object", + "properties": { + "if": { + "$ref": "#/definitions/TagConfigJson", + "description": "question: When should this single mapping match?\n\nIf this condition is met, then the text under `then` will be shown.\nIf no value matches, and the user selects this mapping as an option, then these tags will be uploaded to OSM.\n\nFor example: {'if': 'diet:vegetarion=yes', 'then':'A vegetarian option is offered here'}\n\nThis can be an substituting-tag as well, e.g. {'if': 'addr:street:={_calculated_nearby_streetname}', 'then': '{_calculated_nearby_streetname}'}" + }, + "then": { + "description": "question: What text should be shown?\n\nIf the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "icon": { + "description": "question: What icon should be added to this mapping?\nifunset: Do not show an extra icon next to the render value\n\nAn icon supporting this mapping; typically shown pretty small.\nThis can be used to show a e.g. 'phone'-icon next to the phone number\n\nThis supports patterns, you can e.g. have `close:red;some/other/icon.svg`\n\ninline: {icon}\nType: icon", + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "path" + ] + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "if", + "then" + ] + } + }, + "description": { + "description": "A human-readable text explaining what this tagRendering does.\nMostly used for the shared tagrenderings", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "classes": { + "description": "question: What css-classes should be applied to showing this attribute?\n\nA list of css-classes to apply to the entire tagRendering.\nThese classes are applied in 'answer'-mode, not in question mode\nThis is only for advanced users.\n\nValues are split on ` ` (space)", + "type": "string" + }, + "filter": { + "description": "This tagRendering can introduce this builtin filter", + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] + } + } + }, + "MappingConfigJson": { + "type": "object", + "properties": { + "if": { + "$ref": "#/definitions/TagConfigJson", + "description": "question: What tags should be matched to show this option?\n\nIf in 'question'-mode and the contributor selects this option, these tags will be applied to the object" + }, + "then": { + "description": "Question: What corresponding text should be shown?\nShown if the `if` is fulfilled\nType: rendered", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "icon": { + "description": "question: What icon should be shown next to this mapping?\n\nThis icon will only be shown if the value is known, it is not displayed in the options (but might be in the future)\n\nifunset: Show no icon\nType: icon", + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "Size of the image", + "type": "string" + } + }, + "required": [ + "path" + ] + }, + { + "type": "string" + } + ] + }, + "hideInAnswer": { + "description": "question: Under what circumstances should this mapping be hidden from the possibilities a contributor can pick?\niftrue: Never show this mapping as option to pick\nifunset: Always show this mapping as option to pick\ntype: tag\n\nIn some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).\n\nIn the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user.\nIn this case, one of the mappings can be hiden by setting this flag.\n\nTo demonstrate an example making a default assumption:\n\nmappings: [\n {\n if: \"access=\", -- no access tag present, we assume accessible\n then: \"Accessible to the general public\",\n hideInAnswer: true\n },\n {\n if: \"access=yes\",\n then: \"Accessible to the general public\", -- the user selected this, we add that to OSM\n },\n {\n if: \"access=no\",\n then: \"Not accessible to the public\"\n }\n]\n\n\nFor example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`.\nThen, we would add two mappings:\n{\n if: \"operator=Agentschap Natuur en Bos\" -- the non-abbreviated version which should be uploaded\n then: \"Maintained by Agentschap Natuur en Bos\"\n},\n{\n if: \"operator=ANB\", -- we don't want to upload abbreviations\n then: \"Maintained by Agentschap Natuur en Bos\"\n hideInAnswer: true\n}\n\nHide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate.\nKeep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch\n\ne.g., for toilets: if \"wheelchair=no\", we know there is no wheelchair dedicated room.\nFor the location of the changing table, the option \"in the wheelchair accessible toilet is weird\", so we write:\n\n{\n \"question\": \"Where is the changing table located?\"\n \"mappings\": [\n {\"if\":\"changing_table:location=female\",\"then\":\"In the female restroom\"},\n {\"if\":\"changing_table:location=male\",\"then\":\"In the male restroom\"},\n {\"if\":\"changing_table:location=wheelchair\",\"then\":\"In the wheelchair accessible restroom\", \"hideInAnswer\": \"wheelchair=no\"},\n\n ]\n}\n\nAlso have a look for the meta-tags\n{\n if: \"operator=Agentschap Natuur en Bos\",\n then: \"Maintained by Agentschap Natuur en Bos\",\n hideInAnswer: \"_country!=be\"\n}", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": [ + "string", + "boolean" + ] + } + ] + }, + "alsoShowIf": { + "description": "question: In what other cases should this item be rendered?\n\nAlso show this 'then'-option if the feature matches these tags.\nIdeal for outdated tags or default assumptions. The tags from this options will not be set if the option is chosen!\n\nifunset: No other cases when this text is shown", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "ifnot": { + "description": "question: What tags should be applied if this mapping is _not_ chosen?\n\nOnly applicable if 'multiAnswer' is set.\nThis is for situations such as:\n`accepts:coins=no` where one can select all the possible payment methods. However, we want to make explicit that some options _were not_ selected.\nThis can be done with `ifnot`\nNote that we can not explicitly render this negative case to the user, we cannot show `does _not_ accept coins`.\nIf this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer`\n\nifunset: Do not apply a tag if a different mapping is chosen.", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "addExtraTags": { + "description": "question: What extra tags should be added to the object if this object is chosen?\ntype: simple_tag\n\nIf chosen as answer, these tags will be applied onto the object, together with the tags from the `if`.\nNote that if the contributor picks this mapping, saves and then changes their mind and uses a different mapping,\nthe extraTags will reside.\nE.g. when picking `memorial:type=bench`, then `amenity=bench` will also be applied.\nIf someone later on changes the type to `memorial:statue`, `amenity=bench` will stay onto the object\n(which is the desired behaviour, see e.g. for https://www.openstreetmap.org/node/5620038478)\nUse 'ifNot' to explicitly remove an tag if this is important\n\nIf someone marks the question as 'unknown', the extra tags will not be erased\n\nNot compatible with multiAnswer.\n\nThis can be used e.g. to erase other keys which indicate the 'not' value:\n```json\n{\n \"if\": \"crossing:marking=rainbow\",\n \"then\": \"This is a rainbow crossing\",\n \"addExtraTags\": [\"not:crossing:marking=\"]\n}\n```", + "type": "array", + "items": { + "type": "string" + } + }, + "searchTerms": { + "description": "question: If there are many options, what search terms match too?\nIf there are many options, the mappings-radiobuttons will be replaced by an element with a searchfunction\n\nSearchterms (per language) allow to easily find an option if there are many options\ngroup: hidden", + "$ref": "#/definitions/Record" + }, + "priorityIf": { + "description": "If the searchable selector is picked, mappings with this item will have priority and show up even if the others are hidden\nUse this sparingly\ngroup: hidden", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "#": { + "description": "Used for comments or to disable a validation\n\ngroup: hidden\nignore-image-in-then: normally, a `then`-clause is not allowed to have an `img`-html-element as icons are preferred. In some cases (most notably title-icons), this is allowed", + "type": "string" + } + }, + "required": [ + "if", + "then" + ] + }, + "T": { + "type": "object" + }, + "default_4": { + "description": "The PointRenderingConfig gives all details onto how to render a single point of a feature.\n\nThis can be used if:\n\n- The feature is a point\n- To render something at the centroid of an area, or at the start, end or projected centroid of a way", + "type": "object", + "properties": { + "location": { + "description": "question: At what location should this icon be shown?\nmultianswer: true\nsuggestions: return [{if: \"value=point\",then: \"Show an icon for point (node) objects\"},{if: \"value=centroid\",then: \"Show an icon for line or polygon (way) objects at their centroid location\"}, {if: \"value=start\",then: \"Show an icon for line (way) objects at the start\"},{if: \"value=end\",then: \"Show an icon for line (way) object at the end\"},{if: \"value=projected_centerpoint\",then: \"Show an icon for line (way) object near the centroid location, but moved onto the line. Does not show an item on polygons\"}, {if: \"value=polygon_centroid\",then: \"Show an icon at a polygon centroid (but not if it is a way)\"}]", + "type": "array", + "items": { + "type": "string" + } + }, + "marker": { + "description": "The marker for an element.\nNote that this also defines the icon for this layer (rendered with the overpass-tags) and the icon in the presets.\n\nThe result of the icon is rendered as follows:\n- The first icon is rendered on the map\n- The second entry is overlayed on top of it\n- ...\n\nAs a result, on could use a generic icon (`pin`, `circle`, `square`) with a color, then overlay it with a specific icon.\nicon: value\ntitle: value.icon", + "type": "array", + "items": { + "$ref": "#/definitions/IconConfigJson" + } + }, + "iconBadges": { + "description": "A list of extra badges to show next to the icon as small badge\nThey will be added as a 25% height icon at the bottom right of the icon, with all the badges in a flex layout.\n\nNote: strings are interpreted as icons, so layering and substituting is supported. You can use `circle:white;./my_icon.svg` to add a background circle\ngroup: hidden", + "type": "array", + "items": { + "type": "object", + "properties": { + "if": { + "$ref": "#/definitions/TagConfigJson", + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag" + }, + "then": { + "description": "Badge to show\nType: icon", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "if", + "then" + ] + } + }, + "iconSize": { + "description": "question: What size should the marker be on the map?\nA string containing \",\" in pixels\nifunset: Use the default size (40,40 px)", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "anchor": { + "description": "question: What is the anchorpoint of the icon?\n\nThis matches the geographical point with a location on the icon.\n\nifunset: Use MapComplete-default (center)\nsuggestions: return [{if: \"value=center\", then: \"Place the center of the icon on the geographical location\"},{if: \"value=top\", then: \"Place the top of the icon on the geographical location\"},{if: \"value=bottom\", then: \"Place the bottom of the icon on the geographical location\"},{if: \"value=left\", then: \"Place the left of the icon on the geographical location\"},{if: \"value=right\", then: \"Place the right of the icon on the geographical location\"}]", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "rotation": { + "description": "question: What rotation should be applied on the icon?\nThis is mostly useful for items that face a specific direction, such as surveillance cameras\nThis is interpreted as css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)`\n\nIf the icon is shown on the projected centerpoint of a way, one can also use `_direction:centerpoint`\n\ntypes: Dynamic value ; string\nsuggestions: return [{if: \"value={_direction:centerpoint}deg\", then: \"Point the top of the icon towards the end of the way\"}, {if: \"value=calc( {_direction:centerpoint}deg + 90deg )\", then: \"Point the left of the icon towards the end of the way\"}, {if: \"value=calc( {_direction:centerpoint}deg - 90deg )\", then: \"Point the right of the icon towards the end of the way\"}, {if: \"value=calc( {_direction:centerpoint}deg + 180deg )\", then: \"Point the bottom of the icon towards the end of the way\"}]\nifunset: Do not rotate", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "label": { + "description": "question: What label should be shown beneath the marker?\nFor example: `<div style=\"background: white\">{name}</div>`\n\nIf the icon is undefined, then the label is shown in the center of the feature.\ntypes: Dynamic value based on the attributes ; string\ninline: Always show label {value} beneath the marker\nifunset: Do not show a label beneath the marker", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "labelCss": { + "description": "question: What CSS should be applied to the label?\nYou can set the css-properties here, e.g. `background: red; font-size: 12px; `\ninline: Apply CSS-style {value} to the label\ntypes: Dynamic value ; string\nifunset: Do not apply extra CSS-labels to the label\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "labelCssClasses": { + "description": "question: Which CSS-classes should be applied to the label?\n\nThe classes should be separated by a space (` `)\nYou can use most Tailwind-css classes, see https://tailwindcss.com/ for more information\nFor example: `center bg-gray-500 mx-2 my-1 rounded-full`\ninline: Apply CSS-classes {value} to the label\ntypes: Dynamic value ; string\nifunset: Do not apply extra CSS-classes to the label\nsuggestions: return [{if: \"value=bg-white rounded px-2\", then: \"Draw on a white background\"}]", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "css": { + "description": "question: What CSS should be applied to the entire marker?\nYou can set the css-properties here, e.g. `background: red; font-size: 12px; `\nThis will be applied to the _container_ containing both the marker and the label\ninline: Apply CSS-style {value} to the _entire marker_\ntypes: Dynamic value ; string\nifunset: Do not apply extra CSS element to the entire marker\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "cssClasses": { + "description": "question: Which CSS-classes should be applied to the entire marker?\nThis will be applied to the _container_ containing both the marker and the label\n\nThe classes should be separated by a space (` `)\nYou can use most Tailwind-css classes, see https://tailwindcss.com/ for more information\nFor example: `center bg-gray-500 mx-2 my-1 rounded-full`\ninline: Apply CSS-classes {value} to the entire container\ntypes: Dynamic value ; string\nifunset: Do not apply extra CSS-classes to the entire marker\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "pitchAlignment": { + "description": "question: If the map is pitched, should the icon stay parallel to the screen or to the groundplane?\nsuggestions: return [{if: \"value=canvas\", alsoShowIf: \"value=\", then: \"The icon will stay upward and not be transformed as if it sticks to the screen\"}, {if: \"value=map\", then: \"The icon will be transformed as if it were painted onto the ground. (Automatically sets rotationAlignment)\"}]\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "enum": [ + "canvas", + "map" + ], + "type": "string" + } + ] + }, + "rotationAlignment": { + "description": "question: Should the icon be rotated if the map is rotated?\nifunset: Do not rotate or tilt icons. Always keep the icons straight\nsuggestions: return [{if: \"value=canvas\", then: \"Never rotate the icon\"}, {if: \"value=map\", then: \"If the map is rotated, rotate the icon as well. This gives the impression of an icon that floats perpendicular above the ground.\"}]\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "enum": [ + "canvas", + "map" + ], + "type": "string" + } + ] + } + }, + "required": [ + "location" + ] + }, + "default_5": { + "description": "The LineRenderingConfig gives all details onto how to render a single line of a feature.\n\nThis can be used if:\n\n- The feature is a line\n- The feature is an area", + "type": "object", + "properties": { + "color": { + "description": "question: What color should lines be drawn in?\n\nFor an area, this will be the colour of the outside line.\nIf the value starts with \"--\", the style of the body element will be queried for the corresponding variable instead\n\ntypes: dynamic value ; string\ntitle: Line Colour\ninline: The line colour always is {value}\ntype: color", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "width": { + "description": "question: How wide should the line be?\nThe stroke-width for way-elements\n\ntypes: dynamic value ; string\ntitle: Line width\ninline: The line width is {value} pixels\ntype: pnat\nifunset: Use the default-linewidth of 7 pixels", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": [ + "string", + "number" + ] + } + ] + }, + "dashArray": { + "description": "question: Should a dasharray be used to render the lines?\nThe dasharray defines 'pixels of line, pixels of gap, pixels of line, pixels of gap, ...'. For example, `5 6` will be 5 pixels of line followed by a 6 pixel gap.\nCannot be a dynamic property due to a MapLibre limitation (see https://github.com/maplibre/maplibre-gl-js/issues/1235)\nifunset: Ways are rendered with a full line", + "type": "string" + }, + "lineCap": { + "description": "question: What form should the line-ending have?\nsuggestions: return [{if:\"value=round\",then:\"Round endings\"}, {if: \"value=square\", then: \"square endings\"}, {if: \"value=butt\", then: \"no ending (square ending at the end, without padding)\"}]\ntypes: dynamic value ; string\ntitle: Line Cap\nifunset: Use the default value (round ending)", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "fillColor": { + "description": "question: What colour should be used as fill colour for polygons?\nifunset: The polygon fill colour will be a more transparent version of the stroke colour\nsuggestions: return [{if: \"value=#00000000\", then: \"Use a transparent fill (only render the outline)\"}]\ninline: The fill colour is {value}\ntypes: dynamic value ; string\ntype: color", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "offset": { + "description": "question: Should the lines be moved (offsetted) with a number of pixels against the geographical lines?\nThe number of pixels this line should be moved.\nUse a positive number to move to the right in the drawing direction or a negative to move to the left (left/right as defined by the drawing direction of the line).\n\nIMPORTANT: MapComplete will already normalize 'key:both:property' and 'key:both' into the corresponding 'key:left' and 'key:right' tagging (same for 'sidewalk=left/right/both' which is rewritten to 'sidewalk:left' and 'sidewalk:right')\nThis simplifies programming. Refer to the CalculatedTags.md-documentation for more details\nifunset: don't offset lines on the map\ninline: Pixel offset by {value} pixels\ntypes: dynamic value ; number\ntype: int", + "anyOf": [ + { + "$ref": "#/definitions/MinimalTagRenderingConfigJson" + }, + { + "type": "number" + } + ] + }, + "imageAlongWay": { + "description": "question: What PNG-image should be shown along the way?\n\nifunset: no image is shown along the way\nsuggestions: [{if: \"./assets/png/oneway.png\", then: \"Show a oneway error\"}]\ntype: image", + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "if": { + "$ref": "#/definitions/TagConfigJson", + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag" + }, + "then": { + "type": "string" + } + }, + "required": [ + "if", + "then" + ] + } + }, + { + "type": "string" + } + ] + } + } + }, + "QuestionableTagRenderingConfigJson": { + "description": "A QuestionableTagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nIf the desired tags are missing and a question is defined, a question will be shown instead.", + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "mappings": { + "description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes\n\nquestion: What are common options?", + "type": "array", + "items": { + "$ref": "#/definitions/MappingConfigJson" + } + }, + "multiAnswer": { + "description": "question: Should a contributor be allowed to select multiple mappings?\n\nIf true, use checkboxes instead of radio buttons when asking the question.\n\niftrue: allow to select multiple mappings (and show a freeform-value as list if ';'-separated)\niffalse: only allow to select a single mapping\nifunset: only allow to select a single mapping", + "type": "boolean" + }, + "freeform": { + "description": "Allow freeform text input from the user", + "type": "object", + "properties": { + "key": { + "description": "question: What is the name of the attribute that should be written to?\nThis is the OpenStreetMap-key that that value will be written to\nifunset: do not offer a freeform textfield as answer option", + "type": "string" + }, + "type": { + "description": "question: What is the input type?\nThe type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values\nifunset: use an unconstrained string as input (default)\nsuggestions: return validators.AllValidators.filter(type => !type.isMeta).map((type) => ({if: \"value=\"+type.name, then: \"\"+type.name+\" \"+type.explanation.split(\"\\n\")[0]}))", + "type": "string" + }, + "placeholder": { + "description": "question: What placeholder text should be shown in the input-element if there is no input?\nA (translated) text that is shown (as gray text) within the textfield\ntype: translation\ngroup: expert\nifunset: No specific placeholder is set, show the type of the textfield", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "addExtraTags": { + "description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'\ngroup: expert", + "type": "array", + "items": { + "type": "string" + } + }, + "inline": { + "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question\ngroup: expert", + "type": "boolean" + }, + "default": { + "description": "question: What value should be entered in the text field if no value is set?\nThis can help people to quickly enter the most common option\nifunset: do not prefill the textfield\ngroup: expert", + "type": "string" + }, + "invalidValues": { + "description": "question: What values of the freeform key should be interpreted as 'unknown'?\nFor example, if a feature has `shop=yes`, the question 'what type of shop is this?' should still asked\nifunset: The question will be considered answered if any value is set for the key\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "postfixDistinguished": { + "description": "question: If this key shared and distinguished by a postfix, what is the postfix?\nThis option is used specifically for `charge`, where the cost is indicated with `/item`.\n\nFor example, a vending machine might sell `bicycle_tube`.\nSetting this value to `bicycle_tube`, then answering this question will set `charge= €XX/bicycle_tube`.\nIf charge did already contain another value, e.g. `charge= €YY/some_item; €ZZ/other_item`, then `€XX/bicycle_tube`will be added.\nNote: those values are sorted alphabetically\nNote: no need to add the `/`\n\nifunset: Don't distinguish by postfix\ngroup: expert", + "type": "string" + }, + "helperArgs": { + "description": "Extra arguments to configure the input element\ngroup: hidden" + } + }, + "required": [ + "helperArgs" + ] + }, + "question": { + "description": "question: What question should be shown to the contributor?\n\nA question is presented ot the user if no mapping matches and the 'freeform' key is not set as well.\n\nifunset: This tagrendering will be shown if it is known, but cannot be edited by the contributor, effectively resutling in a read-only rendering", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "questionHint": { + "description": "question: Should some extra information be shown to the contributor, alongside the question?\nThis hint is shown in subtle text under the question.\nThis can give some extra information on what the answer should ook like\nifunset: No extra hint is given", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "editButtonAriaLabel": { + "description": "When using a screenreader and selecting the 'edit' button, the current rendered value is read aloud in normal circumstances.\nIn some rare cases, this is not desirable. For example, if the rendered value is a link to a website, this link can be selected (and will be read aloud).\nIf the user presses _tab_ again, they'll select the button and have the link read aloud a second time.", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "labels": { + "description": "What labels should be applied on this tagRendering?\n\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\n\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search", + "type": "array", + "items": { + "type": "string" + } + }, + "render": { + "description": "question: What text should be rendered?\n\nThis piece of text will be shown in the infobox.\nIn this text, values within braces (such as {braced(key)}) are replaced by the corresponding `value` in the object.\nFor example, if the object contains tags `amenity=school; name=Windy Hill School`, the render string `This school is named {name}` will be shown to the user as `This school is named Windy Hill School`\n\nThis text will be shown if:\n- there is no mapping which matches (or there are no matches)\n- no question, no mappings and no 'freeform' is set\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`\ntype: rendered\nifunset: No text is shown if no predefined options match.", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "object", + "properties": { + "special": { + "allOf": [ + { + "$ref": "#/definitions/Record>" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ] + } + ] + } + }, + "required": [ + "special" + ] + }, + { + "type": "string" + } + ] + }, + "icon": { + "description": "question: what icon should be shown next to the 'render' value?\nAn icon shown next to the rendering; typically shown pretty small\nThis is only shown next to the \"render\" value\nType: icon\nifunset: No additional icon is shown next to the always shown text", + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "path" + ] + }, + { + "type": "string" + } + ] + }, + "condition": { + "description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "metacondition": { + "description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the usersettings/application state; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "description": { + "description": "A human-readable text explaining what this tagRendering does.\nMostly used for the shared tagrenderings", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "classes": { + "description": "question: What css-classes should be applied to showing this attribute?\n\nA list of css-classes to apply to the entire tagRendering.\nThese classes are applied in 'answer'-mode, not in question mode\nThis is only for advanced users.\n\nValues are split on ` ` (space)", + "type": "string" + }, + "filter": { + "description": "This tagRendering can introduce this builtin filter", + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] + } + }, + "required": [ + "id" + ] + }, + "Partial": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "mappings": { + "description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes\n\nquestion: What are common options?", + "type": "array", + "items": { + "$ref": "#/definitions/MappingConfigJson" + } + }, + "multiAnswer": { + "description": "question: Should a contributor be allowed to select multiple mappings?\n\nIf true, use checkboxes instead of radio buttons when asking the question.\n\niftrue: allow to select multiple mappings (and show a freeform-value as list if ';'-separated)\niffalse: only allow to select a single mapping\nifunset: only allow to select a single mapping", + "type": "boolean" + }, + "freeform": { + "description": "Allow freeform text input from the user", + "type": "object", + "properties": { + "key": { + "description": "question: What is the name of the attribute that should be written to?\nThis is the OpenStreetMap-key that that value will be written to\nifunset: do not offer a freeform textfield as answer option", + "type": "string" + }, + "type": { + "description": "question: What is the input type?\nThe type of the text-field, e.g. 'string', 'nat', 'float', 'date',...\nSee Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values\nifunset: use an unconstrained string as input (default)\nsuggestions: return validators.AllValidators.filter(type => !type.isMeta).map((type) => ({if: \"value=\"+type.name, then: \"\"+type.name+\" \"+type.explanation.split(\"\\n\")[0]}))", + "type": "string" + }, + "placeholder": { + "description": "question: What placeholder text should be shown in the input-element if there is no input?\nA (translated) text that is shown (as gray text) within the textfield\ntype: translation\ngroup: expert\nifunset: No specific placeholder is set, show the type of the textfield", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "addExtraTags": { + "description": "If a value is added with the textfield, these extra tag is addded.\nUseful to add a 'fixme=freeform textfield used - to be checked'\ngroup: expert", + "type": "array", + "items": { + "type": "string" + } + }, + "inline": { + "description": "question: Show the freeform as box within the question?\nInstead of showing a full-width text field, the text field will be shown within the rendering of the question.\n\nThis combines badly with special input elements, as it'll distort the layout.\nifunset: show the freeform input field full-width\niftrue: show the freeform input field as a small field within the question\ngroup: expert", + "type": "boolean" + }, + "default": { + "description": "question: What value should be entered in the text field if no value is set?\nThis can help people to quickly enter the most common option\nifunset: do not prefill the textfield\ngroup: expert", + "type": "string" + }, + "invalidValues": { + "description": "question: What values of the freeform key should be interpreted as 'unknown'?\nFor example, if a feature has `shop=yes`, the question 'what type of shop is this?' should still asked\nifunset: The question will be considered answered if any value is set for the key\ngroup: expert", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "postfixDistinguished": { + "description": "question: If this key shared and distinguished by a postfix, what is the postfix?\nThis option is used specifically for `charge`, where the cost is indicated with `/item`.\n\nFor example, a vending machine might sell `bicycle_tube`.\nSetting this value to `bicycle_tube`, then answering this question will set `charge= €XX/bicycle_tube`.\nIf charge did already contain another value, e.g. `charge= €YY/some_item; €ZZ/other_item`, then `€XX/bicycle_tube`will be added.\nNote: those values are sorted alphabetically\nNote: no need to add the `/`\n\nifunset: Don't distinguish by postfix\ngroup: expert", + "type": "string" + }, + "helperArgs": { + "description": "Extra arguments to configure the input element\ngroup: hidden" + } + }, + "required": [ + "helperArgs" + ] + }, + "question": { + "description": "question: What question should be shown to the contributor?\n\nA question is presented ot the user if no mapping matches and the 'freeform' key is not set as well.\n\nifunset: This tagrendering will be shown if it is known, but cannot be edited by the contributor, effectively resutling in a read-only rendering", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "questionHint": { + "description": "question: Should some extra information be shown to the contributor, alongside the question?\nThis hint is shown in subtle text under the question.\nThis can give some extra information on what the answer should ook like\nifunset: No extra hint is given", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "editButtonAriaLabel": { + "description": "When using a screenreader and selecting the 'edit' button, the current rendered value is read aloud in normal circumstances.\nIn some rare cases, this is not desirable. For example, if the rendered value is a link to a website, this link can be selected (and will be read aloud).\nIf the user presses _tab_ again, they'll select the button and have the link read aloud a second time.", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "labels": { + "description": "What labels should be applied on this tagRendering?\n\nA list of labels. These are strings that are used for various purposes, e.g. to only include a subset of the tagRenderings when reusing a layer\n\nSpecial values:\n- \"hidden\": do not show this tagRendering. Useful in it is used by e.g. an accordion\n- \"description\": this label is a description used in the search", + "type": "array", + "items": { + "type": "string" + } + }, + "render": { + "description": "question: What text should be rendered?\n\nThis piece of text will be shown in the infobox.\nIn this text, values within braces (such as {braced(key)}) are replaced by the corresponding `value` in the object.\nFor example, if the object contains tags `amenity=school; name=Windy Hill School`, the render string `This school is named {name}` will be shown to the user as `This school is named Windy Hill School`\n\nThis text will be shown if:\n- there is no mapping which matches (or there are no matches)\n- no question, no mappings and no 'freeform' is set\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`\ntype: rendered\nifunset: No text is shown if no predefined options match.", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "object", + "properties": { + "special": { + "allOf": [ + { + "$ref": "#/definitions/Record>" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "required": [ + "type" + ] + } + ] + } + }, + "required": [ + "special" + ] + }, + { + "type": "string" + } + ] + }, + "icon": { + "description": "question: what icon should be shown next to the 'render' value?\nAn icon shown next to the rendering; typically shown pretty small\nThis is only shown next to the \"render\" value\nType: icon\nifunset: No additional icon is shown next to the always shown text", + "anyOf": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "type": "string" + } + }, + "required": [ + "path" + ] + }, + { + "type": "string" + } + ] + }, + "condition": { + "description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "metacondition": { + "description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the usersettings/application state; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "description": { + "description": "A human-readable text explaining what this tagRendering does.\nMostly used for the shared tagrenderings", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "classes": { + "description": "question: What css-classes should be applied to showing this attribute?\n\nA list of css-classes to apply to the entire tagRendering.\nThese classes are applied in 'answer'-mode, not in question mode\nThis is only for advanced users.\n\nValues are split on ` ` (space)", + "type": "string" + }, + "filter": { + "description": "This tagRendering can introduce this builtin filter", + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "enum": [ + true + ], + "type": "boolean" + } + ] + } + } + }, + "default<(string|QuestionableTagRenderingConfigJson|{builtin:string;override:Partial;})[]>": { + "description": "Rewrites and multiplies the given renderings of type T.\n\nThis can be used for introducing many similar questions automatically,\nwhich also makes translations easier.\n\n(Note that the key does _not_ need to be wrapped in {}.\nHowever, we recommend to use them if the key is used in a translation, as missing keys will be picked up and warned for by the translation scripts)\n\nFor example:\n\n```\n{\n rewrite: {\n sourceString: [\"key\", \"a|b|c\"],\n into: [\n [\"X\", 0]\n [\"Y\", 1],\n [\"Z\", 2]\n ],\n renderings: [{\n \"key\":\"a|b|c\"\n }]\n }\n}\n```\nwill result in _three_ copies (as the values to rewrite into have three values, namely:\n\n[\n {\n # The first pair: key --> X, a|b|c --> 0\n \"X\": 0\n },\n {\n \"Y\": 1\n },\n {\n \"Z\": 2\n }\n\n]", + "type": "object", + "properties": { + "rewrite": { + "type": "object", + "properties": { + "sourceString": { + "type": "array", + "items": { + "type": "string" + } + }, + "into": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + } + }, + "required": [ + "into", + "sourceString" + ] + }, + "subexpand": { + "description": "Used to expand a sublist.\nE.g. a target `rendering` is:\n\ne.g.\n{\n rewrite: [\"{{x}}\", \"{{y}}\"],\n into:[\n [\"{{x}}\": \"some X\"],\n [\"{{y}}\", [\"option 1\", \"option 2\"]]\n ],\n renderings:[\n {\n \"question\":\"Is {{x}}\",\n \"mappings\": [\"if={{y}}\",then: \"...\"]\n }\n ]\n subExpand: {\n // The list with the key\n \"mappings\":\n // will be taken and multiplied by all possible values of\n \"{{y}}\"\n // Note that this implies that `into.[*].[{{y}}]` should be a list of items\n }\n}\n\nExpansion will result in:\n{\n question: \"Is some X\",\n mappings: [{\"if=option 1\", then: \"...\"}, {\"if=option 2\", then: \"...\"}]\n}", + "$ref": "#/definitions/Record" + }, + "renderings": { + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/QuestionableTagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "builtin": { + "type": "string" + }, + "override": { + "$ref": "#/definitions/Partial" + } + }, + "required": [ + "builtin", + "override" + ] + }, + { + "type": "string" + } + ] + } + }, + { + "type": "array", + "items": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/QuestionableTagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "builtin": { + "type": "string" + }, + "override": { + "$ref": "#/definitions/Partial" + } + }, + "required": [ + "builtin", + "override" + ] + }, + { + "type": "string" + } + ] + } + } + } + ] + } + }, + "required": [ + "renderings", + "rewrite" + ] + }, + "default_1": { + "type": "object", + "properties": { + "id": { + "description": "An id/name for this filter, used to set the URL parameters", + "type": "string" + }, + "options": { + "description": "The options for a filter\nIf there are multiple options these will be a list of radio buttons\nIf there is only one option this will be a checkbox\nFiltering is done based on the given osmTags that are compared to the objects in that layer.\n\nAn example which searches by name:\n\n```\n{\n \"id\": \"shop-name\",\n \"options\": [\n {\n \"fields\": [\n {\n \"name\": \"search\",\n \"type\": \"string\"\n }\n ],\n \"osmTags\": \"name~i~.*{search}.*\",\n \"question\": {\n \"en\": \"Only show shops with name {search}\",\n }\n }\n ]\n }\n ```", + "type": "array", + "items": { + "$ref": "#/definitions/FilterConfigOptionJson" + } + }, + "#": { + "description": "Used for comments or to disable a check\n\n\"ignore-possible-duplicate\": disables a check in `DetectDuplicateFilters` which complains that a filter can be replaced by a filter from the `filters`-library-layer", + "type": "string" + } + }, + "required": [ + "id", + "options" + ] + }, + "DeleteConfigJson": { + "type": "object", + "properties": { + "neededChangesets": { + "description": "*\nBy default, the contributor needs 20 previous changesets to delete points edited by others.\nFor some small features (e.g. bicycle racks) this is too much and this requirement can be lowered or dropped, which can be done here.\n\ntype: nat\nquestion: How many changesets must a contributor have before being allowed to delete a point?", + "type": "number" + }, + "extraDeleteReasons": { + "description": "*\nBy default, three reasons to delete a point are shown:\n\n- The point does not exist anymore\n- The point was a testing point\n- THe point could not be found\n\nHowever, for some layers, there might be different or more specific reasons for deletion which can be user friendly to set, e.g.:\n\n- the shop has closed\n- the climbing route has been closed of for nature conservation reasons\n- ...\n\nThese reasons can be stated here and will be shown in the list of options the user can choose from", + "type": "array", + "items": { + "type": "object", + "properties": { + "explanation": { + "description": "The text that will be shown to the user as option for why this point does not exist anymore.\nNote that the most common reasons (test point, does not exist anymore, cannot be found) are already offered by default\n\nquestion: For what extra reason might this feature be removed in real-life?" + }, + "changesetMessage": { + "description": "The text that will be uploaded into the changeset or will be used in the fixme in case of a soft deletion\nShould be a few words, in english\n\nquestion: What should be added to the changeset as delete reason?", + "type": "string" + } + }, + "required": [ + "changesetMessage", + "explanation" + ] + } + }, + "nonDeleteMappings": { + "description": "In some cases, a (starting) contributor might wish to delete a feature even though deletion is not appropriate.\n(The most relevant case are small paths running over private property. These should be marked as 'private' instead of deleted, as the community might trace the path again from aerial imagery, gettting us back to the original situation).\n\nBy adding a 'nonDeleteMapping', an option can be added into the list which will retag the feature.\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!", + "type": "array", + "items": { + "type": "object", + "properties": { + "if": { + "$ref": "#/definitions/TagConfigJson", + "description": "The tags that will be given to the object.\nThis must remove tags so that the 'source/osmTags' won't match anymore\n\nquestion: What tags should be applied to the object?" + }, + "then": { + "description": "The human explanation for the options\n\nquestion: What text should be shown to the contributor for this reason?", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "if", + "then" + ] + } + }, + "softDeletionTags": { + "description": "In some cases, the contributor is not allowed to delete the current feature (e.g. because it isn't a point, the point is referenced by a relation or the user isn't experienced enough).\nTo still offer the user a 'delete'-option, the feature is retagged with these tags. This is a soft deletion, as the point isn't actually removed from OSM but rather marked as 'disused'\nIt is important that the feature will be retagged in such a way that it won't be picked up by the layer anymore!\n\nExample (note that \"amenity=\" erases the 'amenity'-key alltogether):\n\n```\n{\n \"and\": [\"disussed:amenity=public_bookcase\", \"amenity=\"]\n}\n```\n\nor (notice the use of the ':='-tag to copy the old value of 'shop=*' into 'disused:shop='):\n\n```\n{\n \"and\": [\"disused:shop:={shop}\", \"shop=\"]\n}\n```\n\nquestion: If a hard delete is not possible, what tags should be applied to mark this feature as deleted?\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "omitDefaultDeleteReasons": { + "description": "Set this flag if the default delete reasons should be omitted from the dialog.\nThis requires at least one extraDeleteReason or nonDeleteMapping\n\nquestion: Should the default delete reasons be hidden?\niftrue: Hide the default delete reasons\niffalse: Show the default delete reasons\nifunset: Show the default delete reasons (default behaviour)", + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "boolean" + } + ] + } + } + }, + "default_3": { + "type": "object", + "properties": { + "enableImproveAccuracy": { + "description": "question: Should moving this type of point to improve the accuracy be allowed?\niftrue: This point can be moved to improve the accuracy\nifunset: (default) This point can be moved to improve the accuracy\niffalse: This point cannot be moved to improve the accuracy", + "type": "boolean" + }, + "enableRelocation": { + "description": "question: Should moving this type of point due to a relocation be allowed?\n\nThis will erase the attributes `addr:street`, `addr:housenumber`, `addr:city` and `addr:postcode`\n\niftrue: This type of point can be moved due to a relocation (and will remove address information when this is done)\nifunset: (default) This type of point can be moved due to a relocation (and will remove address information when this is done)\niffalse: This type of point cannot be moved due to a relocation", + "type": "boolean" + } + } + }, + "default_2": { + "description": "In some cases, a value is represented in a certain unit (such as meters for heigt/distance/..., km/h for speed, ...)\n\nSometimes, multiple denominations are possible (e.g. km/h vs mile/h; megawatt vs kilowatt vs gigawatt for power generators, ...)\n\nThis brings in some troubles, as there are multiple ways to write it (no denomitation, 'm' vs 'meter' 'metre', ...)\n\nNot only do we want to write consistent data to OSM, we also want to present this consistently to the user.\nThis is handled by defining units.\n\n# Rendering\n\nTo render a value with long (human) denomination, use {canonical(key)}\n\n# Usage\n\nFirst of all, you define which keys have units applied, for example:\n\n```\nunits: [\n appliesTo: [\"maxspeed\", \"maxspeed:hgv\", \"maxspeed:bus\"]\n applicableUnits: [\n ...\n ]\n]\n```\n\nApplicableUnits defines which is the canonical extension, how it is presented to the user, ...:\n\n```\napplicableUnits: [\n{\n canonicalDenomination: \"km/h\",\n alternativeDenomination: [\"km/u\", \"kmh\", \"kph\"]\n default: true,\n human: {\n en: \"kilometer/hour\",\n nl: \"kilometer/uur\"\n },\n humanShort: {\n en: \"km/h\",\n nl: \"km/u\"\n }\n},\n{\n canoncialDenomination: \"mph\",\n ... similar for miles an hour ...\n}\n]\n```\n\n\nIf this is defined, then every key which the denominations apply to (`maxspeed`, `maxspeed:hgv` and `maxspeed:bus`) will be rewritten at the metatagging stage:\nevery value will be parsed and the canonical extension will be added add presented to the other parts of the code.\n\nAlso, if a freeform text field is used, an extra dropdown with applicable denominations will be given", + "type": "object", + "properties": { + "quantity": { + "description": "What is quantified? E.g. 'speed', 'length' (including width, diameter, ...), 'electric tension', 'electric current', 'duration'", + "type": "string" + }, + "appliesToKey": { + "description": "Every key from this list will be normalized.\n\nTo render the value properly (with a human readable denomination), use `{canonical()}`", + "type": "array", + "items": { + "type": "string" + } + }, + "eraseInvalidValues": { + "description": "If set, invalid values will be erased in the MC application (but not in OSM of course!)\nBe careful with setting this", + "type": "boolean" + }, + "applicableUnits": { + "description": "The possible denominations for this unit.\nFor length, denominations could be \"meter\", \"kilometer\", \"miles\", \"foot\"", + "type": "array", + "items": { + "$ref": "#/definitions/DenominationConfigJson" + } + }, + "defaultInput": { + "description": "In some cases, the default denomination is not the most user friendly to input.\nE.g., when measuring kerb heights, it is illogical to ask contributors to input an amount in meters.\n\nWhen a default input method should be used, this can be specified by setting the canonical denomination here, e.g.\n`defaultInput: \"cm\"`. This must be a denomination which appears in the applicableUnits", + "type": "string" + } + }, + "required": [ + "applicableUnits" + ] + }, + "Record": { + "type": "object" + }, + "default": { + "type": "object", + "properties": { + "icon": { + "description": "question: What icon should be shown in the link button?\nifunset: do not show an icon\ntype: icon", + "type": "string" + }, + "text": { + "description": "question: What text should be shown in the link icon?\n\nNote that {lat},{lon},{zoom}, {language} and {theme} will be replaced\n\nifunset: do not show a text", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "href": { + "description": "question: if clicked, what webpage should open?\nNote that {lat},{lon},{zoom}, {language} and {theme} will be replaced\n\ntype: url", + "type": "string" + }, + "newTab": { + "description": "question: Should the link open in a new tab?\niftrue: Open in a new tab\niffalse: do not open in a new tab\nifunset: do not open in a new tab", + "type": "boolean" + }, + "requirements": { + "description": "question: When should the extra button be shown?\nsuggestions: return [{if: \"value=iframe\", then: \"When shown in an iframe\"}, {if: \"value=no-iframe\", then: \"When shown as stand-alone webpage\"}, {if: \"value=welcome-message\", then: \"When the welcome messages are enabled\"}, {if: \"value=iframe\", then: \"When the welcome messages are disabled\"}]", + "type": "array", + "items": { + "enum": [ + "iframe", + "no-iframe", + "no-welcome-message", + "welcome-message" + ], + "type": "string" + } + } + }, + "required": [ + "href" + ] + }, + "LayerConfigJson": { + "description": "Configuration for a single layer", + "type": "object", + "properties": { + "id": { + "description": "question: What is the identifier of this layer?\n\nThis should be a simple, lowercase, human readable string that is used to identify the layer.\n A good ID is:\n - a noun\n - written in singular\n - describes the object\n - in english\n - only has lowercase letters, numbers or underscores. Do not use a space or a dash\n\ntype: id\ngroup: Basic", + "type": "string" + }, + "name": { + "description": "Used in the layer control panel to toggle a layer on and of.\n\nifunset: This will hide the layer in the layer control, making it not filterable and not toggleable\n\ngroup: Basic\nquestion: What is the name of this layer?", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "description": { + "description": "question: How would you describe the features that are shown on this layer?\n\nA description for the features shown in this layer.\nThis often resembles the introduction of the wiki.osm.org-page for this feature.\n\ngroup: Basic", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "description": "question: What are some other terms used to describe these objects?\n\nThis is used in the search functionality", + "$ref": "#/definitions/Record" + }, + "source": { + "description": "Question: Where should the data be fetched from?\ntitle: Data Source\n\nThis determines where the data for the layer is fetched: from OSM or from an external geojson dataset.\n\nIf no 'geojson' is defined, data will be fetched from overpass and the OSM-API.\n\nEvery source _must_ define which tags _must_ be present in order to be picked up.\n\nNote: a source must always be defined. 'special' is only allowed if this is a builtin-layer\n\ntypes: Load data with specific tags from OpenStreetMap ; Load data from an external geojson source ;\ntypesdefault: 0\nifunset: Determine the tags automatically based on the presets\ngroup: Basic", + "anyOf": [ + { + "type": "object", + "properties": { + "osmTags": { + "$ref": "#/definitions/TagConfigJson", + "description": "question: Which tags must be present on the feature to show it in this layer?\nEvery source must set which tags have to be present in order to load the given layer." + } + }, + "required": [ + "osmTags" + ] + }, + { + "type": "object", + "properties": { + "geoJson": { + "description": "The actual source of the data to load, if loaded via geojson.\n\n# A single geojson-file\nsource: {geoJson: \"https://my.source.net/some-geo-data.geojson\"}\n fetches a geojson from a third party source\n\n# A tiled geojson source\nsource: {geoJson: \"https://my.source.net/some-tile-geojson-{layer}-{z}-{x}-{y}.geojson\", geoJsonZoomLevel: 14}\n to use a tiled geojson source. The web server must offer multiple geojsons. {z}, {x} and {y} are substituted by the location; {layer} is substituted with the id of the loaded layer\n\nSome API's use a BBOX instead of a tile, this can be used by specifying {y_min}, {y_max}, {x_min} and {x_max}\n\nquestion: What is the URL of the geojson?\ntype: url", + "type": "string" + }, + "geoJsonZoomLevel": { + "description": "To load a tiled geojson layer, set the zoomlevel of the tiles\n\nquestion: If using a tiled geojson, what is the zoomlevel of the tiles?\nifunset: This is not a tiled geojson", + "type": "number" + }, + "mercatorCrs": { + "description": "Some API's use a mercator-projection (EPSG:900913) instead of WGS84. Set the flag `mercatorCrs: true` in the source for this\n\nquestion: Does this geojson use EPSG:900913 instead of WGS84 as projection?\niftrue: This geojson uses EPSG:900913 instead of WGS84\nifunset: This geojson uses WGS84 just like most geojson (default)", + "type": "boolean" + }, + "idKey": { + "description": "Some API's have an id-field, but give it a different name.\nSetting this key will rename this field into 'id'\n\nifunset: An id with key `id` will be assigned automatically if no attribute `id` is set\ninline: This geojson uses {value} as attribute to set the id\nquestion: What is the name of the attribute containing the ID of the object?", + "type": "string" + } + }, + "required": [ + "geoJson" + ] + }, + { + "enum": [ + "special", + "special:library" + ], + "type": "string" + } + ] + }, + "calculatedTags": { + "description": "A list of extra tags to calculate, specified as \"keyToAssignTo=javascript-expression\".\nThere are a few extra functions available. Refer to Docs/CalculatedTags.md for more information\nThe functions will be run in order, e.g.\n[\n \"_max_overlap_m2=Math.max(...feat.overlapsWith(\"someOtherLayer\").map(o => o.overlap))\n \"_max_overlap_ratio=Number(feat._max_overlap_m2)/feat.area\n]\n\nThe specified tags are evaluated lazily. E.g. if a calculated tag is only used in the popup (e.g. the number of nearby features),\nthe expensive calculation will only be performed then for that feature. This avoids clogging up the contributors PC when all features are loaded.\n\nIf a tag has to be evaluated strictly, use ':=' instead:\n\n[\n\"_some_key:=some_javascript_expression\"\n]\n\nSee the full documentation on [https://github.com/pietervdvn/MapComplete/blob/master/Docs/CalculatedTags.md]\n\ngroup: expert\nquestion: What extra attributes should be calculated with javascript?", + "type": "array", + "items": { + "type": "string" + } + }, + "isShown": { + "description": "If set, only features matching this extra tag will be shown.\nThis is useful to hide certain features from view based on a calculated tag or if the features are provided by a different layer.\n\nquestion: What other tags should features match for being shown?\ngroup: advanced\nifunset: all features which match the 'source'-specification are shown.", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "isCounted": { + "description": "question: should this layer be included in the summary counts?\n\nThe layer server can give summary counts for a tile.\nThis should however be disabled for some layers, e.g. because there are too many features (walls_and_buildings) or because the count is irrelevant.\n\nifunset: Do count\niffalse: Do not include the counts\niftrue: Do include the count", + "type": "boolean" + }, + "minzoom": { + "description": "The minimum needed zoomlevel required to start loading and displaying the data.\nThis can be used to only show common features (e.g. a bicycle parking) only when the map is zoomed in very much (17).\nThis prevents cluttering the map with thousands of parkings if one is looking to an entire city.\n\nDefault: 0\ngroup: Basic\ntype: nat\nquestion: At what zoom level should features of the layer be shown?\nifunset: Always load this layer, even if the entire world is in view.", + "type": "number" + }, + "shownByDefault": { + "description": "Indicates if this layer is shown by default;\ncan be used to hide a layer from start, or to load the layer but only to show it when appropriate (e.g. for advanced users)\n\nquestion: Should this layer be enabled when opening the map for the first time?\niftrue: the layer is enabled when opening the map\niffalse: the layer is hidden until the contributor enables it. (If the filter-popup is disabled, this might never get enabled nor loaded)\ndefault: true\ngroup: advanced", + "type": "boolean" + }, + "minzoomVisible": { + "description": "The zoom level at which point the data is hidden again\nDefault: 100 (thus: always visible\n\ngroup: expert", + "type": "number" + }, + "title": { + "description": "question: Edit the popup title\nThe title shown in a popup for elements of this layer.\n\ngroup: title\ntypes: use a fixed translation ; Use a dynamic tagRendering ; hidden\ntypesdefault: 1\ntype: translation\ninline: {translated{value}}", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "popupInFloatover": { + "description": "Question: Should the information for this layer be shown in the sidebar or in a splash screen?\n\nIf set, open the selectedElementView in a floatOver instead of on the right.\n\niftrue: show the infobox in the splashscreen floating over the entire UI; hide the title bar\niffalse: show the infobox in a sidebar on the right\nsuggestions: return [{if: \"value=title\", then: \"Show in a floatover and show the title bar\"}]\ngroup: advanced\ndefault: sidebar", + "type": [ + "string", + "boolean" + ] + }, + "titleIcons": { + "description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons (which are added automatically)\n\nUse `auto:` to automatically create an icon based on a tagRendering which has icons\n\nType: icon[]\ngroup: infobox", + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "allOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + } + ] + }, + { + "type": "string" + } + ] + } + }, + { + "type": "array", + "items": [ + { + "type": "string", + "enum": [ + "defaults" + ] + } + ], + "minItems": 1, + "maxItems": 1 + } + ] + }, + "pointRendering": { + "description": "Creates points to render on the map.\nThis can render points for point-objects, lineobjects or areaobjects; use 'location' to indicate where it should be rendered.\n\nNote that all attributes - including [the calculated tags](https://github.com/pietervdvn/MapComplete/blob/develop/Docs/CalculatedTags.md) can be used to create the markers and lines\n\ngroup: pointrendering", + "type": "array", + "items": { + "$ref": "#/definitions/default_4" + } + }, + "lineRendering": { + "description": "Creates lines and areas to render on the map\ngroup: linerendering", + "type": "array", + "items": { + "$ref": "#/definitions/default_5" + } + }, + "passAllFeatures": { + "description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert", + "type": "boolean" + }, + "doNotDownload": { + "description": "If set, this layer will not query overpass; but it'll still match the tags above which are by chance returned by other layers.\nWorks well together with 'passAllFeatures', to add decoration\nThe opposite of `forceLoad`\n\niftrue: Do not attempt to query the data for this layer from overpass/the OSM API\niffalse: download the data as usual\ngroup: expert\nquestion: Should this layer be downloaded or is the data provided by a different layer (which has 'passAllFeatures'-set)?\ndefault: false", + "type": "boolean" + }, + "forceLoad": { + "description": "Advanced option - might be set by the theme compiler\n\nIf true, this data will _always_ be loaded, even if the theme is disabled by a filter or hidden.\nThe opposite of `doNotDownload`\n\nquestion: Should this layer be forcibly loaded?\nifftrue: always download this layer, even if it is disabled\niffalse: only download data for this layer when needed (default)\ndefault: false\ngroup: expert", + "type": "boolean" + }, + "presets": { + "description": "
\n
\nPresets for this layer.\n\nA preset consists of one or more attributes (tags), a title and optionally a description and optionally example images.\n\nWhen the contributor wishes to add a point to OpenStreetMap, they'll:\n\n1. Press the 'add new point'-button\n2. Choose a preset from the list of all presets\n3. Confirm the choice. In this step, the `description` (if set) and `exampleImages` (if given) will be shown\n4. Confirm the location\n5. A new point will be created with the attributes that were defined in the preset\n\nIf no presets are defined, the button which invites to add a new preset will not be shown.\n
\n
\n\ngroup: presets\ntitle: value.title", + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "description": "The title - shown on the 'add-new'-button.\n\nThis should include the article of the noun, e.g. 'a hydrant', 'a bicycle pump'.\nThis text will be inserted into `Add {category} here`, becoming `Add a hydrant here`.\n\nDo _not_ indicate 'new': 'add a new shop here' is incorrect, as the shop might have existed forever, it could just be unmapped!\n\nquestion: What is the word to describe this object?\ninline: Add {translated(value)::font-bold} here", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "tags": { + "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag\ntypeHelper: uploadableOnly", + "type": "array", + "items": { + "type": "string" + } + }, + "description": { + "description": "An extra explanation of what the feature is, if it is not immediately clear from the title alone.\n\nThe _first sentence_ of the description is shown on the button of the `add` menu.\nThe full description is shown in the confirmation dialog.\n\n(The first sentence is until the first '.'-character in the description)\n\nquestion: How would you describe this feature?\nifunset: No extra description is given. This can be used to further explain the preset", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "exampleImages": { + "description": "The URL of an example image which shows a real-life example of what such a feature might look like.\n\nType: image\nquestion: What is the URL of an image showing such a feature?", + "type": "array", + "items": { + "type": "string" + } + }, + "snapToLayer": { + "description": "question: Should the created point be snapped to a line layer?\n\nIf specified, these layers will be shown in the precise location picker and the new point will be snapped towards it.\nFor example, this can be used to snap against `walls_and_buildings` (e.g. to attach a defibrillator, an entrance, an artwork, ... to the wall)\nor to snap an obstacle (such as a bollard) to the `cycleways_and_roads`.\n\nsuggestions: return Array.from(layers.keys()).map(key => ({if: \"value=\"+key, then: key+\" - \"+layers.get(key).description}))", + "type": "array", + "items": { + "type": "string" + } + }, + "maxSnapDistance": { + "description": "question: What is the maximum distance in the location-input that a point can be moved to be snapped to a way?\n\ninline: a point is snapped if the location input is at most {value}m away from an object\n\nIf specified, a new point will only be snapped if it is within this range.\nIf further away, it'll be placed in the center of the location input\nDistance in meter\n\nifunset: Do not snap to a layer", + "type": "number" + } + }, + "required": [ + "tags", + "title" + ] + } + }, + "tagRenderings": { + "description": "question: Edit this way this attributed is displayed or queried\n\nA tag rendering is a block that either shows the known value or asks a question.\n\nRefer to the class `TagRenderingConfigJson` to see the possibilities.\n\nNote that we can also use a string here - where the string refers to a tag rendering defined in `assets/questions/questions.json`,\nwhere a few very general questions are defined e.g. website, phone number, ...\nFurthermore, _all_ the questions of another layer can be reused with `otherlayer.*`\nIf you need only a single of the tagRenderings, use `otherlayer.tagrenderingId`\nIf one or more questions have a 'group' or 'label' set, select all the entries with the corresponding group or label with `otherlayer.*group`\nRemark: if a tagRendering is 'lent' from another layer, the 'source'-tags are copied and added as condition.\nIf they are not wanted, remove them with an override\n\nA special value is 'questions', which indicates the location of the questions box. If not specified, it'll be appended to the bottom of the featureInfobox.\n\nAt last, one can define a group of renderings where parts of all strings will be replaced by multiple other strings.\nThis is mainly create questions for a 'left' and a 'right' side of the road.\nThese will be grouped and questions will be asked together\n\ntype: tagrendering[]\ngroup: tagrenderings", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/QuestionableTagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "builtin": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "override": { + "$ref": "#/definitions/Partial" + } + }, + "required": [ + "builtin", + "override" + ] + }, + { + "allOf": [ + { + "$ref": "#/definitions/default<(string|QuestionableTagRenderingConfigJson|{builtin:string;override:Partial;})[]>" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ] + } + ] + }, + { + "type": "string" + } + ] + } + }, + "filter": { + "description": "All the extra questions for filtering.\nIf a string is given, mapComplete will search in\n1. The tagrenderings for a match on ID and use the mappings as options\n2. search 'filters.json' for the appropriate filter or\n3. will try to parse it as `layername.filterid` and us that one.\n\nNote: adding \"#filter\":\"no-auto\" will disable the filters added by tagRenderings\n\ngroup: filters", + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/default_1" + }, + { + "type": "string" + } + ] + } + }, + { + "type": "object", + "properties": { + "sameAs": { + "type": "string" + } + }, + "required": [ + "sameAs" + ] + } + ] + }, + "#filter": { + "description": "Set this to disable the feature that tagRenderings can introduce filters", + "enum": [ + "no-auto" + ], + "type": "string" + }, + "deletion": { + "description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: Use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion\nifunset: Do not allow deletion", + "anyOf": [ + { + "$ref": "#/definitions/DeleteConfigJson" + }, + { + "type": "boolean" + } + ] + }, + "allowMove": { + "description": "Indicates if a point can be moved and why.\n\nA feature can be moved by MapComplete if:\n\n- It is a point\n- The point is _not_ part of a way or a a relation.\n\ntypes: use an advanced move configuration ; boolean\ngroup: editing\nquestion: Is deleting a point allowed?\niftrue: Allow contributors to move a point (for accuracy or a relocation)\niffalse: Don't allow contributors to move points\nifunset: Don't allow contributors to move points (default)", + "anyOf": [ + { + "$ref": "#/definitions/default_3" + }, + { + "type": "boolean" + } + ] + }, + "allowSplit": { + "description": "If set, a 'split this way' button is shown on objects rendered as LineStrings, e.g. highways.\n\nIf the way is part of a relation, MapComplete will attempt to update this relation as well\nquestion: Should the contributor be able to split ways using this layer?\niftrue: enable the 'split-roads'-component\niffalse: don't enable the split-roads component\nifunset: don't enable the split-roads component\ngroup: editing", + "type": "boolean" + }, + "units": { + "description": "Either a list with [{\"key\": \"unitname\", \"key2\": {\"quantity\": \"unitname\", \"denominations\": [\"denom\", \"denom\"]}}]\n\nUse `\"inverted\": true` if the amount should be _divided_ by the denomination, e.g. for charge over time (`€5/day`)", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/default_2" + }, + { + "$ref": "#/definitions/Record" + } + ] + } + }, + "syncSelection": { + "description": "If set, synchronizes whether this layer is enabled.\n\ngroup: advanced\nquestion: Should enabling/disabling the layer be saved (locally or in the cloud)?\nsuggestions: return [{if: \"value=no\", then: \"Don't save, always revert to the default\"}, {if: \"value=local\", then: \"Save selection in local storage\"}, {if: \"value=theme-only\", then: \"Save the state in the OSM-usersettings, but apply on this theme only (default)\"}, {if: \"value=global\", then: \"Save in OSM-usersettings and toggle on _all_ themes using this layer\"}]", + "enum": [ + "global", + "local", + "no", + "theme-only" + ], + "type": "string" + }, + "#": { + "description": "Used for comments and/or to disable some checks\n\nno-question-hint-check: disables a check in MiscTagRenderingChecks which complains about 'div', 'span' or 'class=subtle'-HTML elements in the tagRendering\n\ngroup: hidden", + "type": "string" + }, + "fullNodeDatabase": { + "description": "_Set automatically by MapComplete, please ignore_\n\ngroup: hidden", + "type": "boolean" + }, + "enableMorePrivacy": { + "description": "question: Should a theme using this layer leak some location info when making changes?\n\nWhen a changeset is made, a 'distance to object'-class is written to the changeset.\nFor some particular themes and layers, this might leak too much information, and we want to obfuscate this\n\nifunset: Write 'change_within_x_m' as usual and if GPS is enabled\niftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey", + "type": "boolean" + }, + "snapName": { + "description": "question: When a feature is snapped to this name, how should this item be called?\n\nIn the move wizard, the option `snap object onto {snapName}` is shown\n\ngroup: hidden", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "id", + "pointRendering" + ] + }, + "Partial": { + "type": "object", + "properties": { + "id": { + "description": "question: What is the identifier of this layer?\n\nThis should be a simple, lowercase, human readable string that is used to identify the layer.\n A good ID is:\n - a noun\n - written in singular\n - describes the object\n - in english\n - only has lowercase letters, numbers or underscores. Do not use a space or a dash\n\ntype: id\ngroup: Basic", + "type": "string" + }, + "name": { + "description": "Used in the layer control panel to toggle a layer on and of.\n\nifunset: This will hide the layer in the layer control, making it not filterable and not toggleable\n\ngroup: Basic\nquestion: What is the name of this layer?", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "description": { + "description": "question: How would you describe the features that are shown on this layer?\n\nA description for the features shown in this layer.\nThis often resembles the introduction of the wiki.osm.org-page for this feature.\n\ngroup: Basic", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "description": "question: What are some other terms used to describe these objects?\n\nThis is used in the search functionality", + "$ref": "#/definitions/Record" + }, + "source": { + "description": "Question: Where should the data be fetched from?\ntitle: Data Source\n\nThis determines where the data for the layer is fetched: from OSM or from an external geojson dataset.\n\nIf no 'geojson' is defined, data will be fetched from overpass and the OSM-API.\n\nEvery source _must_ define which tags _must_ be present in order to be picked up.\n\nNote: a source must always be defined. 'special' is only allowed if this is a builtin-layer\n\ntypes: Load data with specific tags from OpenStreetMap ; Load data from an external geojson source ;\ntypesdefault: 0\nifunset: Determine the tags automatically based on the presets\ngroup: Basic", + "anyOf": [ + { + "type": "object", + "properties": { + "osmTags": { + "$ref": "#/definitions/TagConfigJson", + "description": "question: Which tags must be present on the feature to show it in this layer?\nEvery source must set which tags have to be present in order to load the given layer." + } + }, + "required": [ + "osmTags" + ] + }, + { + "type": "object", + "properties": { + "geoJson": { + "description": "The actual source of the data to load, if loaded via geojson.\n\n# A single geojson-file\nsource: {geoJson: \"https://my.source.net/some-geo-data.geojson\"}\n fetches a geojson from a third party source\n\n# A tiled geojson source\nsource: {geoJson: \"https://my.source.net/some-tile-geojson-{layer}-{z}-{x}-{y}.geojson\", geoJsonZoomLevel: 14}\n to use a tiled geojson source. The web server must offer multiple geojsons. {z}, {x} and {y} are substituted by the location; {layer} is substituted with the id of the loaded layer\n\nSome API's use a BBOX instead of a tile, this can be used by specifying {y_min}, {y_max}, {x_min} and {x_max}\n\nquestion: What is the URL of the geojson?\ntype: url", + "type": "string" + }, + "geoJsonZoomLevel": { + "description": "To load a tiled geojson layer, set the zoomlevel of the tiles\n\nquestion: If using a tiled geojson, what is the zoomlevel of the tiles?\nifunset: This is not a tiled geojson", + "type": "number" + }, + "mercatorCrs": { + "description": "Some API's use a mercator-projection (EPSG:900913) instead of WGS84. Set the flag `mercatorCrs: true` in the source for this\n\nquestion: Does this geojson use EPSG:900913 instead of WGS84 as projection?\niftrue: This geojson uses EPSG:900913 instead of WGS84\nifunset: This geojson uses WGS84 just like most geojson (default)", + "type": "boolean" + }, + "idKey": { + "description": "Some API's have an id-field, but give it a different name.\nSetting this key will rename this field into 'id'\n\nifunset: An id with key `id` will be assigned automatically if no attribute `id` is set\ninline: This geojson uses {value} as attribute to set the id\nquestion: What is the name of the attribute containing the ID of the object?", + "type": "string" + } + }, + "required": [ + "geoJson" + ] + }, + { + "enum": [ + "special", + "special:library" + ], + "type": "string" + } + ] + }, + "calculatedTags": { + "description": "A list of extra tags to calculate, specified as \"keyToAssignTo=javascript-expression\".\nThere are a few extra functions available. Refer to Docs/CalculatedTags.md for more information\nThe functions will be run in order, e.g.\n[\n \"_max_overlap_m2=Math.max(...feat.overlapsWith(\"someOtherLayer\").map(o => o.overlap))\n \"_max_overlap_ratio=Number(feat._max_overlap_m2)/feat.area\n]\n\nThe specified tags are evaluated lazily. E.g. if a calculated tag is only used in the popup (e.g. the number of nearby features),\nthe expensive calculation will only be performed then for that feature. This avoids clogging up the contributors PC when all features are loaded.\n\nIf a tag has to be evaluated strictly, use ':=' instead:\n\n[\n\"_some_key:=some_javascript_expression\"\n]\n\nSee the full documentation on [https://github.com/pietervdvn/MapComplete/blob/master/Docs/CalculatedTags.md]\n\ngroup: expert\nquestion: What extra attributes should be calculated with javascript?", + "type": "array", + "items": { + "type": "string" + } + }, + "isShown": { + "description": "If set, only features matching this extra tag will be shown.\nThis is useful to hide certain features from view based on a calculated tag or if the features are provided by a different layer.\n\nquestion: What other tags should features match for being shown?\ngroup: advanced\nifunset: all features which match the 'source'-specification are shown.", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "isCounted": { + "description": "question: should this layer be included in the summary counts?\n\nThe layer server can give summary counts for a tile.\nThis should however be disabled for some layers, e.g. because there are too many features (walls_and_buildings) or because the count is irrelevant.\n\nifunset: Do count\niffalse: Do not include the counts\niftrue: Do include the count", + "type": "boolean" + }, + "minzoom": { + "description": "The minimum needed zoomlevel required to start loading and displaying the data.\nThis can be used to only show common features (e.g. a bicycle parking) only when the map is zoomed in very much (17).\nThis prevents cluttering the map with thousands of parkings if one is looking to an entire city.\n\nDefault: 0\ngroup: Basic\ntype: nat\nquestion: At what zoom level should features of the layer be shown?\nifunset: Always load this layer, even if the entire world is in view.", + "type": "number" + }, + "shownByDefault": { + "description": "Indicates if this layer is shown by default;\ncan be used to hide a layer from start, or to load the layer but only to show it when appropriate (e.g. for advanced users)\n\nquestion: Should this layer be enabled when opening the map for the first time?\niftrue: the layer is enabled when opening the map\niffalse: the layer is hidden until the contributor enables it. (If the filter-popup is disabled, this might never get enabled nor loaded)\ndefault: true\ngroup: advanced", + "type": "boolean" + }, + "minzoomVisible": { + "description": "The zoom level at which point the data is hidden again\nDefault: 100 (thus: always visible\n\ngroup: expert", + "type": "number" + }, + "title": { + "description": "question: Edit the popup title\nThe title shown in a popup for elements of this layer.\n\ngroup: title\ntypes: use a fixed translation ; Use a dynamic tagRendering ; hidden\ntypesdefault: 1\ntype: translation\ninline: {translated{value}}", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "string" + } + ] + }, + "popupInFloatover": { + "description": "Question: Should the information for this layer be shown in the sidebar or in a splash screen?\n\nIf set, open the selectedElementView in a floatOver instead of on the right.\n\niftrue: show the infobox in the splashscreen floating over the entire UI; hide the title bar\niffalse: show the infobox in a sidebar on the right\nsuggestions: return [{if: \"value=title\", then: \"Show in a floatover and show the title bar\"}]\ngroup: advanced\ndefault: sidebar", + "type": [ + "string", + "boolean" + ] + }, + "titleIcons": { + "description": "Small icons shown next to the title.\nIf not specified, the OsmLink and wikipedia links will be used by default.\nUse an empty array to hide them.\nNote that \"defaults\" will insert all the default titleIcons (which are added automatically)\n\nUse `auto:` to automatically create an icon based on a tagRendering which has icons\n\nType: icon[]\ngroup: infobox", + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "allOf": [ + { + "$ref": "#/definitions/TagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + } + ] + }, + { + "type": "string" + } + ] + } + }, + { + "type": "array", + "items": [ + { + "type": "string", + "enum": [ + "defaults" + ] + } + ], + "minItems": 1, + "maxItems": 1 + } + ] + }, + "pointRendering": { + "description": "Creates points to render on the map.\nThis can render points for point-objects, lineobjects or areaobjects; use 'location' to indicate where it should be rendered.\n\nNote that all attributes - including [the calculated tags](https://github.com/pietervdvn/MapComplete/blob/develop/Docs/CalculatedTags.md) can be used to create the markers and lines\n\ngroup: pointrendering", + "type": "array", + "items": { + "$ref": "#/definitions/default_4" + } + }, + "lineRendering": { + "description": "Creates lines and areas to render on the map\ngroup: linerendering", + "type": "array", + "items": { + "$ref": "#/definitions/default_5" + } + }, + "passAllFeatures": { + "description": "If set, this layer will pass all the features it receives onto the next layer.\nThis is ideal for decoration, e.g. directions on cameras\niftrue: Make the features from this layer also available to the other layer; might result in this object being rendered by multiple layers\niffalse: normal behaviour: don't pass features allong\nquestion: should this layer pass features to the next layers?\ngroup: expert", + "type": "boolean" + }, + "doNotDownload": { + "description": "If set, this layer will not query overpass; but it'll still match the tags above which are by chance returned by other layers.\nWorks well together with 'passAllFeatures', to add decoration\nThe opposite of `forceLoad`\n\niftrue: Do not attempt to query the data for this layer from overpass/the OSM API\niffalse: download the data as usual\ngroup: expert\nquestion: Should this layer be downloaded or is the data provided by a different layer (which has 'passAllFeatures'-set)?\ndefault: false", + "type": "boolean" + }, + "forceLoad": { + "description": "Advanced option - might be set by the theme compiler\n\nIf true, this data will _always_ be loaded, even if the theme is disabled by a filter or hidden.\nThe opposite of `doNotDownload`\n\nquestion: Should this layer be forcibly loaded?\nifftrue: always download this layer, even if it is disabled\niffalse: only download data for this layer when needed (default)\ndefault: false\ngroup: expert", + "type": "boolean" + }, + "presets": { + "description": "
\n
\nPresets for this layer.\n\nA preset consists of one or more attributes (tags), a title and optionally a description and optionally example images.\n\nWhen the contributor wishes to add a point to OpenStreetMap, they'll:\n\n1. Press the 'add new point'-button\n2. Choose a preset from the list of all presets\n3. Confirm the choice. In this step, the `description` (if set) and `exampleImages` (if given) will be shown\n4. Confirm the location\n5. A new point will be created with the attributes that were defined in the preset\n\nIf no presets are defined, the button which invites to add a new preset will not be shown.\n
\n
\n\ngroup: presets\ntitle: value.title", + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "description": "The title - shown on the 'add-new'-button.\n\nThis should include the article of the noun, e.g. 'a hydrant', 'a bicycle pump'.\nThis text will be inserted into `Add {category} here`, becoming `Add a hydrant here`.\n\nDo _not_ indicate 'new': 'add a new shop here' is incorrect, as the shop might have existed forever, it could just be unmapped!\n\nquestion: What is the word to describe this object?\ninline: Add {translated(value)::font-bold} here", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "tags": { + "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag\ntypeHelper: uploadableOnly", + "type": "array", + "items": { + "type": "string" + } + }, + "description": { + "description": "An extra explanation of what the feature is, if it is not immediately clear from the title alone.\n\nThe _first sentence_ of the description is shown on the button of the `add` menu.\nThe full description is shown in the confirmation dialog.\n\n(The first sentence is until the first '.'-character in the description)\n\nquestion: How would you describe this feature?\nifunset: No extra description is given. This can be used to further explain the preset", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "exampleImages": { + "description": "The URL of an example image which shows a real-life example of what such a feature might look like.\n\nType: image\nquestion: What is the URL of an image showing such a feature?", + "type": "array", + "items": { + "type": "string" + } + }, + "snapToLayer": { + "description": "question: Should the created point be snapped to a line layer?\n\nIf specified, these layers will be shown in the precise location picker and the new point will be snapped towards it.\nFor example, this can be used to snap against `walls_and_buildings` (e.g. to attach a defibrillator, an entrance, an artwork, ... to the wall)\nor to snap an obstacle (such as a bollard) to the `cycleways_and_roads`.\n\nsuggestions: return Array.from(layers.keys()).map(key => ({if: \"value=\"+key, then: key+\" - \"+layers.get(key).description}))", + "type": "array", + "items": { + "type": "string" + } + }, + "maxSnapDistance": { + "description": "question: What is the maximum distance in the location-input that a point can be moved to be snapped to a way?\n\ninline: a point is snapped if the location input is at most {value}m away from an object\n\nIf specified, a new point will only be snapped if it is within this range.\nIf further away, it'll be placed in the center of the location input\nDistance in meter\n\nifunset: Do not snap to a layer", + "type": "number" + } + }, + "required": [ + "tags", + "title" + ] + } + }, + "tagRenderings": { + "description": "question: Edit this way this attributed is displayed or queried\n\nA tag rendering is a block that either shows the known value or asks a question.\n\nRefer to the class `TagRenderingConfigJson` to see the possibilities.\n\nNote that we can also use a string here - where the string refers to a tag rendering defined in `assets/questions/questions.json`,\nwhere a few very general questions are defined e.g. website, phone number, ...\nFurthermore, _all_ the questions of another layer can be reused with `otherlayer.*`\nIf you need only a single of the tagRenderings, use `otherlayer.tagrenderingId`\nIf one or more questions have a 'group' or 'label' set, select all the entries with the corresponding group or label with `otherlayer.*group`\nRemark: if a tagRendering is 'lent' from another layer, the 'source'-tags are copied and added as condition.\nIf they are not wanted, remove them with an override\n\nA special value is 'questions', which indicates the location of the questions box. If not specified, it'll be appended to the bottom of the featureInfobox.\n\nAt last, one can define a group of renderings where parts of all strings will be replaced by multiple other strings.\nThis is mainly create questions for a 'left' and a 'right' side of the road.\nThese will be grouped and questions will be asked together\n\ntype: tagrendering[]\ngroup: tagrenderings", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/QuestionableTagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "builtin": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "override": { + "$ref": "#/definitions/Partial" + } + }, + "required": [ + "builtin", + "override" + ] + }, + { + "allOf": [ + { + "$ref": "#/definitions/default<(string|QuestionableTagRenderingConfigJson|{builtin:string;override:Partial;})[]>" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ] + } + ] + }, + { + "type": "string" + } + ] + } + }, + "filter": { + "description": "All the extra questions for filtering.\nIf a string is given, mapComplete will search in\n1. The tagrenderings for a match on ID and use the mappings as options\n2. search 'filters.json' for the appropriate filter or\n3. will try to parse it as `layername.filterid` and us that one.\n\nNote: adding \"#filter\":\"no-auto\" will disable the filters added by tagRenderings\n\ngroup: filters", + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/default_1" + }, + { + "type": "string" + } + ] + } + }, + { + "type": "object", + "properties": { + "sameAs": { + "type": "string" + } + }, + "required": [ + "sameAs" + ] + } + ] + }, + "#filter": { + "description": "Set this to disable the feature that tagRenderings can introduce filters", + "enum": [ + "no-auto" + ], + "type": "string" + }, + "deletion": { + "description": "This block defines under what circumstances the delete dialog is shown for objects of this layer.\nIf set, a dialog is shown to the user to (soft) delete the point.\nThe dialog is built to be user friendly and to prevent mistakes.\nIf deletion is not possible, the dialog will hide itself and show the reason of non-deletability instead.\n\nTo configure, the following values are possible:\n\n- false: never ever show the delete button\n- true: show the default delete button\n- undefined: use the mapcomplete default to show deletion or not. Currently, this is the same as 'false' but this will change in the future\n- or: a hash with options (see below)\n\n### The delete dialog\n\n\n\n#### Hard deletion if enough experience\n\nA feature can only be deleted from OpenStreetMap by mapcomplete if:\n\n- It is a node\n- No ways or relations use the node\n- The logged-in user has enough experience OR the user is the only one to have edited the point previously\n- The logged-in user has no unread messages (or has a ton of experience)\n- The user did not select one of the 'non-delete-options' (see below)\n\nIn all other cases, a 'soft deletion' is used.\n\n#### Soft deletion\n\nA 'soft deletion' is when the point isn't deleted fromOSM but retagged so that it'll won't how up in the mapcomplete theme anymore.\nThis makes it look like it was deleted, without doing damage. A fixme will be added to the point.\n\nNote that a soft deletion is _only_ possible if these tags are provided by the theme creator, as they'll be different for every theme\n\n##### No-delete options\n\nIn some cases, the contributor might want to delete something for the wrong reason (e.g. someone who wants to have a path removed \"because the path is on their private property\").\nHowever, the path exists in reality and should thus be on OSM - otherwise the next contributor will pass by and notice \"hey, there is a path missing here! Let me redraw it in OSM!)\n\nThe correct approach is to retag the feature in such a way that it is semantically correct *and* that it doesn't show up on the theme anymore.\nA no-delete option is offered as 'reason to delete it', but secretly retags.\n\ngroup: editing\ntypes: Use an advanced delete configuration ; boolean\niftrue: Allow deletion\niffalse: Do not allow deletion\nifunset: Do not allow deletion", + "anyOf": [ + { + "$ref": "#/definitions/DeleteConfigJson" + }, + { + "type": "boolean" + } + ] + }, + "allowMove": { + "description": "Indicates if a point can be moved and why.\n\nA feature can be moved by MapComplete if:\n\n- It is a point\n- The point is _not_ part of a way or a a relation.\n\ntypes: use an advanced move configuration ; boolean\ngroup: editing\nquestion: Is deleting a point allowed?\niftrue: Allow contributors to move a point (for accuracy or a relocation)\niffalse: Don't allow contributors to move points\nifunset: Don't allow contributors to move points (default)", + "anyOf": [ + { + "$ref": "#/definitions/default_3" + }, + { + "type": "boolean" + } + ] + }, + "allowSplit": { + "description": "If set, a 'split this way' button is shown on objects rendered as LineStrings, e.g. highways.\n\nIf the way is part of a relation, MapComplete will attempt to update this relation as well\nquestion: Should the contributor be able to split ways using this layer?\niftrue: enable the 'split-roads'-component\niffalse: don't enable the split-roads component\nifunset: don't enable the split-roads component\ngroup: editing", + "type": "boolean" + }, + "units": { + "description": "Either a list with [{\"key\": \"unitname\", \"key2\": {\"quantity\": \"unitname\", \"denominations\": [\"denom\", \"denom\"]}}]\n\nUse `\"inverted\": true` if the amount should be _divided_ by the denomination, e.g. for charge over time (`€5/day`)", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/default_2" + }, + { + "$ref": "#/definitions/Record" + } + ] + } + }, + "syncSelection": { + "description": "If set, synchronizes whether this layer is enabled.\n\ngroup: advanced\nquestion: Should enabling/disabling the layer be saved (locally or in the cloud)?\nsuggestions: return [{if: \"value=no\", then: \"Don't save, always revert to the default\"}, {if: \"value=local\", then: \"Save selection in local storage\"}, {if: \"value=theme-only\", then: \"Save the state in the OSM-usersettings, but apply on this theme only (default)\"}, {if: \"value=global\", then: \"Save in OSM-usersettings and toggle on _all_ themes using this layer\"}]", + "enum": [ + "global", + "local", + "no", + "theme-only" + ], + "type": "string" + }, + "#": { + "description": "Used for comments and/or to disable some checks\n\nno-question-hint-check: disables a check in MiscTagRenderingChecks which complains about 'div', 'span' or 'class=subtle'-HTML elements in the tagRendering\n\ngroup: hidden", + "type": "string" + }, + "fullNodeDatabase": { + "description": "_Set automatically by MapComplete, please ignore_\n\ngroup: hidden", + "type": "boolean" + }, + "enableMorePrivacy": { + "description": "question: Should a theme using this layer leak some location info when making changes?\n\nWhen a changeset is made, a 'distance to object'-class is written to the changeset.\nFor some particular themes and layers, this might leak too much information, and we want to obfuscate this\n\nifunset: Write 'change_within_x_m' as usual and if GPS is enabled\niftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey", + "type": "boolean" + }, + "snapName": { + "description": "question: When a feature is snapped to this name, how should this item be called?\n\nIn the move wizard, the option `snap object onto {snapName}` is shown\n\ngroup: hidden", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + } + } + }, + "Partial": { + "type": "object" + }, + "RasterLayerProperties": { + "description": "This class has grown beyond the point of only containing Raster Layers", + "type": "object", + "properties": { + "name": { + "description": "The name of the imagery source", + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "isOverlay": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "url": { + "type": "string" + }, + "category": { + "type": "string" + }, + "type": { + "type": "string" + }, + "style": { + "type": "string" + }, + "attribution": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "text": { + "type": "string" + }, + "html": { + "type": "string" + }, + "required": { + "type": "boolean" + } + } + }, + "min_zoom": { + "type": "number" + }, + "max_zoom": { + "type": "number" + }, + "best": { + "type": "boolean" + } + }, + "required": [ + "id", + "name", + "url" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/Docs/Schemas/UnitConfigJson.schema.json b/Docs/Schemas/UnitConfigJson.schema.json index ee18e79ee..2a831cc22 100644 --- a/Docs/Schemas/UnitConfigJson.schema.json +++ b/Docs/Schemas/UnitConfigJson.schema.json @@ -92,6 +92,73 @@ ], "additionalProperties": false }, + "FilterConfigOptionJson": { + "type": "object", + "properties": { + "question": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "string" + } + ] + }, + "searchTerms": { + "$ref": "#/definitions/Record" + }, + "emoji": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "osmTags": { + "description": "The main representation of Tags.\nSee https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Tags_format.md for more documentation\n\ntype: tag", + "anyOf": [ + { + "$ref": "#/definitions/{and:TagConfigJson[];}" + }, + { + "$ref": "#/definitions/{or:TagConfigJson[];}" + }, + { + "type": "string" + } + ] + }, + "default": { + "type": "boolean" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "If name is `search`, use \"_first_comment~.*{search}.*\" as osmTags", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + } + }, + "required": [ + "question" + ], + "additionalProperties": false + }, + "Record": { + "type": "object", + "additionalProperties": false + }, "Record>": { "type": "object", "additionalProperties": false diff --git a/Docs/TagInfo/mapcomplete_bicycle_parkings.json b/Docs/TagInfo/mapcomplete_bicycle_parkings.json index b9465f249..9286fa935 100644 --- a/Docs/TagInfo/mapcomplete_bicycle_parkings.json +++ b/Docs/TagInfo/mapcomplete_bicycle_parkings.json @@ -89,6 +89,11 @@ "description": "Layer 'Bike parking' shows bicycle_parking=lockers with a fixed text, namely 'A locker - the bicycles are enclosed completely individually or with a few bicycles together. The locker is too small to fit a person standing..' and allows to pick this as a default answer (in the mapcomplete.org theme 'Bicycle parkings')", "value": "lockers" }, + { + "key": "bicycle_parking", + "description": "Layer 'Bike parking' shows bicycle_parking=lean_and_stick with a fixed text, namely 'A lean-to bracket with possibility to use a lock through eyelet. The seat tube can be held by the stand by an anchor' and allows to pick this as a default answer (in the mapcomplete.org theme 'Bicycle parkings')", + "value": "lean_and_stick" + }, { "key": "location", "description": "Layer 'Bike parking' shows location=underground with a fixed text, namely 'Underground parking' and allows to pick this as a default answer (in the mapcomplete.org theme 'Bicycle parkings')", @@ -144,7 +149,7 @@ }, { "key": "access", - "description": "Layer 'Bike parking' shows access=private with a fixed text, namely 'Access is limited to members of a school, company or organisation' and allows to pick this as a default answer (in the mapcomplete.org theme 'Bicycle parkings')", + "description": "Layer 'Bike parking' shows access=private with a fixed text, namely 'Private bicycle parking which is never available to the public, also not via a membership fee' and allows to pick this as a default answer (in the mapcomplete.org theme 'Bicycle parkings')", "value": "private" }, { diff --git a/Docs/TagInfo/mapcomplete_circular_economy.json b/Docs/TagInfo/mapcomplete_circular_economy.json index c8f7e8922..9a3283b9d 100644 --- a/Docs/TagInfo/mapcomplete_circular_economy.json +++ b/Docs/TagInfo/mapcomplete_circular_economy.json @@ -1716,12 +1716,12 @@ }, { "key": "service:bicycle:retail", - "description": "Layer 'Second hand shops' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Second hand shops' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "yes" }, { "key": "service:bicycle:retail", - "description": "Layer 'Second hand shops' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Second hand shops' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "no" }, { @@ -1741,32 +1741,32 @@ }, { "key": "service:bicycle:repair", - "description": "Layer 'Second hand shops' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Second hand shops' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "yes" }, { "key": "service:bicycle:repair", - "description": "Layer 'Second hand shops' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Second hand shops' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "no" }, { "key": "service:bicycle:repair", - "description": "Layer 'Second hand shops' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Second hand shops' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "only_sold" }, { "key": "service:bicycle:repair", - "description": "Layer 'Second hand shops' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Second hand shops' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "brand" }, { "key": "service:bicycle:rental", - "description": "Layer 'Second hand shops' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Second hand shops' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "yes" }, { "key": "service:bicycle:rental", - "description": "Layer 'Second hand shops' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Second hand shops' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Circular economy') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "no" }, { diff --git a/Docs/TagInfo/mapcomplete_climbing.json b/Docs/TagInfo/mapcomplete_climbing.json index 822d17676..42ad6ceee 100644 --- a/Docs/TagInfo/mapcomplete_climbing.json +++ b/Docs/TagInfo/mapcomplete_climbing.json @@ -1733,12 +1733,12 @@ }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "yes" }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "no" }, { @@ -1758,32 +1758,32 @@ }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "yes" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "no" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "only_sold" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "brand" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "yes" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "no" }, { diff --git a/Docs/TagInfo/mapcomplete_cyclofix.json b/Docs/TagInfo/mapcomplete_cyclofix.json index c5dd60fbd..d6213f1fa 100644 --- a/Docs/TagInfo/mapcomplete_cyclofix.json +++ b/Docs/TagInfo/mapcomplete_cyclofix.json @@ -323,6 +323,830 @@ "key": "name", "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'name' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists')" }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'shop' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bicycle_rental with a fixed text, namely 'Bicycle rental shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "bicycle_rental" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=agrarian with a fixed text, namely 'Farm Supply Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "agrarian" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=alcohol with a fixed text, namely 'Liquor Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "alcohol" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=anime with a fixed text, namely 'Anime / Manga Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "anime" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=antiques with a fixed text, namely 'Antique Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "antiques" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=appliance with a fixed text, namely 'Appliance Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "appliance" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=art with a fixed text, namely 'Art Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "art" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=baby_goods with a fixed text, namely 'Baby Goods Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "baby_goods" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bag with a fixed text, namely 'Bag/Luggage Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "bag" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bakery with a fixed text, namely 'Bakery' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "bakery" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bathroom_furnishing with a fixed text, namely 'Bathroom Furnishing Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "bathroom_furnishing" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=beauty with a fixed text, namely 'Beauty Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "beauty" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bed with a fixed text, namely 'Bedding/Mattress Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "bed" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=beverages with a fixed text, namely 'Beverage Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "beverages" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bicycle with a fixed text, namely 'Bicycle Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "bicycle" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=boat with a fixed text, namely 'Boat Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "boat" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bookmaker with a fixed text, namely 'Bookmaker' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "bookmaker" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=books with a fixed text, namely 'Bookstore' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "books" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=brewing_supplies with a fixed text, namely 'Brewing Supply Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "brewing_supplies" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=butcher with a fixed text, namely 'Butcher' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "butcher" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=camera with a fixed text, namely 'Camera Equipment Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "camera" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=candles with a fixed text, namely 'Candle Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "candles" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=cannabis with a fixed text, namely 'Cannabis Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "cannabis" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=car with a fixed text, namely 'Car Dealership' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "car" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=car_parts with a fixed text, namely 'Car Parts Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "car_parts" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=car_repair with a fixed text, namely 'Car Repair Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "car_repair" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=caravan with a fixed text, namely 'RV Dealership' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "caravan" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=carpet with a fixed text, namely 'Carpet Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "carpet" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=catalogue with a fixed text, namely 'Catalog Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "catalogue" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=charity with a fixed text, namely 'Charity Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "charity" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=cheese with a fixed text, namely 'Cheese Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "cheese" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=chemist with a fixed text, namely 'Drugstore' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "chemist" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=chocolate with a fixed text, namely 'Chocolate Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "chocolate" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=clothes with a fixed text, namely 'Clothing Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "clothes" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=coffee with a fixed text, namely 'Coffee Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "coffee" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=collector with a fixed text, namely 'Collectibles Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "collector" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=computer with a fixed text, namely 'Computer Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "computer" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=confectionery with a fixed text, namely 'Candy Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "confectionery" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=convenience with a fixed text, namely 'Convenience Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "convenience" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=copyshop with a fixed text, namely 'Copy Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "copyshop" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=cosmetics with a fixed text, namely 'Cosmetics Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "cosmetics" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=country_store with a fixed text, namely 'Rural Supplies Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "country_store" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=craft with a fixed text, namely 'Arts & Crafts Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "craft" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=curtain with a fixed text, namely 'Curtain Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "curtain" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=dairy with a fixed text, namely 'Dairy Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "dairy" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=deli with a fixed text, namely 'Delicatessen' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "deli" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=department_store with a fixed text, namely 'Department Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "department_store" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=doityourself with a fixed text, namely 'DIY Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "doityourself" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=doors with a fixed text, namely 'Door Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "doors" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=dry_cleaning with a fixed text, namely 'Dry Cleaner' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "dry_cleaning" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=e-cigarette with a fixed text, namely 'E-Cigarette Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "e-cigarette" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=electrical with a fixed text, namely 'Electrical Equipment Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "electrical" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=electronics with a fixed text, namely 'Electronics Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "electronics" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=erotic with a fixed text, namely 'Erotic Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "erotic" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=fabric with a fixed text, namely 'Fabric Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "fabric" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=farm with a fixed text, namely 'Produce Stand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "farm" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=fashion_accessories with a fixed text, namely 'Fashion Accessories Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "fashion_accessories" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=fireplace with a fixed text, namely 'Fireplace Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "fireplace" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=fishing with a fixed text, namely 'Fishing Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "fishing" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=flooring with a fixed text, namely 'Flooring Supply Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "flooring" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=florist with a fixed text, namely 'Florist' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "florist" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=frame with a fixed text, namely 'Framing Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "frame" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=frozen_food with a fixed text, namely 'Frozen Food Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "frozen_food" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=fuel with a fixed text, namely 'Fuel Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "fuel" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=funeral_directors with a fixed text, namely 'Funeral Home' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "funeral_directors" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=furniture with a fixed text, namely 'Furniture Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "furniture" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=games with a fixed text, namely 'Tabletop Game Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "games" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=garden_centre with a fixed text, namely 'Garden Center' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "garden_centre" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=gas with a fixed text, namely 'Bottled Gas Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "gas" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=general with a fixed text, namely 'General Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "general" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=gift with a fixed text, namely 'Gift Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "gift" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=greengrocer with a fixed text, namely 'Greengrocer' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "greengrocer" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=hairdresser with a fixed text, namely 'Hairdresser' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "hairdresser" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=hairdresser_supply with a fixed text, namely 'Hairdresser Supply Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "hairdresser_supply" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=hardware with a fixed text, namely 'Hardware Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "hardware" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=health_food with a fixed text, namely 'Health Food Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "health_food" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=hearing_aids with a fixed text, namely 'Hearing Aids Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "hearing_aids" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=herbalist with a fixed text, namely 'Herbalist' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "herbalist" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=hifi with a fixed text, namely 'Hifi Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "hifi" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=honey with a fixed text, namely 'Honey Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "honey" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=household_linen with a fixed text, namely 'Household Linen Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "household_linen" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=houseware with a fixed text, namely 'Houseware Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "houseware" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=hunting with a fixed text, namely 'Hunting Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "hunting" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=interior_decoration with a fixed text, namely 'Interior Decoration Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "interior_decoration" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=jewelry with a fixed text, namely 'Jewelry Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "jewelry" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=kiosk with a fixed text, namely 'Kiosk' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "kiosk" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=kitchen with a fixed text, namely 'Kitchen Design Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "kitchen" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=laundry with a fixed text, namely 'Laundry' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "laundry" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=leather with a fixed text, namely 'Leather Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "leather" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=lighting with a fixed text, namely 'Lighting Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "lighting" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=locksmith with a fixed text, namely 'Locksmith' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "locksmith" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=lottery with a fixed text, namely 'Lottery Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "lottery" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=mall with a fixed text, namely 'Mall' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "mall" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=massage with a fixed text, namely 'Massage Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "massage" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=medical_supply with a fixed text, namely 'Medical Supply Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "medical_supply" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=military_surplus with a fixed text, namely 'Military Surplus Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "military_surplus" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=mobile_phone with a fixed text, namely 'Mobile Phone Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "mobile_phone" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=model with a fixed text, namely 'Model Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "model" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=money_lender with a fixed text, namely 'Money Lender' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "money_lender" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=motorcycle with a fixed text, namely 'Motorcycle Dealership' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "motorcycle" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=motorcycle_repair with a fixed text, namely 'Motorcycle Repair Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "motorcycle_repair" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=music with a fixed text, namely 'Music Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "music" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=musical_instrument with a fixed text, namely 'Musical Instrument Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "musical_instrument" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=newsagent with a fixed text, namely 'Newsstand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "newsagent" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=nutrition_supplements with a fixed text, namely 'Nutrition Supplements Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "nutrition_supplements" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=nuts with a fixed text, namely 'Nuts Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "nuts" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=optician with a fixed text, namely 'Optician' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "optician" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=outdoor with a fixed text, namely 'Outdoors Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "outdoor" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=outpost with a fixed text, namely 'Online Retailer Outpost' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "outpost" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=paint with a fixed text, namely 'Paint Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "paint" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=party with a fixed text, namely 'Party Supply Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "party" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pasta with a fixed text, namely 'Pasta Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "pasta" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pastry with a fixed text, namely 'Pastry Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "pastry" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pawnbroker with a fixed text, namely 'Pawnshop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "pawnbroker" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=perfumery with a fixed text, namely 'Perfume Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "perfumery" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pet with a fixed text, namely 'Pet Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "pet" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pet_grooming with a fixed text, namely 'Pet Groomer' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "pet_grooming" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=photo with a fixed text, namely 'Photography Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "photo" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pottery with a fixed text, namely 'Pottery Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "pottery" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=printer_ink with a fixed text, namely 'Printer Ink Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "printer_ink" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=psychic with a fixed text, namely 'Psychic' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "psychic" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pyrotechnics with a fixed text, namely 'Fireworks Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "pyrotechnics" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=radiotechnics with a fixed text, namely 'Radio/Electronic Component Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "radiotechnics" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=religion with a fixed text, namely 'Religious Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "religion" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=rental with a fixed text, namely 'Rental Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "rental" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=repair with a fixed text, namely 'Repair Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "repair" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=rice with a fixed text, namely 'Rice Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "rice" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=scuba_diving with a fixed text, namely 'Scuba Diving Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "scuba_diving" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=seafood with a fixed text, namely 'Seafood Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "seafood" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=second_hand with a fixed text, namely 'Thrift Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "second_hand" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=sewing with a fixed text, namely 'Sewing Supply Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "sewing" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=shoe_repair with a fixed text, namely 'Shoe Repair Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "shoe_repair" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=shoes with a fixed text, namely 'Shoe Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "shoes" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=spices with a fixed text, namely 'Spice Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "spices" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=sports with a fixed text, namely 'Sporting Goods Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "sports" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=stationery with a fixed text, namely 'Stationery Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "stationery" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=storage_rental with a fixed text, namely 'Storage Rental' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "storage_rental" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=supermarket with a fixed text, namely 'Supermarket' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "supermarket" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=swimming_pool with a fixed text, namely 'Pool Supply Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "swimming_pool" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tailor with a fixed text, namely 'Tailor' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "tailor" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tattoo with a fixed text, namely 'Tattoo Parlor' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "tattoo" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tea with a fixed text, namely 'Tea Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "tea" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=telecommunication with a fixed text, namely 'Telecom Retail Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "telecommunication" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=ticket with a fixed text, namely 'Ticket Seller' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "ticket" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tiles with a fixed text, namely 'Tile Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "tiles" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tobacco with a fixed text, namely 'Tobacco Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "tobacco" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tool_hire with a fixed text, namely 'Tool Rental' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "tool_hire" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=toys with a fixed text, namely 'Toy Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "toys" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=trade with a fixed text, namely 'Trade Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "trade" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=travel_agency with a fixed text, namely 'Travel Agency' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "travel_agency" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=trophy with a fixed text, namely 'Trophy Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "trophy" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tyres with a fixed text, namely 'Tire Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "tyres" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=vacuum_cleaner with a fixed text, namely 'Vacuum Cleaner Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "vacuum_cleaner" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=variety_store with a fixed text, namely 'Discount Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "variety_store" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=video with a fixed text, namely 'Video Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "video" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=video_games with a fixed text, namely 'Video Game Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "video_games" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=watches with a fixed text, namely 'Watches Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "watches" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=water with a fixed text, namely 'Drinking Water Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "water" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=water_sports with a fixed text, namely 'Watersport/Swim Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "water_sports" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=weapons with a fixed text, namely 'Weapon Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "weapons" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=wholesale with a fixed text, namely 'Wholesale Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "wholesale" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=wigs with a fixed text, namely 'Wig Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "wigs" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=window_blind with a fixed text, namely 'Window Blind Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "window_blind" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=wine with a fixed text, namely 'Wine Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if craft=)", + "value": "wine" + }, { "key": "brand", "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'brand' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists')" @@ -485,12 +1309,12 @@ }, { "key": "service:bicycle:retail", - "description": "Layer 'Bike repair/shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "yes" }, { "key": "service:bicycle:retail", - "description": "Layer 'Bike repair/shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "no" }, { @@ -510,34 +1334,111 @@ }, { "key": "service:bicycle:repair", - "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "yes" }, { "key": "service:bicycle:repair", - "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "no" }, { "key": "service:bicycle:repair", - "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "only_sold" }, { "key": "service:bicycle:repair", - "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "brand" }, { "key": "service:bicycle:rental", - "description": "Layer 'Bike repair/shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "yes" }, { "key": "service:bicycle:rental", - "description": "Layer 'Bike repair/shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "no" }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'rental' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=city_bike with a fixed text, namely 'Normal city bikes can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "city_bike" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=ebike with a fixed text, namely 'Electrical bikes can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "ebike" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=bmx with a fixed text, namely 'BMX bikes can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "bmx" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=mtb with a fixed text, namely 'Mountainbikes can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "mtb" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=kid_bike with a fixed text, namely 'Bikes for children can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "kid_bike" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=tandem with a fixed text, namely 'Tandem bicycles can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "tandem" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=racebike with a fixed text, namely 'Race bicycles can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "racebike" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=bike_helmet with a fixed text, namely 'Bike helmets can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "bike_helmet" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=cargo_bike with a fixed text, namely 'Cargo bikes can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "cargo_bike" + }, + { + "key": "capacity:city_bike", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:city_bike' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if rental~^(.*city_bike.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "capacity:ebike", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:ebike' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if rental~^(.*ebike.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "capacity:kid_bike", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:kid_bike' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if rental~^(.*kid_bike.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "capacity:bmx", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:bmx' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if rental~^(.*bmx.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "capacity:mtb", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:mtb' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if rental~^(.*mtb.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "capacity:bicycle_pannier", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:bicycle_pannier' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if rental~^(.*bicycle_pannier.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "capacity:tandem_bicycle", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:tandem_bicycle' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if rental~^(.*tandem_bicycle.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, { "key": "service:bicycle:pump", "description": "Layer 'Bike repair/shop' shows service:bicycle:pump=yes with a fixed text, namely 'This shop offers a bike pump for anyone' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:pump~.+ | shop=bicycle | shop=bicycle_repair | service:bicycle:retail=yes | ^(service:bicycle:.+)$~~^(yes)$)", @@ -583,6 +1484,25 @@ "description": "Layer 'Bike repair/shop' shows service:bicycle:cleaning=no with a fixed text, namely 'This shop doesn't offer bicycle cleaning' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if service:bicycle:cleaning~.+ | shop=bicycle | shop=bicycle_repair | ^(service:bicycle:.*)$~~^(yes|only)$)", "value": "no" }, + { + "key": "service:bicycle:cleaning:charge", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if amenity!=bike_wash & amenity!=bicycle_wash & service:bicycle:cleaning!=no & service:bicycle:cleaning~.+)" + }, + { + "key": "service:bicycle:cleaning:fee", + "description": "Layer 'Bike repair/shop' shows service:bicycle:cleaning:fee=no with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if amenity!=bike_wash & amenity!=bicycle_wash & service:bicycle:cleaning!=no & service:bicycle:cleaning~.+)", + "value": "no" + }, + { + "key": "service:bicycle:cleaning:fee", + "description": "Layer 'Bike repair/shop' shows service:bicycle:cleaning:fee=yes & service:bicycle:cleaning:charge= with a fixed text, namely 'Free to use' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') (This is only shown if amenity!=bike_wash & amenity!=bicycle_wash & service:bicycle:cleaning!=no & service:bicycle:cleaning~.+)", + "value": "yes" + }, + { + "key": "service:bicycle:cleaning:charge", + "description": "Layer 'Bike repair/shop' shows service:bicycle:cleaning:fee=yes & service:bicycle:cleaning:charge= with a fixed text, namely 'Free to use' (in the mapcomplete.org theme 'Cyclofix - a map for cyclists') Picking this answer will delete the key service:bicycle:cleaning:charge. (This is only shown if amenity!=bike_wash & amenity!=bicycle_wash & service:bicycle:cleaning!=no & service:bicycle:cleaning~.+)", + "value": "" + }, { "key": "internet_access", "description": "Layer 'Bike repair/shop' shows internet_access=wlan with a fixed text, namely 'This place offers wireless internet access' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists')", @@ -1756,6 +2676,11 @@ "description": "Layer 'Bike parking' shows bicycle_parking=lockers with a fixed text, namely 'A locker - the bicycles are enclosed completely individually or with a few bicycles together. The locker is too small to fit a person standing..' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists')", "value": "lockers" }, + { + "key": "bicycle_parking", + "description": "Layer 'Bike parking' shows bicycle_parking=lean_and_stick with a fixed text, namely 'A lean-to bracket with possibility to use a lock through eyelet. The seat tube can be held by the stand by an anchor' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists')", + "value": "lean_and_stick" + }, { "key": "location", "description": "Layer 'Bike parking' shows location=underground with a fixed text, namely 'Underground parking' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists')", @@ -1811,7 +2736,7 @@ }, { "key": "access", - "description": "Layer 'Bike parking' shows access=private with a fixed text, namely 'Access is limited to members of a school, company or organisation' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists')", + "description": "Layer 'Bike parking' shows access=private with a fixed text, namely 'Private bicycle parking which is never available to the public, also not via a membership fee' and allows to pick this as a default answer (in the mapcomplete.org theme 'Cyclofix - a map for cyclists')", "value": "private" }, { diff --git a/Docs/TagInfo/mapcomplete_glutenfree.json b/Docs/TagInfo/mapcomplete_glutenfree.json index 74e7d19e1..abe6e0f72 100644 --- a/Docs/TagInfo/mapcomplete_glutenfree.json +++ b/Docs/TagInfo/mapcomplete_glutenfree.json @@ -1911,12 +1911,12 @@ }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "yes" }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "no" }, { @@ -1936,32 +1936,32 @@ }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "yes" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "no" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "only_sold" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "brand" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "yes" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Glutenfree') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "no" }, { diff --git a/Docs/TagInfo/mapcomplete_healthcare.json b/Docs/TagInfo/mapcomplete_healthcare.json index 90f009299..a6fade9a3 100644 --- a/Docs/TagInfo/mapcomplete_healthcare.json +++ b/Docs/TagInfo/mapcomplete_healthcare.json @@ -1445,12 +1445,12 @@ }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "yes" }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "no" }, { @@ -1470,32 +1470,32 @@ }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "yes" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "no" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "only_sold" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "brand" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "yes" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Healthcare') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "no" }, { diff --git a/Docs/TagInfo/mapcomplete_lactosefree.json b/Docs/TagInfo/mapcomplete_lactosefree.json index 8d29b2001..ac82dd520 100644 --- a/Docs/TagInfo/mapcomplete_lactosefree.json +++ b/Docs/TagInfo/mapcomplete_lactosefree.json @@ -1911,12 +1911,12 @@ }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "yes" }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "no" }, { @@ -1936,32 +1936,32 @@ }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "yes" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "no" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "only_sold" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "brand" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "yes" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Lactose free shops and restaurants') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "no" }, { diff --git a/Docs/TagInfo/mapcomplete_onwheels.json b/Docs/TagInfo/mapcomplete_onwheels.json index bffd13c25..fda8f80fb 100644 --- a/Docs/TagInfo/mapcomplete_onwheels.json +++ b/Docs/TagInfo/mapcomplete_onwheels.json @@ -2453,12 +2453,12 @@ }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "yes" }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "no" }, { @@ -2478,32 +2478,32 @@ }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "yes" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "no" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "only_sold" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "brand" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "yes" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'OnWheels') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "no" }, { diff --git a/Docs/TagInfo/mapcomplete_personal.json b/Docs/TagInfo/mapcomplete_personal.json index ea4d29169..3932e9829 100644 --- a/Docs/TagInfo/mapcomplete_personal.json +++ b/Docs/TagInfo/mapcomplete_personal.json @@ -2483,6 +2483,11 @@ "description": "Layer 'Bike parking' shows bicycle_parking=lockers with a fixed text, namely 'A locker - the bicycles are enclosed completely individually or with a few bicycles together. The locker is too small to fit a person standing..' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme')", "value": "lockers" }, + { + "key": "bicycle_parking", + "description": "Layer 'Bike parking' shows bicycle_parking=lean_and_stick with a fixed text, namely 'A lean-to bracket with possibility to use a lock through eyelet. The seat tube can be held by the stand by an anchor' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme')", + "value": "lean_and_stick" + }, { "key": "location", "description": "Layer 'Bike parking' shows location=underground with a fixed text, namely 'Underground parking' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme')", @@ -2538,7 +2543,7 @@ }, { "key": "access", - "description": "Layer 'Bike parking' shows access=private with a fixed text, namely 'Access is limited to members of a school, company or organisation' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme')", + "description": "Layer 'Bike parking' shows access=private with a fixed text, namely 'Private bicycle parking which is never available to the public, also not via a membership fee' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme')", "value": "private" }, { @@ -2897,6 +2902,830 @@ "key": "name", "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'name' (in the mapcomplete.org theme 'Personal theme')" }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'shop' (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bicycle_rental with a fixed text, namely 'Bicycle rental shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "bicycle_rental" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=agrarian with a fixed text, namely 'Farm Supply Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "agrarian" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=alcohol with a fixed text, namely 'Liquor Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "alcohol" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=anime with a fixed text, namely 'Anime / Manga Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "anime" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=antiques with a fixed text, namely 'Antique Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "antiques" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=appliance with a fixed text, namely 'Appliance Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "appliance" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=art with a fixed text, namely 'Art Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "art" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=baby_goods with a fixed text, namely 'Baby Goods Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "baby_goods" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bag with a fixed text, namely 'Bag/Luggage Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "bag" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bakery with a fixed text, namely 'Bakery' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "bakery" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bathroom_furnishing with a fixed text, namely 'Bathroom Furnishing Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "bathroom_furnishing" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=beauty with a fixed text, namely 'Beauty Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "beauty" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bed with a fixed text, namely 'Bedding/Mattress Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "bed" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=beverages with a fixed text, namely 'Beverage Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "beverages" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bicycle with a fixed text, namely 'Bicycle Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "bicycle" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=boat with a fixed text, namely 'Boat Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "boat" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=bookmaker with a fixed text, namely 'Bookmaker' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "bookmaker" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=books with a fixed text, namely 'Bookstore' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "books" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=brewing_supplies with a fixed text, namely 'Brewing Supply Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "brewing_supplies" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=butcher with a fixed text, namely 'Butcher' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "butcher" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=camera with a fixed text, namely 'Camera Equipment Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "camera" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=candles with a fixed text, namely 'Candle Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "candles" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=cannabis with a fixed text, namely 'Cannabis Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "cannabis" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=car with a fixed text, namely 'Car Dealership' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "car" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=car_parts with a fixed text, namely 'Car Parts Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "car_parts" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=car_repair with a fixed text, namely 'Car Repair Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "car_repair" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=caravan with a fixed text, namely 'RV Dealership' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "caravan" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=carpet with a fixed text, namely 'Carpet Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "carpet" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=catalogue with a fixed text, namely 'Catalog Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "catalogue" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=charity with a fixed text, namely 'Charity Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "charity" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=cheese with a fixed text, namely 'Cheese Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "cheese" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=chemist with a fixed text, namely 'Drugstore' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "chemist" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=chocolate with a fixed text, namely 'Chocolate Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "chocolate" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=clothes with a fixed text, namely 'Clothing Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "clothes" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=coffee with a fixed text, namely 'Coffee Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "coffee" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=collector with a fixed text, namely 'Collectibles Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "collector" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=computer with a fixed text, namely 'Computer Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "computer" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=confectionery with a fixed text, namely 'Candy Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "confectionery" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=convenience with a fixed text, namely 'Convenience Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "convenience" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=copyshop with a fixed text, namely 'Copy Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "copyshop" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=cosmetics with a fixed text, namely 'Cosmetics Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "cosmetics" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=country_store with a fixed text, namely 'Rural Supplies Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "country_store" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=craft with a fixed text, namely 'Arts & Crafts Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "craft" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=curtain with a fixed text, namely 'Curtain Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "curtain" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=dairy with a fixed text, namely 'Dairy Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "dairy" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=deli with a fixed text, namely 'Delicatessen' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "deli" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=department_store with a fixed text, namely 'Department Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "department_store" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=doityourself with a fixed text, namely 'DIY Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "doityourself" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=doors with a fixed text, namely 'Door Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "doors" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=dry_cleaning with a fixed text, namely 'Dry Cleaner' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "dry_cleaning" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=e-cigarette with a fixed text, namely 'E-Cigarette Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "e-cigarette" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=electrical with a fixed text, namely 'Electrical Equipment Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "electrical" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=electronics with a fixed text, namely 'Electronics Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "electronics" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=erotic with a fixed text, namely 'Erotic Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "erotic" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=fabric with a fixed text, namely 'Fabric Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "fabric" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=farm with a fixed text, namely 'Produce Stand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "farm" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=fashion_accessories with a fixed text, namely 'Fashion Accessories Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "fashion_accessories" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=fireplace with a fixed text, namely 'Fireplace Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "fireplace" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=fishing with a fixed text, namely 'Fishing Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "fishing" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=flooring with a fixed text, namely 'Flooring Supply Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "flooring" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=florist with a fixed text, namely 'Florist' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "florist" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=frame with a fixed text, namely 'Framing Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "frame" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=frozen_food with a fixed text, namely 'Frozen Food Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "frozen_food" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=fuel with a fixed text, namely 'Fuel Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "fuel" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=funeral_directors with a fixed text, namely 'Funeral Home' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "funeral_directors" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=furniture with a fixed text, namely 'Furniture Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "furniture" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=games with a fixed text, namely 'Tabletop Game Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "games" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=garden_centre with a fixed text, namely 'Garden Center' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "garden_centre" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=gas with a fixed text, namely 'Bottled Gas Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "gas" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=general with a fixed text, namely 'General Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "general" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=gift with a fixed text, namely 'Gift Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "gift" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=greengrocer with a fixed text, namely 'Greengrocer' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "greengrocer" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=hairdresser with a fixed text, namely 'Hairdresser' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "hairdresser" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=hairdresser_supply with a fixed text, namely 'Hairdresser Supply Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "hairdresser_supply" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=hardware with a fixed text, namely 'Hardware Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "hardware" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=health_food with a fixed text, namely 'Health Food Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "health_food" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=hearing_aids with a fixed text, namely 'Hearing Aids Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "hearing_aids" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=herbalist with a fixed text, namely 'Herbalist' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "herbalist" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=hifi with a fixed text, namely 'Hifi Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "hifi" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=honey with a fixed text, namely 'Honey Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "honey" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=household_linen with a fixed text, namely 'Household Linen Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "household_linen" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=houseware with a fixed text, namely 'Houseware Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "houseware" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=hunting with a fixed text, namely 'Hunting Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "hunting" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=interior_decoration with a fixed text, namely 'Interior Decoration Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "interior_decoration" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=jewelry with a fixed text, namely 'Jewelry Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "jewelry" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=kiosk with a fixed text, namely 'Kiosk' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "kiosk" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=kitchen with a fixed text, namely 'Kitchen Design Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "kitchen" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=laundry with a fixed text, namely 'Laundry' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "laundry" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=leather with a fixed text, namely 'Leather Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "leather" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=lighting with a fixed text, namely 'Lighting Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "lighting" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=locksmith with a fixed text, namely 'Locksmith' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "locksmith" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=lottery with a fixed text, namely 'Lottery Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "lottery" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=mall with a fixed text, namely 'Mall' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "mall" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=massage with a fixed text, namely 'Massage Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "massage" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=medical_supply with a fixed text, namely 'Medical Supply Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "medical_supply" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=military_surplus with a fixed text, namely 'Military Surplus Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "military_surplus" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=mobile_phone with a fixed text, namely 'Mobile Phone Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "mobile_phone" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=model with a fixed text, namely 'Model Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "model" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=money_lender with a fixed text, namely 'Money Lender' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "money_lender" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=motorcycle with a fixed text, namely 'Motorcycle Dealership' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "motorcycle" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=motorcycle_repair with a fixed text, namely 'Motorcycle Repair Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "motorcycle_repair" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=music with a fixed text, namely 'Music Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "music" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=musical_instrument with a fixed text, namely 'Musical Instrument Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "musical_instrument" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=newsagent with a fixed text, namely 'Newsstand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "newsagent" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=nutrition_supplements with a fixed text, namely 'Nutrition Supplements Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "nutrition_supplements" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=nuts with a fixed text, namely 'Nuts Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "nuts" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=optician with a fixed text, namely 'Optician' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "optician" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=outdoor with a fixed text, namely 'Outdoors Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "outdoor" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=outpost with a fixed text, namely 'Online Retailer Outpost' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "outpost" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=paint with a fixed text, namely 'Paint Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "paint" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=party with a fixed text, namely 'Party Supply Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "party" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pasta with a fixed text, namely 'Pasta Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "pasta" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pastry with a fixed text, namely 'Pastry Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "pastry" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pawnbroker with a fixed text, namely 'Pawnshop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "pawnbroker" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=perfumery with a fixed text, namely 'Perfume Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "perfumery" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pet with a fixed text, namely 'Pet Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "pet" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pet_grooming with a fixed text, namely 'Pet Groomer' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "pet_grooming" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=photo with a fixed text, namely 'Photography Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "photo" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pottery with a fixed text, namely 'Pottery Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "pottery" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=printer_ink with a fixed text, namely 'Printer Ink Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "printer_ink" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=psychic with a fixed text, namely 'Psychic' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "psychic" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=pyrotechnics with a fixed text, namely 'Fireworks Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "pyrotechnics" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=radiotechnics with a fixed text, namely 'Radio/Electronic Component Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "radiotechnics" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=religion with a fixed text, namely 'Religious Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "religion" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=rental with a fixed text, namely 'Rental Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "rental" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=repair with a fixed text, namely 'Repair Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "repair" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=rice with a fixed text, namely 'Rice Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "rice" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=scuba_diving with a fixed text, namely 'Scuba Diving Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "scuba_diving" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=seafood with a fixed text, namely 'Seafood Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "seafood" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=second_hand with a fixed text, namely 'Thrift Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "second_hand" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=sewing with a fixed text, namely 'Sewing Supply Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "sewing" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=shoe_repair with a fixed text, namely 'Shoe Repair Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "shoe_repair" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=shoes with a fixed text, namely 'Shoe Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "shoes" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=spices with a fixed text, namely 'Spice Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "spices" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=sports with a fixed text, namely 'Sporting Goods Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "sports" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=stationery with a fixed text, namely 'Stationery Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "stationery" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=storage_rental with a fixed text, namely 'Storage Rental' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "storage_rental" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=supermarket with a fixed text, namely 'Supermarket' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "supermarket" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=swimming_pool with a fixed text, namely 'Pool Supply Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "swimming_pool" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tailor with a fixed text, namely 'Tailor' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "tailor" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tattoo with a fixed text, namely 'Tattoo Parlor' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "tattoo" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tea with a fixed text, namely 'Tea Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "tea" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=telecommunication with a fixed text, namely 'Telecom Retail Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "telecommunication" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=ticket with a fixed text, namely 'Ticket Seller' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "ticket" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tiles with a fixed text, namely 'Tile Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "tiles" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tobacco with a fixed text, namely 'Tobacco Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "tobacco" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tool_hire with a fixed text, namely 'Tool Rental' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "tool_hire" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=toys with a fixed text, namely 'Toy Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "toys" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=trade with a fixed text, namely 'Trade Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "trade" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=travel_agency with a fixed text, namely 'Travel Agency' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "travel_agency" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=trophy with a fixed text, namely 'Trophy Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "trophy" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=tyres with a fixed text, namely 'Tire Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "tyres" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=vacuum_cleaner with a fixed text, namely 'Vacuum Cleaner Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "vacuum_cleaner" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=variety_store with a fixed text, namely 'Discount Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "variety_store" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=video with a fixed text, namely 'Video Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "video" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=video_games with a fixed text, namely 'Video Game Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "video_games" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=watches with a fixed text, namely 'Watches Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "watches" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=water with a fixed text, namely 'Drinking Water Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "water" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=water_sports with a fixed text, namely 'Watersport/Swim Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "water_sports" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=weapons with a fixed text, namely 'Weapon Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "weapons" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=wholesale with a fixed text, namely 'Wholesale Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "wholesale" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=wigs with a fixed text, namely 'Wig Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "wigs" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=window_blind with a fixed text, namely 'Window Blind Store' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "window_blind" + }, + { + "key": "shop", + "description": "Layer 'Bike repair/shop' shows shop=wine with a fixed text, namely 'Wine Shop' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if craft=)", + "value": "wine" + }, { "key": "brand", "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'brand' (in the mapcomplete.org theme 'Personal theme')" @@ -3059,12 +3888,12 @@ }, { "key": "service:bicycle:retail", - "description": "Layer 'Bike repair/shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "yes" }, { "key": "service:bicycle:retail", - "description": "Layer 'Bike repair/shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "no" }, { @@ -3084,34 +3913,111 @@ }, { "key": "service:bicycle:repair", - "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "yes" }, { "key": "service:bicycle:repair", - "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "no" }, { "key": "service:bicycle:repair", - "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "only_sold" }, { "key": "service:bicycle:repair", - "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "brand" }, { "key": "service:bicycle:rental", - "description": "Layer 'Bike repair/shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "yes" }, { "key": "service:bicycle:rental", - "description": "Layer 'Bike repair/shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Bike repair/shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "no" }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'rental' (in the mapcomplete.org theme 'Personal theme') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=city_bike with a fixed text, namely 'Normal city bikes can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "city_bike" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=ebike with a fixed text, namely 'Electrical bikes can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "ebike" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=bmx with a fixed text, namely 'BMX bikes can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "bmx" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=mtb with a fixed text, namely 'Mountainbikes can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "mtb" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=kid_bike with a fixed text, namely 'Bikes for children can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "kid_bike" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=tandem with a fixed text, namely 'Tandem bicycles can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "tandem" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=racebike with a fixed text, namely 'Race bicycles can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "racebike" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=bike_helmet with a fixed text, namely 'Bike helmets can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "bike_helmet" + }, + { + "key": "rental", + "description": "Layer 'Bike repair/shop' shows rental=cargo_bike with a fixed text, namely 'Cargo bikes can be rented here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if (service:bicycle:rental=yes | bicycle_rental~.+))", + "value": "cargo_bike" + }, + { + "key": "capacity:city_bike", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:city_bike' (in the mapcomplete.org theme 'Personal theme') (This is only shown if rental~^(.*city_bike.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "capacity:ebike", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:ebike' (in the mapcomplete.org theme 'Personal theme') (This is only shown if rental~^(.*ebike.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "capacity:kid_bike", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:kid_bike' (in the mapcomplete.org theme 'Personal theme') (This is only shown if rental~^(.*kid_bike.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "capacity:bmx", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:bmx' (in the mapcomplete.org theme 'Personal theme') (This is only shown if rental~^(.*bmx.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "capacity:mtb", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:mtb' (in the mapcomplete.org theme 'Personal theme') (This is only shown if rental~^(.*mtb.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "capacity:bicycle_pannier", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:bicycle_pannier' (in the mapcomplete.org theme 'Personal theme') (This is only shown if rental~^(.*bicycle_pannier.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, + { + "key": "capacity:tandem_bicycle", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'capacity:tandem_bicycle' (in the mapcomplete.org theme 'Personal theme') (This is only shown if rental~^(.*tandem_bicycle.*)$ & (service:bicycle:rental=yes | bicycle_rental~.+))" + }, { "key": "service:bicycle:pump", "description": "Layer 'Bike repair/shop' shows service:bicycle:pump=yes with a fixed text, namely 'This shop offers a bike pump for anyone' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:pump~.+ | shop=bicycle | shop=bicycle_repair | service:bicycle:retail=yes | ^(service:bicycle:.+)$~~^(yes)$)", @@ -3157,6 +4063,25 @@ "description": "Layer 'Bike repair/shop' shows service:bicycle:cleaning=no with a fixed text, namely 'This shop doesn't offer bicycle cleaning' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:cleaning~.+ | shop=bicycle | shop=bicycle_repair | ^(service:bicycle:.*)$~~^(yes|only)$)", "value": "no" }, + { + "key": "service:bicycle:cleaning:charge", + "description": "Layer 'Bike repair/shop' shows and asks freeform values for key 'service:bicycle:cleaning:charge' (in the mapcomplete.org theme 'Personal theme') (This is only shown if amenity!=bike_wash & amenity!=bicycle_wash & service:bicycle:cleaning!=no & service:bicycle:cleaning~.+)" + }, + { + "key": "service:bicycle:cleaning:fee", + "description": "Layer 'Bike repair/shop' shows service:bicycle:cleaning:fee=no with a fixed text, namely 'The cleaning service is free to use' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if amenity!=bike_wash & amenity!=bicycle_wash & service:bicycle:cleaning!=no & service:bicycle:cleaning~.+)", + "value": "no" + }, + { + "key": "service:bicycle:cleaning:fee", + "description": "Layer 'Bike repair/shop' shows service:bicycle:cleaning:fee=yes & service:bicycle:cleaning:charge= with a fixed text, namely 'Free to use' (in the mapcomplete.org theme 'Personal theme') (This is only shown if amenity!=bike_wash & amenity!=bicycle_wash & service:bicycle:cleaning!=no & service:bicycle:cleaning~.+)", + "value": "yes" + }, + { + "key": "service:bicycle:cleaning:charge", + "description": "Layer 'Bike repair/shop' shows service:bicycle:cleaning:fee=yes & service:bicycle:cleaning:charge= with a fixed text, namely 'Free to use' (in the mapcomplete.org theme 'Personal theme') Picking this answer will delete the key service:bicycle:cleaning:charge. (This is only shown if amenity!=bike_wash & amenity!=bicycle_wash & service:bicycle:cleaning!=no & service:bicycle:cleaning~.+)", + "value": "" + }, { "key": "internet_access", "description": "Layer 'Bike repair/shop' shows internet_access=wlan with a fixed text, namely 'This place offers wireless internet access' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme')", @@ -15750,12 +16675,12 @@ }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "yes" }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "no" }, { @@ -15775,32 +16700,32 @@ }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "yes" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "no" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "only_sold" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "brand" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "yes" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "no" }, { diff --git a/Docs/TagInfo/mapcomplete_pets.json b/Docs/TagInfo/mapcomplete_pets.json index 083329459..524302467 100644 --- a/Docs/TagInfo/mapcomplete_pets.json +++ b/Docs/TagInfo/mapcomplete_pets.json @@ -1799,12 +1799,12 @@ }, { "key": "service:bicycle:retail", - "description": "Layer 'Dog-friendly shops' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Dog-friendly shops' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "yes" }, { "key": "service:bicycle:retail", - "description": "Layer 'Dog-friendly shops' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Dog-friendly shops' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "no" }, { @@ -1824,32 +1824,32 @@ }, { "key": "service:bicycle:repair", - "description": "Layer 'Dog-friendly shops' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Dog-friendly shops' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "yes" }, { "key": "service:bicycle:repair", - "description": "Layer 'Dog-friendly shops' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Dog-friendly shops' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "no" }, { "key": "service:bicycle:repair", - "description": "Layer 'Dog-friendly shops' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Dog-friendly shops' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "only_sold" }, { "key": "service:bicycle:repair", - "description": "Layer 'Dog-friendly shops' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Dog-friendly shops' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "brand" }, { "key": "service:bicycle:rental", - "description": "Layer 'Dog-friendly shops' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Dog-friendly shops' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "yes" }, { "key": "service:bicycle:rental", - "description": "Layer 'Dog-friendly shops' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Dog-friendly shops' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Veterinarians, dog parks and other pet-amenities') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "no" }, { diff --git a/Docs/TagInfo/mapcomplete_postboxes.json b/Docs/TagInfo/mapcomplete_postboxes.json index b53bc5cad..8d0d699dd 100644 --- a/Docs/TagInfo/mapcomplete_postboxes.json +++ b/Docs/TagInfo/mapcomplete_postboxes.json @@ -1332,12 +1332,12 @@ }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "yes" }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "no" }, { @@ -1357,32 +1357,32 @@ }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "yes" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "no" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "only_sold" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "brand" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "yes" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Postbox and Post Office Map') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "no" }, { diff --git a/Docs/TagInfo/mapcomplete_shops.json b/Docs/TagInfo/mapcomplete_shops.json index 606ae2670..dd34796c2 100644 --- a/Docs/TagInfo/mapcomplete_shops.json +++ b/Docs/TagInfo/mapcomplete_shops.json @@ -1034,12 +1034,12 @@ }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "yes" }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "no" }, { @@ -1059,32 +1059,32 @@ }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "yes" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "no" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "only_sold" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "brand" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "yes" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Shops') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "no" }, { diff --git a/Docs/TagInfo/mapcomplete_sports.json b/Docs/TagInfo/mapcomplete_sports.json index 390469ffa..602f809a3 100644 --- a/Docs/TagInfo/mapcomplete_sports.json +++ b/Docs/TagInfo/mapcomplete_sports.json @@ -1593,12 +1593,12 @@ }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=yes with a fixed text, namely 'This shop sells new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "yes" }, { "key": "service:bicycle:retail", - "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=diy | shop=doityourself)", + "description": "Layer 'Shop' shows service:bicycle:retail=no with a fixed text, namely 'This shop doesn't sell new bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:retail~.+ | shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself)", "value": "no" }, { @@ -1618,32 +1618,32 @@ }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=yes with a fixed text, namely 'This shop repairs bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "yes" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=no with a fixed text, namely 'This shop doesn't repair bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "no" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=only_sold with a fixed text, namely 'This shop only repairs bikes bought here' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "only_sold" }, { "key": "service:bicycle:repair", - "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", + "description": "Layer 'Shop' shows service:bicycle:repair=brand with a fixed text, namely 'This shop only repairs bikes of a certain brand' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:repair~.+ | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only)", "value": "brand" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=yes with a fixed text, namely 'This shop rents out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "yes" }, { "key": "service:bicycle:rental", - "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental)", + "description": "Layer 'Shop' shows service:bicycle:rental=no with a fixed text, namely 'This shop doesn't rent out bikes' and allows to pick this as a default answer (in the mapcomplete.org theme 'Sports') (This is only shown if service:bicycle:rental~.+ | shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental)", "value": "no" }, { diff --git a/Docs/TagInfo/mapcomplete_transit.json b/Docs/TagInfo/mapcomplete_transit.json index f5124fe50..a3727fe14 100644 --- a/Docs/TagInfo/mapcomplete_transit.json +++ b/Docs/TagInfo/mapcomplete_transit.json @@ -285,6 +285,11 @@ "description": "Layer 'Bike parking' shows bicycle_parking=lockers with a fixed text, namely 'A locker - the bicycles are enclosed completely individually or with a few bicycles together. The locker is too small to fit a person standing..' and allows to pick this as a default answer (in the mapcomplete.org theme 'Bus routes')", "value": "lockers" }, + { + "key": "bicycle_parking", + "description": "Layer 'Bike parking' shows bicycle_parking=lean_and_stick with a fixed text, namely 'A lean-to bracket with possibility to use a lock through eyelet. The seat tube can be held by the stand by an anchor' and allows to pick this as a default answer (in the mapcomplete.org theme 'Bus routes')", + "value": "lean_and_stick" + }, { "key": "location", "description": "Layer 'Bike parking' shows location=underground with a fixed text, namely 'Underground parking' and allows to pick this as a default answer (in the mapcomplete.org theme 'Bus routes')", @@ -340,7 +345,7 @@ }, { "key": "access", - "description": "Layer 'Bike parking' shows access=private with a fixed text, namely 'Access is limited to members of a school, company or organisation' and allows to pick this as a default answer (in the mapcomplete.org theme 'Bus routes')", + "description": "Layer 'Bike parking' shows access=private with a fixed text, namely 'Private bicycle parking which is never available to the public, also not via a membership fee' and allows to pick this as a default answer (in the mapcomplete.org theme 'Bus routes')", "value": "private" }, { diff --git a/Docs/Themes/circular_economy.md b/Docs/Themes/circular_economy.md index c051bd594..d6fe9ae59 100644 --- a/Docs/Themes/circular_economy.md +++ b/Docs/Themes/circular_economy.md @@ -454,7 +454,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -475,7 +475,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -484,7 +484,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Themes/climbing.md b/Docs/Themes/climbing.md index 8df97f4a8..bb9bb1b51 100644 --- a/Docs/Themes/climbing.md +++ b/Docs/Themes/climbing.md @@ -475,7 +475,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -496,7 +496,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -505,7 +505,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Themes/glutenfree.md b/Docs/Themes/glutenfree.md index 6d47e6576..d86b74880 100644 --- a/Docs/Themes/glutenfree.md +++ b/Docs/Themes/glutenfree.md @@ -1252,7 +1252,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -1273,7 +1273,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -1282,7 +1282,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Themes/healthcare.md b/Docs/Themes/healthcare.md index fa1cb0c84..56e6b7aaa 100644 --- a/Docs/Themes/healthcare.md +++ b/Docs/Themes/healthcare.md @@ -472,7 +472,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -493,7 +493,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -502,7 +502,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Themes/lactosefree.md b/Docs/Themes/lactosefree.md index b48fe943c..0b550b0f6 100644 --- a/Docs/Themes/lactosefree.md +++ b/Docs/Themes/lactosefree.md @@ -1250,7 +1250,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -1271,7 +1271,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -1280,7 +1280,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Themes/openlovemap.md b/Docs/Themes/openlovemap.md index ebf6849de..f7986e7a9 100644 --- a/Docs/Themes/openlovemap.md +++ b/Docs/Themes/openlovemap.md @@ -507,7 +507,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -528,7 +528,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -537,7 +537,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Themes/pets.md b/Docs/Themes/pets.md index d0d85166c..a2edbc574 100644 --- a/Docs/Themes/pets.md +++ b/Docs/Themes/pets.md @@ -1024,7 +1024,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -1045,7 +1045,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -1054,7 +1054,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Themes/sports.md b/Docs/Themes/sports.md index 4cc83a4ec..e48c1b55a 100644 --- a/Docs/Themes/sports.md +++ b/Docs/Themes/sports.md @@ -463,7 +463,7 @@ The question is `Does this shop sell bikes?` - *This shop sells new bikes* is shown if with service:bicycle:retail=yes - *This shop doesn't sell new bikes* is shown if with service:bicycle:retail=no -This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=diy | shop=doityourself | service:bicycle:retail~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=outdoor | shop=sport | shop=sports | shop=diy | shop=doityourself | service:bicycle:retail~.+ ### bike_second_hand @@ -484,7 +484,7 @@ The question is `Does this shop repair bikes?` - *This shop only repairs bikes bought here* is shown if with service:bicycle:repair=only_sold - *This shop only repairs bikes of a certain brand* is shown if with service:bicycle:repair=brand -This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ +This tagrendering is only visible in the popup if the following condition is met: service:bicycle:retail=yes | service:bicycle:second_hand=yes | service:bicycle:second_hand=only | shop=sport | shop=sports | shop=outdoor | shop=bicycle | service:bicycle:repair~.+ ### bicycle_rental @@ -493,7 +493,7 @@ The question is `Does this shop rent out bikes?` - *This shop rents out bikes* is shown if with service:bicycle:rental=yes - *This shop doesn't rent out bikes* is shown if with service:bicycle:rental=no -This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ +This tagrendering is only visible in the popup if the following condition is met: shop=bicycle | shop=sport | shop=sports | shop=bicycle_repair | shop=outdoor | shop=rental | service:bicycle:rental~.+ ### bicycle-types diff --git a/Docs/Themes/velopark.md b/Docs/Themes/velopark.md index 132a8956c..982513fe9 100644 --- a/Docs/Themes/velopark.md +++ b/Docs/Themes/velopark.md @@ -249,6 +249,10 @@ This tagrendering has labels -----|-----|----- | | created-only.0 | Only unfinished tasks | mr_taskStatus=Created | +| id | question | osmTags | +-----|-----|----- | +| nmbs-only.0 | Only non-SNCB-bicycle parkings | ref:velopark!~^(https:\/\/data.velopark.be\/data\/NMBS.*)$ | + | id | question | osmTags | -----|-----|----- | | too-hard-only.0 | Only too-hard tasks | mr_taskStatus=Too_hard | @@ -279,7 +283,7 @@ Elements must match **all** of the following expressions: | attribute | type | values which are supported by this layer | -----|-----|----- | | [ref:velopark](https://wiki.openstreetmap.org/wiki/Key:ref:velopark) | [velopark](../SpecialInputElements.md#velopark) | | -| [bicycle_parking](https://wiki.openstreetmap.org/wiki/Key:bicycle_parking) | [string](../SpecialInputElements.md#string) | [stands](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dstands) [safe_loops](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dsafe_loops) [wall_loops](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dwall_loops) [handlebar_holder](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dhandlebar_holder) [rack](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Drack) [two_tier](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dtwo_tier) [shed](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dshed) [bollard](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dbollard) [floor](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dfloor) [lockers](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dlockers) | +| [bicycle_parking](https://wiki.openstreetmap.org/wiki/Key:bicycle_parking) | [string](../SpecialInputElements.md#string) | [stands](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dstands) [safe_loops](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dsafe_loops) [wall_loops](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dwall_loops) [handlebar_holder](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dhandlebar_holder) [rack](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Drack) [two_tier](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dtwo_tier) [shed](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dshed) [bollard](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dbollard) [floor](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dfloor) [lockers](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dlockers) [lean_and_stick](https://wiki.openstreetmap.org/wiki/Tag:bicycle_parking%3Dlean_and_stick) | | [location](https://wiki.openstreetmap.org/wiki/Key:location) | Multiple choice | [underground](https://wiki.openstreetmap.org/wiki/Tag:location%3Dunderground) [surface](https://wiki.openstreetmap.org/wiki/Tag:location%3Dsurface) [rooftop](https://wiki.openstreetmap.org/wiki/Tag:location%3Drooftop) | | [covered](https://wiki.openstreetmap.org/wiki/Key:covered) | Multiple choice | [yes](https://wiki.openstreetmap.org/wiki/Tag:covered%3Dyes) [no](https://wiki.openstreetmap.org/wiki/Tag:covered%3Dno) | | [capacity](https://wiki.openstreetmap.org/wiki/Key:capacity) | [nat](../SpecialInputElements.md#nat) | | @@ -339,6 +343,7 @@ The question is `What is the type of this bicycle parking?` - *Bollard* is shown if with bicycle_parking=bollard - *An area on the floor which is marked for bicycle parking* is shown if with bicycle_parking=floor - *A locker - the bicycles are enclosed completely individually or with a few bicycles together. The locker is too small to fit a person standing..* is shown if with bicycle_parking=lockers + - *A lean-to bracket with possibility to use a lock through eyelet. The seat tube can be held by the stand by an anchor* is shown if with bicycle_parking=lean_and_stick ### Underground? @@ -369,7 +374,7 @@ The question is `Who can use this bicycle parking?` - *Publicly accessible* is shown if with access=yes - *Access is primarily for visitors to a business* is shown if with access=customers - *Access is limited to members of a school, company or organisation* is shown if with access=members - - *Access is limited to members of a school, company or organisation* is shown if with access=private + - *Private bicycle parking which is never available to the public, also not via a membership fee* is shown if with access=private ### fee diff --git a/Docs/URL_Parameters.md b/Docs/URL_Parameters.md index 6ab54ccc6..acfb26baa 100644 --- a/Docs/URL_Parameters.md +++ b/Docs/URL_Parameters.md @@ -112,7 +112,7 @@ The default value is _false_ Disables/Enables logging in and thus disables editing all together. This effectively puts MapComplete into read-only mode. -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L103) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L98) The default value is _true_ @@ -120,7 +120,7 @@ The default value is _true_ Disables/Enables the search bar -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L118) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L113) The default value is _true_ @@ -128,7 +128,7 @@ The default value is _true_ Disables/Enables the background layer control where a user can enable e.g. aerial imagery -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L123) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L118) The default value is _true_ @@ -136,7 +136,7 @@ The default value is _true_ Disables/Enables the filter view where a user can enable/disable MapComplete-layers or filter for certain properties -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L129) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L124) The default value is _true_ @@ -144,7 +144,7 @@ The default value is _true_ Disables/enables the help menu or welcome message -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L135) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L130) The default value is _true_ @@ -152,7 +152,7 @@ The default value is _true_ Disables/enables the button to get in touch with the community -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L140) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L135) The default value is _true_ @@ -160,7 +160,7 @@ The default value is _true_ Disables/Enables the extraLink button. By default, if in iframe mode and the welcome message is hidden, a popout button to the full mapcomplete instance is shown instead (unless disabled with this switch or another extraLink button is enabled) -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L145) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L140) The default value is _true_ @@ -168,7 +168,7 @@ The default value is _true_ Disables/Enables the various links which go back to the index page with the theme overview -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L150) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L145) The default value is _true_ @@ -176,7 +176,7 @@ The default value is _true_ Disables/Enables the 'Share-screen'-tab in the welcome message -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L155) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L150) The default value is _true_ @@ -184,7 +184,7 @@ The default value is _true_ Disables/Enables the geolocation button -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L160) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L155) The default value is _true_ @@ -192,7 +192,7 @@ The default value is _true_ If set to false, all layers will be disabled - except the explicitly enabled layers -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L166) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L161) The default value is _true_ @@ -200,7 +200,7 @@ The default value is _true_ Always show all questions -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L171) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L166) The default value is _false_ @@ -208,7 +208,7 @@ The default value is _false_ Enable the export as GeoJSON and CSV button -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L177) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L172) The default value is _true_ @@ -216,7 +216,7 @@ The default value is _true_ Enable/disable caching from localStorage -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L183) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L178) The default value is _true_ @@ -224,7 +224,7 @@ The default value is _true_ If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L198) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L193) The default value is _false_ @@ -232,7 +232,7 @@ The default value is _false_ If true, shows some extra debugging help such as all the available tags on every object -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L204) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L199) The default value is _false_ @@ -240,7 +240,7 @@ The default value is _false_ If true, the location distance indication will not be written to the changeset and other privacy enhancing measures might be taken. -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L210) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L205) The default value is _false_ @@ -248,7 +248,7 @@ The default value is _false_ Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L216) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L211) The default value is _https://overpass-api.de/api/interpreter,https://overpass.private.coffee/api/interpreter,https://overpass.osm.jp/api/interpreter_ @@ -256,7 +256,7 @@ The default value is _https://overpass-api.de/api/interpreter,https://overpass.p Set a different timeout (in seconds) for queries in overpass -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L227) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L222) The default value is _30_ @@ -264,7 +264,7 @@ The default value is _30_ point to switch between OSM-api and overpass -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L235) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L230) The default value is _16_ @@ -272,7 +272,7 @@ The default value is _16_ Tilesize when the OSM-API is used to fetch data within a BBOX -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L243) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L238) The default value is _17_ @@ -332,7 +332,7 @@ One can use the [ID of an ELI-layer](./ELI-overview.md) or use one of the global - HDM_HOT - osmfr-occitan -This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L250) +This documentation is defined in the source code at [FeatureSwitchState.ts](/src/Logic/State/FeatureSwitchState.ts#L245) No default value set diff --git a/assets/layers/advertising/advertising.json b/assets/layers/advertising/advertising.json index 6faf8ef35..4a2b54e70 100644 --- a/assets/layers/advertising/advertising.json +++ b/assets/layers/advertising/advertising.json @@ -1614,7 +1614,7 @@ "ca": "Per quants costats pots veure publicitat?", "es": "¿Por cuantos lados puedes ver publicidad?", "en": "From how many sides you can watch advertisments?", - "de": "Auf wie vielen Seiten wird Werbung angezeigt?", + "de": "Von wie vielen Seiten kann man Werbung betrachten?", "cs": "Z kolika stran můžete vidět reklamy?", "fr": "Sur combien de côtés peut-on voir de la publicité ?", "pt": "De quantos lados você pode assistir a anúncios?", diff --git a/assets/layers/ambulancestation/ambulancestation.json b/assets/layers/ambulancestation/ambulancestation.json index c6fa45448..81b4233d0 100644 --- a/assets/layers/ambulancestation/ambulancestation.json +++ b/assets/layers/ambulancestation/ambulancestation.json @@ -208,7 +208,8 @@ "pl": "Jak nazywa się ulica, przy której znajduje się stacja?", "pt_BR": "Como é o nome da rua em que a estação está localizada?", "zh_Hans": "车站所在的街道名称是什么?", - "pt": "Como é o nome da rua em que a estação está localizada?" + "pt": "Como é o nome da rua em que a estação está localizada?", + "uk": "Як називається вулиця, на якій розташована станція?" }, "render": { "en": "This station is along a highway called {addr:street}.", @@ -247,7 +248,8 @@ "he": "היכן ממוקמת התחנה? (למשל שם השכונה, הכפר או העיירה)", "pt_BR": "Onde está localizada a estação? (por exemplo, nome do bairro, vila ou cidade)", "zh_Hans": "车站位于哪里? (例如邻里、村庄或城镇的名称)", - "pt": "Onde está a estação localizada ? (por exemplo, nome do bairro, vila ou cidade)" + "pt": "Onde está a estação localizada ? (por exemplo, nome do bairro, vila ou cidade)", + "uk": "Де знаходиться станція? (наприклад, назва району, села чи міста)" }, "freeform": { "key": "addr:place" diff --git a/assets/layers/artwork/artwork.json b/assets/layers/artwork/artwork.json index 15a761912..26fc41438 100644 --- a/assets/layers/artwork/artwork.json +++ b/assets/layers/artwork/artwork.json @@ -265,7 +265,8 @@ "pt_BR": "Arquitetura", "he": "אדריכלות", "eu": "Arkitektura", - "zh_Hans": "建筑" + "zh_Hans": "建筑", + "uk": "Архітектура" } }, { @@ -292,7 +293,8 @@ "pt_BR": "Mural", "he": "ציור קיר", "eu": "Murala", - "zh_Hans": "壁画" + "zh_Hans": "壁画", + "uk": "Мурал" } }, { @@ -318,7 +320,8 @@ "he": "ציור", "eu": "Margolana", "pt_BR": "Pintura", - "zh_Hans": "喷绘" + "zh_Hans": "喷绘", + "uk": "Живопис" } }, { @@ -345,7 +348,8 @@ "pt_BR": "Escultura", "he": "פיסול", "eu": "Eskultura", - "zh_Hans": "雕塑" + "zh_Hans": "雕塑", + "uk": "Скульптура" } }, { @@ -370,7 +374,8 @@ "pt_BR": "Estátua", "he": "פסל", "eu": "Estatua", - "zh_Hans": "雕像" + "zh_Hans": "雕像", + "uk": "Статуя" } }, { @@ -394,7 +399,8 @@ "cs": "Busta", "pt_BR": "Busto", "eu": "Bustoa", - "zh_Hans": "半身像" + "zh_Hans": "半身像", + "uk": "Бюст" } }, { @@ -421,7 +427,8 @@ "pt_BR": "Pedra", "he": "אבן", "eu": "Harria", - "zh_Hans": "石头" + "zh_Hans": "石头", + "uk": "Камінь" } }, { @@ -446,7 +453,8 @@ "cs": "Instalace", "pt_BR": "Instalação", "eu": "Instalazioa", - "zh_Hans": "设施" + "zh_Hans": "设施", + "uk": "Інсталяція" } }, { @@ -471,7 +479,8 @@ "cs": "Grafiti", "eu": "Graffitia", "pt_BR": "Grafite", - "zh_Hans": "涂鸦/粗糙雕刻" + "zh_Hans": "涂鸦/粗糙雕刻", + "uk": "Графіті" } }, { @@ -495,7 +504,8 @@ "da": "Relief", "cs": "Reliéf", "eu": "Erliebea", - "pt_BR": "Relevo" + "pt_BR": "Relevo", + "uk": "Рельєф" } }, { @@ -519,7 +529,8 @@ "cs": "Azulejo (španělské dekorativní dlaždice)", "ca": "Azulejo (Rajoles decoratives espanyoles i portugueses)", "pt_BR": "Azulejo (Revestimento de azulejos decorativos espanhóis)", - "zh_Hans": "Azulejo(西班牙装饰瓷砖)" + "zh_Hans": "Azulejo(西班牙装饰瓷砖)", + "uk": "Азулехо (іспанська декоративна плитка)" } }, { @@ -543,7 +554,8 @@ "cs": "Obklady a dlažba", "he": "יצירת פסיפס", "eu": "Keramika", - "pt_BR": "Azulejaria" + "pt_BR": "Azulejaria", + "uk": "Кахельна плитка" } }, { @@ -562,7 +574,8 @@ "eu": "Egur taila", "pl": "Rzeźbienie w drewnie", "pt_BR": "Entalhe", - "it": "Scultura in legno" + "it": "Scultura in legno", + "uk": "Різьба по дереву" } } ], @@ -599,7 +612,8 @@ "he": "מי יצר את היצירה הזו?", "pl": "Kto stworzył to dzieło sztuki?", "pt_BR": "Quem fez essa obra de arte?", - "zh_Hans": "这件艺术品是谁创作的?" + "zh_Hans": "这件艺术品是谁创作的?", + "uk": "Хто створив цю роботу?" }, "freeform": { "key": "artist:wikidata", @@ -797,7 +811,8 @@ "de": "Dient dieses Kunstwerk als Mahnmal?", "fr": "Cet œuvre a-t-elle un rôle de mémorial ?", "cs": "Slouží toto dílo jako památník?", - "es": "¿Sirve esta obra de arte como monumento conmemorativo?" + "es": "¿Sirve esta obra de arte como monumento conmemorativo?", + "uk": "Чи слугує ця робота меморіалом?" }, "mappings": [ { @@ -808,7 +823,8 @@ "ca": "Aquesta obra d'art també serveix com a memorial", "fr": "Cette œuvre est un mémorial", "cs": "Toto dílo slouží také jako památník", - "es": "Esta obra de arte también sirve como monumento conmemorativo" + "es": "Esta obra de arte también sirve como monumento conmemorativo", + "uk": "Цей витвір мистецтва також слугує меморіалом" } }, { @@ -816,7 +832,8 @@ "alsoShowIf": "historic!=memorial", "then": { "en": "This artwork does not serve as a memorial", - "de": "Dieses Kunstwerk dient nicht als Denkmal" + "de": "Dieses Kunstwerk dient nicht als Denkmal", + "uk": "Ця робота не є меморіалом" } } ] @@ -846,7 +863,8 @@ "pt_BR": "Essa obra de arte serve como banco?", "es": "¿Sirve esta obra de arte como banco?", "pt": "Esta obra de arte serve como banco?", - "it": "Quest'opera d'arte funge da panchina?" + "it": "Quest'opera d'arte funge da panchina?", + "uk": "Чи слугує ця робота лавкою?" }, "mappings": [ { @@ -863,7 +881,8 @@ "pt_BR": "Essa obra de arte também serve como banco", "es": "Esta obra de arte también sirve de banco", "pt": "Esta obra de arte também serve como banco", - "it": "Quest'opera d'arte funge anche da panchina" + "it": "Quest'opera d'arte funge anche da panchina", + "uk": "Цей витвір мистецтва також слугує лавкою" } }, { @@ -881,7 +900,8 @@ "pt_BR": "Essa obra de arte não serve como banco", "es": "Esta obra no sirve de banco", "pt": "Esta obra de arte não serve como banco", - "it": "Quest'opera d'arte non funge anche da panchina" + "it": "Quest'opera d'arte non funge anche da panchina", + "uk": "Цей витвір мистецтва не слугує лавкою" } }, { diff --git a/assets/layers/bbq/bbq.json b/assets/layers/bbq/bbq.json index 025891645..9264d20b5 100644 --- a/assets/layers/bbq/bbq.json +++ b/assets/layers/bbq/bbq.json @@ -161,7 +161,7 @@ "id": "fuel", "question": { "en": "How ist the grill fueled?", - "de": "Mit was wird die Grillstelle angefeuert?", + "de": "Womit wird der Grill befeuert?", "cs": "Jak je gril napájen?", "es": "¿Cómo se alimenta la parrilla?" }, diff --git a/assets/layers/bench/bench.json b/assets/layers/bench/bench.json index a9e5bca9d..020e7060c 100644 --- a/assets/layers/bench/bench.json +++ b/assets/layers/bench/bench.json @@ -145,7 +145,8 @@ "fr": "Ce banc a deux côtés avec un dossier partagé", "pl": "Ta ławka jest dwustronna ze wspólnym oparciem", "pt_BR": "Esse banco é de dois lados e compartilha o encosto", - "pt": "Esse banco é de dois lados e partilha o encosto" + "pt": "Esse banco é de dois lados e partilha o encosto", + "uk": "Ця лавка двостороння і має спільну спинку" }, "icon": { "path": "./assets/layers/bench/two_sided.svg", @@ -160,7 +161,7 @@ "fr": "Ce banc a un dossier", "nl": "Heeft een rugleuning", "es": "Este banco tiene respaldo", - "hu": "Van háttámlája", + "hu": "A padnak van háttámlája", "id": "Sandaran: Ya", "it": "Schienale: Sì", "ru": "Со спинкой", @@ -174,7 +175,8 @@ "ca": "Aquest banc té espatller", "da": "Har et ryglæn", "cs": "Tato lavice má opěradlo", - "he": "יש משענת גב" + "he": "יש משענת גב", + "uk": "Ця лавка має спинку" }, "addExtraTags": [ "two_sided=no" @@ -191,7 +193,7 @@ "fr": "Ce banc n'a pas de dossier", "nl": "Heeft geen rugleuning", "es": "Este banco no tiene respaldo", - "hu": "Nincs háttámlája", + "hu": "A padnak nincs háttámlája", "id": "Sandaran: Tidak", "it": "Questa panchina non ha uno schienale", "ru": "Без спинки", @@ -204,7 +206,8 @@ "pt": "Não not tem um encosto", "ca": "Aquest banc No té espatller", "da": "Har ikke et ryglæn", - "cs": "Tato lavice nemá opěradlo" + "cs": "Tato lavice nemá opěradlo", + "uk": "Ця лавка не має спинки" } } ], @@ -227,7 +230,8 @@ "da": "Har denne bænk et ryglæn?", "cs": "Má tato lavička opěradlo?", "ca": "Aquest banc té respatller?", - "he": "האם לספסל הזה יש משענת?" + "he": "האם לספסל הזה יש משענת?", + "uk": "Чи є у цієї лавки спинка?" }, "id": "bench-backrest", "labels": [ @@ -243,7 +247,8 @@ "de": "Diese Sitzbank hat eine oder mehrere Armlehnen", "nl": "Deze bank heeft armleuningen", "cs": "Tato lavice má jednu nebo více opěrek na ruce", - "es": "Este banco dispone de uno o más reposabrazos" + "es": "Este banco dispone de uno o más reposabrazos", + "uk": "Ця лавка має один або кілька підлокітників" } }, { @@ -253,7 +258,8 @@ "de": "Diese Sitzbank hat keine Armlehnen", "nl": "Deze bank heeft geen armleuningen", "cs": "Tato lavice nemá opěrky rukou", - "es": "Este banco not dispone de reposabrazos" + "es": "Este banco not dispone de reposabrazos", + "uk": "Ця лавка не має підлокітників" } } ], @@ -262,7 +268,8 @@ "de": "Hat diese Sitzbank eine oder mehrere Armlehnen?", "nl": "Heeft deze bank armleuningen?", "cs": "Má tato lavice jednu nebo více opěrek?", - "es": "¿Dispone este banco de uno o más reposabrazos?" + "es": "¿Dispone este banco de uno o más reposabrazos?", + "uk": "Чи має ця лавка один або кілька підлокітників?" }, "id": "bench-armrest", "labels": [ @@ -276,7 +283,7 @@ "fr": "{seats} places", "nl": "{seats} zitplaatsen", "es": "Este banco tiene {seats} asientos", - "hu": "{seats} ülőhely", + "hu": "A padra {seats} ember tud leülni", "id": "{seats} kursi", "it": "{seats} posti", "ru": "{seats} мест", @@ -925,7 +932,8 @@ "cs": "Tato lavice má následující nápis:

{inscription}

", "pt": "Este banco tem a seguinte inscrição:

{inscription}

", "pt_BR": "Esse banco tem a seguinte inscrição:

{inscription}

", - "es": "Este banco tiene la siguiente inscripción:

{inscription}

" + "es": "Este banco tiene la siguiente inscripción:

{inscription}

", + "uk": "Ця лавка має такий напис:

{inscription}

" }, "question": { "en": "Does this bench have an inscription?", @@ -936,7 +944,8 @@ "cs": "Má tato lavička nápis?", "pt": "Esse banco tem inscrição?", "pt_BR": "Esse banco tem uma inscrição?", - "es": "¿Tiene este banco una inscripción?" + "es": "¿Tiene este banco una inscripción?", + "uk": "Чи є на цій лавці напис?" }, "freeform": { "key": "inscription", @@ -957,7 +966,8 @@ "cs": "Tato lavička nemá nápis", "pt": "Este banco não tem inscrição", "pt_BR": "Esse banco não tem uma inscrição", - "es": "Este banco no tiene inscripción" + "es": "Este banco no tiene inscripción", + "uk": "Ця лавка не має напису" }, "addExtraTags": [ "inscription=" @@ -974,7 +984,8 @@ "ca": "Aquest banc (probablement) no té cap inscripció", "cs": "Tato lavička (pravděpodobně) nemá nápis", "pt_BR": "Esse banco provavelmente não tem uma inscrição", - "pt": "Esse banco provavelmente não tem uma inscrição" + "pt": "Esse banco provavelmente não tem uma inscrição", + "uk": "Ця лавка імовірно не має напису" }, "hideInAnswer": true } @@ -988,7 +999,8 @@ "cs": "Např. na připevněné desce, v opěradle, …", "pt": "Por exemplo: em placa montada, no encosto, ...", "pt_BR": "P. ex. em uma placa montada, no encosto, …", - "es": "Por ejemplo, en una placa montada, en el respaldo, …" + "es": "Por ejemplo, en una placa montada, en el respaldo, …", + "uk": "Наприклад, на вмонтованій табличці, в спинці крісла, …" } }, { @@ -1004,7 +1016,8 @@ "pl": "Czy ta ławka ma wbudowane dzieło sztuki?", "pt_BR": "Esse banco tem um elemento artístico?", "es": "¿Tiene este banco un elemento artístico?", - "pt": "Este banco tem um elemento artístico?" + "pt": "Este banco tem um elemento artístico?", + "uk": "Чи є в цій лавці художній елемент?" }, "mappings": [ { @@ -1023,7 +1036,8 @@ "pl": "Ta ławka ma wbudowane dzieło sztuki", "pt_BR": "Esse banco tem uma obra de arte integrada", "es": "Este banco tiene una obra de arte integrada", - "pt": "Este banco tem uma obra de arte integrada" + "pt": "Este banco tem uma obra de arte integrada", + "uk": "Ця лавка має інтегрований арт-об'єкт" } }, { @@ -1039,7 +1053,8 @@ "he": "לספסל זה אין יצירת אמנות משולבת", "pl": "Ta ławka nie ma wbudowanego dzieła sztuki", "pt_BR": "Esse banco não tem uma obra de arte integrada", - "pt": "Este banco não tem uma obra de arte integrada" + "pt": "Este banco não tem uma obra de arte integrada", + "uk": "Ця лавка не має інтегрованого художнього оформлення" }, "addExtraTags": [ "tourism=" @@ -1071,7 +1086,8 @@ "pl": "Np. jest na niej coś namalowane, ma rzeźbę lub inną nietrywialną pracę", "pt_BR": "Ex. possui uma pintura integrada, estátua ou outro trabalho criativo não trivial", "es": "Por ejemplo, si lleva integrado un cuadro, una estatua u otra obra creativa no trivial", - "pt": "Ex. possui uma pintura integrada, estátua ou outro trabalho criativo não trivial" + "pt": "Ex. possui uma pintura integrada, estátua ou outro trabalho criativo não trivial", + "uk": "Наприклад, він має інтегровану картину, статую або іншу нетривіальну, творчу роботу" } }, { diff --git a/assets/layers/bench_at_pt/bench_at_pt.json b/assets/layers/bench_at_pt/bench_at_pt.json index b8153e73b..0bdc3f565 100644 --- a/assets/layers/bench_at_pt/bench_at_pt.json +++ b/assets/layers/bench_at_pt/bench_at_pt.json @@ -255,7 +255,7 @@ "if": "bench=no", "then": { "en": "This bus stop does not have a bench (there never was one or it has been removed)", - "de": "Diese Bushaltestelle hat keine Bank (es gab nie eine oder sie wurde entfernt)", + "de": "Diese Bushaltestelle hat keine Sitzank (es gab nie eine oder sie wurde entfernt)", "fr": "Cette station de bus n'a pas de banc (il n'y en a jamais eu ou il a été retiré)", "nl": "Deze bushalte heeft geen zitbank (er is er nooit een geweest of deze is verwijderd)", "ca": "Aquesta para de bus no té un banc (mai n'ha tingut un o ha estat eliminat)", @@ -263,7 +263,8 @@ "pt": "Este ponto de ônibus não tem banco (nunca houve ou foi removido)", "pl": "Ten przystanek autobusowy nie ma ławki (nigdy jej nie było lub została usunięta)", "pt_BR": "Essa parada de ônibus não tem um banco (nunca teve ou ele foi removido)", - "es": "Esta parada de autobús no tiene banco (nunca lo hubo o lo han quitado)" + "es": "Esta parada de autobús no tiene banco (nunca lo hubo o lo han quitado)", + "uk": "На цій зупинці немає лавки (ніколи не було або її прибрали)" } } ], @@ -280,7 +281,8 @@ "pt": "Este ponto de ônibus não é mais usado", "pl": "Ten przystanek autobusowy nie jest już używany", "pt_BR": "Essa parada de ônibus não é mais usada", - "es": "Esta parada ya no se usa" + "es": "Esta parada ya no se usa", + "uk": "Ця автобусна зупинка більше не використовується" } } ], diff --git a/assets/layers/bicycle_rental/bicycle_rental.json b/assets/layers/bicycle_rental/bicycle_rental.json index 23f6b044a..c93c1f4a6 100644 --- a/assets/layers/bicycle_rental/bicycle_rental.json +++ b/assets/layers/bicycle_rental/bicycle_rental.json @@ -672,7 +672,8 @@ "cs": "{title()} je trvale uzavřen", "ca": "{title()} ha tancat permanentment", "pt_BR": "{title()} fechou permanentemente", - "pt": "{title()} fechou permanentemente" + "pt": "{title()} fechou permanentemente", + "uk": "{title()} закрито назавжди" }, "changesetMessage": "shop_closed" } @@ -694,7 +695,8 @@ "cs": "Tento obchod s koly dříve půjčoval kola, ale už je nepůjčuje", "ca": "Aquesta botiga de bicicletes solia llogar bicicletes, però ja no", "pt_BR": "Essa loja de bicicletas costumava alugar bicicletas, mas não aluga mais", - "pt": "Esta loja de bicicletas costumava alugar bicicletas, mas ja não as aluga" + "pt": "Esta loja de bicicletas costumava alugar bicicletas, mas ja não as aluga", + "uk": "У цьому веломагазині раніше видавали велосипеди напрокат, але більше не видають" } } ] diff --git a/assets/layers/bike_parking/bike_parking.json b/assets/layers/bike_parking/bike_parking.json index 1855ba666..0d2d3121b 100644 --- a/assets/layers/bike_parking/bike_parking.json +++ b/assets/layers/bike_parking/bike_parking.json @@ -675,7 +675,8 @@ "if": "access=private", "then": { "en": "Private bicycle parking which is never available to the public, also not via a membership fee", - "nl": "Private fietsenstalling die niet voor het grote publiek toegankelijk is (dus ook niet met bv. een abonnement)" + "nl": "Private fietsenstalling die niet voor het grote publiek toegankelijk is (dus ook niet met bv. een abonnement)", + "de": "Private Fahrradabstellplätze, die nie der Öffentlichkeit zur Verfügung stehen, auch nicht über einen Mitgliedsbeitrag" } } ], @@ -812,7 +813,8 @@ "en": "What is the website number of the operator of this bicycle parking?", "nl": "Wat is de website van de operator van deze fietsenstalling?", "de": "Wie lautet die Nummer auf der Website des Betreibers dieses Fahrradparkplatzes?", - "es": "¿Cuál es la página web del operador de este aparcamiento de bicicletas?" + "es": "¿Cuál es la página web del operador de este aparcamiento de bicicletas?", + "uk": "Яка адреса веб-сайту оператора цієї велопарковки?" }, "icon": "./assets/layers/icons/website.svg", "freeform": { diff --git a/assets/layers/bike_repair_station/bike_repair_station.json b/assets/layers/bike_repair_station/bike_repair_station.json index 932dad59d..cc25cdbd9 100644 --- a/assets/layers/bike_repair_station/bike_repair_station.json +++ b/assets/layers/bike_repair_station/bike_repair_station.json @@ -382,7 +382,7 @@ "nl": "Een fietspomp en gereedschap om je fiets te herstellen in de publieke ruimte. Deze zijn op een vastgemaakt, bijvoorbeeld aan een paal.", "fr": "Un dispositif avec des outils pour réparer votre vélo combiné à une pompe a un emplacement fixe. Les outils sont souvent attachés par une chaîne pour empêcher le vol.", "it": "Un dispositivo con attrezzi per riparare la tua bici e una pompa in un luogo fisso. Gli attrezzi sono spesso attaccati ad una catena per prevenire il furto.", - "de": "Ein Gerät mit Werkzeugen zur Reparatur von Fahrrädern kombiniert mit einer Pumpe an einem festen Standort. Die Werkzeuge sind oft mit Ketten gegen Diebstahl gesichert.", + "de": "Ein Gerät mit Werkzeug zur Reparatur von Fahrrädern kombiniert mit einer Pumpe an einem festen Standort. Das Werkzeug ist oft mit Ketten gegen Diebstahl gesichert.", "es": "Una bomba de bicicletas y herramientas para reparar tu bicicleta en el espacio público. Las herramientas habitualmente están aseguradas con cadenas contra el robo.", "da": "En cykelpumpe og værktøj til at reparere din cykel i det offentlige rum. Værktøjet er ofte sikret med kæder mod tyveri.", "cs": "Vzduchový kompresor na kolo a nářadí na opravu kola na veřejném místě. Nářadí je často zajištěno řetězy proti krádeži.", @@ -417,7 +417,7 @@ "description": { "en": "Tools to repair your bike in the public space (without pump). The tools are secured against theft.", "nl": "Gereedschap om je fiets te herstellen in de publieke ruimte (zonder pomp). Deze zijn op een vastgemaakt, bijvoorbeeld aan een paal.", - "de": "Werkzeug, um Ihr Fahrrad im öffentlichen Raum zu reparieren (ohne Pumpe). Die Werkzeuge sind gegen Diebstahl gesichert.", + "de": "Werkzeug, um Ihr Fahrrad im öffentlichen Raum zu reparieren (ohne Pumpe). Das Werkzeug ist gegen Diebstahl gesichert.", "es": "Herramientas para reparar tu bici en el espacio público (sin bomba).Las herramientas están aseguradas contra el robo.", "da": "Værktøj til at reparere din cykel i det offentlige rum (uden pumpe). Værktøjet er sikret mod tyveri.", "fr": "Des outils pour réparer les vélos dans l’espace public (sans pompe). Les outils sont sécurisés contre le vol.", @@ -881,7 +881,8 @@ "es": "Hola,\n\nCon este correo, me gustaría informar de que esta bomba para bicicletas situada en https://mapcomplete.org/cyclofix?lat={_lat}&lon={_lon}&z=18#{id} está rota.\n\nUn saludo", "fr": "Bonjour,\n\nCe mail pour vous informer que la pompe à vélo située à https://mapcomplete.org/cyclofix?lat={_lat}&lon={_lon}&z=18#{id} est cassée.\n\nBien à vous.", "cs": "Dobrý den,\n\ntímto e-mailem bych Vás chtěl informovat, že vzduchový kompresor na kolo umístěný na adrese https://mapcomplete.org/cyclofix?lat={_lat}&lon={_lon}&z=18#{id} je rozbitý.\n\n S pozdravem", - "ca": "Hola,\n\nAmb aquest correu electrònic, m'agradaria informar-vos que la bomba de bicicleta situada a https://mapcomplete.org/cyclofix?lat={_lat}&lon={_lon}&z=18#{id} està trencada.\n\nSalutacions cordials" + "ca": "Hola,\n\nAmb aquest correu electrònic, m'agradaria informar-vos que la bomba de bicicleta situada a https://mapcomplete.org/cyclofix?lat={_lat}&lon={_lon}&z=18#{id} està trencada.\n\nSalutacions cordials", + "uk": "Доброго дня,\n\nЦим листом хочу повідомити, що велосипедний насос, розташований за адресою https://mapcomplete.org/cyclofix?lat={_lat}&lon={_lon}&z=18#{id}, вийшов з ладу.\n\n З повагою" }, "button_text": { "en": "Report this bicycle pump as broken", diff --git a/assets/layers/cafe_pub/cafe_pub.json b/assets/layers/cafe_pub/cafe_pub.json index cb09fcbd6..cd6853643 100644 --- a/assets/layers/cafe_pub/cafe_pub.json +++ b/assets/layers/cafe_pub/cafe_pub.json @@ -265,18 +265,20 @@ "da": "Hvad hedder denne pub?", "es": "¿Cuál es el nombre de esta empresa?", "ca": "Quin és el nom d'aquest negoci?", - "cs": "Jak se tento podnik jmenuje?" + "cs": "Jak se tento podnik jmenuje?", + "uk": "Як називається цей бізнес?" }, "render": { "nl": "De naam van deze zaak is {name}", "en": "This business is named {name}", "de": "Das Unternehmen heißt {name}", "fr": "Ce pub se nomme {name}", - "hu": "A kocsma neve: {name}", + "hu": "A vállalkozás neve: {name}", "da": "Denne pub hedder {name}", "es": "Esta empresa se llama {name}", "ca": "Aquest negoci es diu {name}", - "cs": "Tento podnik se jmenuje {name}" + "cs": "Tento podnik se jmenuje {name}", + "uk": "Цей бізнес називається {name}" }, "freeform": { "key": "name" @@ -290,11 +292,12 @@ "de": "Was ist das für ein Café?", "hu": "Milyen fajta kávézó ez?", "da": "Hvilken slags cafe er dette?", - "es": "Qué tipo de cafe es este?", + "es": "¿Qué tipo de café es este?", "fr": "Quel genre de café est-ce ?", "ca": "Quin tipus de cafeteria és aquesta?", "pl": "Jakiego rodzaju jest to kawiarnia?", - "cs": "O jakou kavárnu se jedná?" + "cs": "O jakou kavárnu se jedná?", + "uk": "Що це за кафе?" }, "mappings": [ { @@ -307,7 +310,8 @@ "fr": "Un pub, principalement pour boire un verre dans une atmosphère chaleureuse et décontractée", "da": "En pub, mest et sted at drikke øl i hyggelige, afslappede omgivelser", "ca": "Un bar, principalment per a beure cerveses en un interior càlid i relaxat", - "cs": "Hospoda, kde se většinou pije pivo v příjemném a uvolněném interiéru" + "cs": "Hospoda, kde se většinou pije pivo v příjemném a uvolněném interiéru", + "uk": "Паб, переважно для пиття пива в теплому, розслабленому інтер'єрі" } }, { @@ -320,7 +324,8 @@ "es": "Un bar de copas más moderno y comercial, posiblemente con una instalación de música y luz", "fr": "Un bar plus moderne et commercial, avec éventuellement musique et jeux de lumière", "ca": "Un bar de copes més modern i comercial, possiblement amb equipació de música i llums", - "cs": "Modernější a komerčnější bar, případně s hudební a světelnou instalací" + "cs": "Modernější a komerčnější bar, případně s hudební a světelnou instalací", + "uk": "Більш сучасний і комерційний бар, можливо, з музичною та світловою інсталяцією" } }, { @@ -333,7 +338,8 @@ "es": "Una cafetería para beber té, café o una bebida alcohólica en un ambiente tranquilo", "fr": "Un café pour boire du thé, du café ou une boisson alcoolisée au calme", "ca": "Una cafeteria per a a beure té, café o una beguda alcohólica en un ambient tranquil", - "cs": "kavárna, kde si můžete v klidném prostředí vypít čaj, kávu nebo alkoholický nápoj" + "cs": "kavárna, kde si můžete v klidném prostředí vypít čaj, kávu nebo alkoholický nápoj", + "uk": "Кафе, щоб випити чаю, кави чи алкогольного напою в тихій обстановці" } }, { @@ -346,7 +352,8 @@ "es": "Un restaurante donde se pueda comer como es debido", "fr": "Un restaurant où l'on peut prendre un bon repas", "ca": "Un restaurant on es pot menjar bé", - "cs": "Restaurace, kde se dá pořádně najíst" + "cs": "Restaurace, kde se dá pořádně najíst", + "uk": "Ресторан де можна смачно поїсти" } }, { @@ -373,7 +380,8 @@ "fr": "Une boîte de nuit ou discothèque orientée danse, la musique d'un DJ avec un spectacle de lumière et un bar pour prendre des boissons (alcoolisées)", "da": "Dette er en natklub eller diskotek med fokus på dans, musik af en DJ med tilhørende lysshow og en bar, hvor man kan købe (alkoholiske) drikkevarer", "ca": "Açò és un club nocturn o discoteca centrat en ballar, música d'un DJ acompanyat d'un espectacle de llums i una barra on obtindre begudes (alcohòliques)", - "cs": "Jedná se o noční klub nebo diskotéku se zaměřením na tanec, hudbu DJ s doprovodnou světelnou show a bar, kde si můžete dát (alkoholické) nápoje" + "cs": "Jedná se o noční klub nebo diskotéku se zaměřením na tanec, hudbu DJ s doprovodnou světelnou show a bar, kde si můžete dát (alkoholické) nápoje", + "uk": "Це нічний клуб або дискотека з акцентом на танці, музикою від ді-джея з супроводом світлового шоу та баром, щоб отримати (алкогольні) напої" } } ], @@ -417,7 +425,8 @@ "da": "{title()} er lukket permanent", "fr": "{title()} est définitivement fermé", "ca": "{title()} ha tancat permanentment", - "cs": "{title()} byla trvale uzavřena" + "cs": "{title()} byla trvale uzavřena", + "uk": "{title()} закрито назавжди" }, "changesetMessage": "shop_closed" } diff --git a/assets/layers/caravansites/caravansites.json b/assets/layers/caravansites/caravansites.json index 2c02f7130..e35151a1a 100644 --- a/assets/layers/caravansites/caravansites.json +++ b/assets/layers/caravansites/caravansites.json @@ -13,7 +13,7 @@ "hu": "Lakóautós megállóhely", "id": "Tempat camping", "ca": "Llocs d'acampada", - "es": "Sitios de Acampada", + "es": "Zonas de acampada", "da": "Campingpladser", "pa_PK": "کیمب‌سائیٹاں", "cs": "Kempovací místa", diff --git a/assets/layers/crossings/crossings.json b/assets/layers/crossings/crossings.json index fd5fe6e66..2cd743b98 100644 --- a/assets/layers/crossings/crossings.json +++ b/assets/layers/crossings/crossings.json @@ -509,7 +509,8 @@ "nl": "Heeft dit verkeerslicht een element dat trilt om te helpen bij het oversteken? (meestal onderaan de oversteekknop geplaatst)", "fr": "Est-ce que ce feu a un signal vibrant pour aider à traverser ? (habituellement situé sous le bouton)", "ca": "Aquest semàfor té senyals de vibració per facilitar el pas? (normalment es troba a la part inferior del botó d'encreuament)", - "cs": "Má tento semafor vibrační signály pro usnadnění přecházení? (obvykle se nachází ve spodní části tlačítka pro přecházení)" + "cs": "Má tento semafor vibrační signály pro usnadnění přecházení? (obvykle se nachází ve spodní části tlačítka pro přecházení)", + "uk": "Чи має цей світлофор вібраційні сигнали для полегшення переходу? (зазвичай розташовані внизу кнопки переходу)" }, "condition": { "and": [ diff --git a/assets/layers/cycleways_and_roads/cycleways_and_roads.json b/assets/layers/cycleways_and_roads/cycleways_and_roads.json index ea8a549b0..96f1e5fd6 100644 --- a/assets/layers/cycleways_and_roads/cycleways_and_roads.json +++ b/assets/layers/cycleways_and_roads/cycleways_and_roads.json @@ -823,7 +823,7 @@ "then": { "en": "There is (probably) no incline here", "de": "Hier gibt es (wahrscheinlich) keine Steigung", - "nl": "De straat hier is (waarschijnlijk) zonder helling" + "nl": "De straat heeft hier (waarschijnlijk) geen helling" }, "hideInAnswer": true }, diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index 5399a9403..d8a9ce2e3 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -527,7 +527,8 @@ "de": "Dies ist ein Zierbrunnen, dessen Wasser für den Menschen nicht trinkbar ist", "es": "Esta es una fuente decorativa con agua no potable", "ca": "Es tracta d'una font decorativa amb aigua no potable", - "cs": "Jedná se o dekorativní fontánu, jejíž voda není pro lidi pitná" + "cs": "Jedná se o dekorativní fontánu, jejíž voda není pro lidi pitná", + "uk": "Це декоративний фонтан, вода з якого не придатна для пиття" } }, { @@ -544,7 +545,8 @@ "de": "Dies ist ein Wasserhahn oder eine Wasserpumpe mit nicht trinkbarem Wasser.
Beispiele sind Wasserhähne mit Regenwasser zum Zapfen von Wasser für nahe gelegene Pflanzen
", "es": "Este es un grifo de agua o una bomba de agua con agua no potable.
Ejemplos son grifos con agua de lluvia o agua del grifo para plantas cercanas
", "ca": "Es tracta d'una aixeta d'aigua o bomba d'aigua amb aigua no potable.
Per exemple les aixetes d'aigua amb aigua de pluja per aprofitar i regar les plantes properes
", - "cs": "Jedná se o vodovodní kohoutek nebo vodní čerpadlo s nepitnou vodou.
Příkladem jsou vodovodní kohoutky s dešťovou vodou pro zalévání rostlin v okolí
" + "cs": "Jedná se o vodovodní kohoutek nebo vodní čerpadlo s nepitnou vodou.
Příkladem jsou vodovodní kohoutky s dešťovou vodou pro zalévání rostlin v okolí
", + "uk": "Це водопровідний кран або водяний насос з непитною водою.
Прикладами є водопровідні крани з дощовою водою для поливу прилеглих рослин
" } }, { diff --git a/assets/layers/elongated_coin/elongated_coin.json b/assets/layers/elongated_coin/elongated_coin.json index e56b47ddf..b05213fba 100644 --- a/assets/layers/elongated_coin/elongated_coin.json +++ b/assets/layers/elongated_coin/elongated_coin.json @@ -416,7 +416,8 @@ "de": "Der Prägeautomat befindet sich im Inneren.", "es": "Esta prensa está ubicada en interior.", "ca": "Esta premsa està ubicada en interior.", - "cs": "Tento lis na mince je umístěn v interiéru." + "cs": "Tento lis na mince je umístěn v interiéru.", + "uk": "Цей копійчаний прес розташований у приміщенні." } }, { @@ -426,7 +427,8 @@ "de": "Der Prägeautomat befindet sich im Freien.", "es": "Esta prensa está ubicada al aire libre.", "ca": "Esta premsa està ubicada a l'exterior.", - "cs": "Tento lis na mince je umístěn venku." + "cs": "Tento lis na mince je umístěn venku.", + "uk": "Цей копійчаний прес розташований на відкритому повітрі." } } ] diff --git a/assets/layers/fire_station/fire_station.json b/assets/layers/fire_station/fire_station.json index 324538279..130827c4e 100644 --- a/assets/layers/fire_station/fire_station.json +++ b/assets/layers/fire_station/fire_station.json @@ -96,7 +96,8 @@ "nl": "Een brandweerstation is een plaats waar brandweerwagens en brandweerlieden gebaseerd zijn.", "es": "Un parque de bomberos es donde los bomberos y los camiones de incendios se encuentran cuando no están en uso.", "ca": "Un parc de bombers és on els bombers i els camions es troben quan no estan en ús.", - "cs": "Hasičská zbrojnice je místo, kde se nacházejí hasičská vozidla a hasiči." + "cs": "Hasičská zbrojnice je místo, kde se nacházejí hasičská vozidla a hasiči.", + "uk": "Пожежна станція - це місце, де знаходяться пожежні машини та пожежники, коли вони не працюють." } } ], @@ -146,7 +147,8 @@ "nl": " Aan welke straat ligt dit station?", "de": " In welcher Straße ist die Feuerwache?", "ca": " Quin és el nom del carrer on es troba aquesta estació?", - "cs": " Jak se jmenuje ulice, kde se stanice nachází?" + "cs": " Jak se jmenuje ulice, kde se stanice nachází?", + "uk": " Як називається вулиця, на якій розташована станція?" }, "render": { "en": "This station is along a highway called {addr:street}.", @@ -171,7 +173,8 @@ "nl": "Waar is dit station gelegen? (v.b. naam van de buurt, dorp of stad)", "de": "Wo befindet sich die Station? (z. B. Name des Viertels, des Dorfes oder der Stadt)", "ca": "On es troba aquesta estació? (p.e. nom del barri, poble o ciutat)", - "cs": "Kde se stanice nachází? (např. název čtvrti, obce nebo města)" + "cs": "Kde se stanice nachází? (např. název čtvrti, obce nebo města)", + "uk": "Де розташована станція? (наприклад, назва району, села чи міста)" }, "freeform": { "key": "addr:place" diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index 61725d677..27a2eb33b 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -258,7 +258,8 @@ "es": "¿Cuál es el nombre de este negocio?", "fr": "Quel est le nom de ce restaurant ?", "ca": "Quin és el nom d'aquest negoci?", - "cs": "Jak se tento podnik jmenuje?" + "cs": "Jak se tento podnik jmenuje?", + "uk": "Як називається цей бізнес?" }, "render": { "nl": "De naam van deze eetgelegeheid is {name}", @@ -267,7 +268,8 @@ "es": "El nombre de este negocio es {name}", "fr": "Le nom de ce restaurant est {name}", "ca": "El nom d'aquest negoci és {name}", - "cs": "Název tohoto podniku je {name}" + "cs": "Název tohoto podniku je {name}", + "uk": "Цей бізнес називається {name}" }, "freeform": { "key": "name" @@ -282,7 +284,8 @@ "es": "¿Qué tipo de negocio es este?", "fr": "Quel type de restaurant est-ce ?", "ca": "Quin tipus de negoci és aquest?", - "cs": "O jaký typ podnikání se jedná?" + "cs": "O jaký typ podnikání se jedná?", + "uk": "Що це за бізнес?" }, "mappings": [ { @@ -293,9 +296,10 @@ "de": "Es handelt sich um einen Schnellimbiss, mit Fokus auf schnelle Bedienung am Tresen. Sitzmöglichkeiten sind begrenzt und funktional.", "es": "Se trata de un negocio de comida rápida, centrado en el servicio rápido. Si hay asientos disponibles, son más bien limitados y funcionales.", "fr": "C'est un fast-food, centrée sur le service rapide. Si des places sont disponibles, elles sont plutôt limitées et fonctionnelles.", - "hu": "Ez egy gyorsétterem (büfé), amely a gyors kiszolgálásra összpontosít. Ha vannak is ülőhelyek, ezek meglehetősen korlátozottak és funkcionálisak.", + "hu": "Ez egy gyorsétterem (büfé), amely a gyors kiszolgálásra összpontosít. Ha van is ülőhely, az meglehetősen korlátozott és funkcionális.", "ca": "Aquest és un negoci de menjar ràpid, centrat en el servei ràpid. Si hi han seients disponibles, aquests seran limitats i funcionals.", - "cs": "Jedná se o podnik s rychlým občerstvením, zaměřený na rychlou obsluhu. Pokud je k dispozici místo k sezení, je spíše omezené a funkční." + "cs": "Jedná se o podnik s rychlým občerstvením, zaměřený na rychlou obsluhu. Pokud je k dispozici místo k sezení, je spíše omezené a funkční.", + "uk": "Це бізнес швидкого харчування, орієнтований на швидке обслуговування. Якщо місця для сидіння і є, то вони досить обмежені та функціональні." } }, { @@ -307,7 +311,8 @@ "es": "Un restaurante, centrado en crear una experiencia agradable en la que uno se sirve en mesa", "fr": "Un restaurant, axé sur la création d'une expérience agréable où l'on est servi à table", "ca": "Un restaurant, centrat en crear una bona experiència on es serveix a taula", - "cs": "Restaurace zaměřená na vytvoření příjemného zážitku, kde se obsluhuje u stolu" + "cs": "Restaurace zaměřená na vytvoření příjemného zážitku, kde se obsluhuje u stolu", + "uk": "Ресторан, зосереджений на створенні приємних вражень, де обслуговують за столом" } } ], diff --git a/assets/layers/ghost_bike/ghost_bike.json b/assets/layers/ghost_bike/ghost_bike.json index 32cc6947b..5f5fdbcce 100644 --- a/assets/layers/ghost_bike/ghost_bike.json +++ b/assets/layers/ghost_bike/ghost_bike.json @@ -255,7 +255,8 @@ "it": "Che cosa è scritto sulla bici fantasma?", "fr": "Quelle est l'inscription sur ce vélo fantôme ?", "ca": "Quina és la inscripció d'aquesta bicicleta fantasma?", - "cs": "Jaký je nápis na tomto kole duchů?" + "cs": "Jaký je nápis na tomto kole duchů?", + "uk": "Який напис на цьому примарному велосипеді?" }, "render": { "en": "{inscription}", @@ -267,7 +268,8 @@ "ru": "{inscription}", "id": "{inscription}", "cs": "{inscription}", - "pl": "{inscription}" + "pl": "{inscription}", + "uk": "{напис}" }, "freeform": { "key": "inscription" diff --git a/assets/layers/ghostsign/ghostsign.json b/assets/layers/ghostsign/ghostsign.json index 3e25ee744..8bdfcaf2a 100644 --- a/assets/layers/ghostsign/ghostsign.json +++ b/assets/layers/ghostsign/ghostsign.json @@ -45,7 +45,8 @@ "en": "What is the text on the sign?", "de": "Was ist der Text auf dem Schild?", "es": "¿Cuál es el texto del cartel?", - "ca": "Quin és el text del rètol?" + "ca": "Quin és el text del rètol?", + "uk": "Який текст на вивісці?" }, "freeform": { "key": "inscription", @@ -54,13 +55,15 @@ "en": "Text on the sign", "de": "Text auf dem Schild", "es": "Texto en el cartel", - "ca": "Text en el rètol" + "ca": "Text en el rètol", + "uk": "Текст на табличці" } }, "render": { "en": "The text on the sign is: {inscription}", "de": "Der Text auf dem Schild ist: {inscription}", - "es": "El texto del cartel es: {inscription}" + "es": "El texto del cartel es: {inscription}", + "uk": "Текст на табличці: {inscription}" } }, { diff --git a/assets/layers/grave/grave.json b/assets/layers/grave/grave.json index b24307fc0..467c65e7f 100644 --- a/assets/layers/grave/grave.json +++ b/assets/layers/grave/grave.json @@ -46,7 +46,8 @@ ], "description": { "en": "A tombstone is a physical object which indicates that one or more persons are buried here. It generally has the name, birth date and death date of the person or persons as inscription.", - "de": "Ein Grabstein ist ein physischer Gegenstand, der anzeigt, dass eine oder mehrere Personen hier begraben sind. Er trägt in der Regel den Namen, das Geburts- und das Sterbedatum der Person oder Personen als Inschrift." + "de": "Ein Grabstein ist ein physischer Gegenstand, der anzeigt, dass eine oder mehrere Personen hier begraben sind. Er trägt in der Regel den Namen, das Geburts- und das Sterbedatum der Person oder Personen als Inschrift.", + "uk": "Надгробок - це фізичний об'єкт, який вказує на те, що тут похована одна або кілька осіб. Як правило, на ньому викарбувано ім'я, дату народження та дату смерті особи чи осіб." } } ], diff --git a/assets/layers/hackerspace/hackerspace.json b/assets/layers/hackerspace/hackerspace.json index 4e63bcfa1..72a51bec6 100644 --- a/assets/layers/hackerspace/hackerspace.json +++ b/assets/layers/hackerspace/hackerspace.json @@ -197,7 +197,8 @@ "if": "hackerspace=hacklab", "then": { "en": "This is a hacklab which is mostly focussed on basic computer skills, using recycled devices and/or providing internet to the community. This is typically located in autonomous spaces, squats or social facilities", - "de": "Dies ist ein Hackerspace/Hack-Lab, das sich hauptsächlich auf grundlegende Computerkenntnisse konzentriert, recycelte Geräte verwendet und/oder der Gemeinschaft Internet bereitstellt. Es befindet sich typischerweise in autonomen Räumen, besetzten Häusern oder sozialen Einrichtungen" + "de": "Dies ist ein Hackerspace/Hack-Lab, das sich hauptsächlich auf grundlegende Computerkenntnisse konzentriert, recycelte Geräte verwendet und/oder der Gemeinschaft Internet bereitstellt. Es befindet sich typischerweise in autonomen Räumen, besetzten Häusern oder sozialen Einrichtungen", + "uk": "Це хак-лабораторія, яка здебільшого зосереджена на базових комп'ютерних навичках, використовує перероблені пристрої та/або надає доступ до Інтернету громаді. Зазвичай вони розташовані в автономних приміщеннях, сквотах або соціальних об'єктах" } } ] diff --git a/assets/layers/memorial/memorial.json b/assets/layers/memorial/memorial.json index 6e4c23254..11b01626e 100644 --- a/assets/layers/memorial/memorial.json +++ b/assets/layers/memorial/memorial.json @@ -124,7 +124,8 @@ "en": "This is a plaque", "de": "Dies ist eine Gedenktafel", "fr": "C'est une plaque", - "sl": "To je plošča" + "sl": "To je plošča", + "uk": "Це меморіальна дошка" } }, { @@ -283,7 +284,8 @@ "de": "Wie lautet die Inschrift auf diesem Denkmal?", "fr": "Quelle est l'inscription sur cette plaque ?", "nl": "Wat is het opschrift van dit gedenkteken?", - "sl": "Kakšen je napis na tem spomeniku?" + "sl": "Kakšen je napis na tem spomeniku?", + "uk": "Який напис на цьому пам'ятнику?" }, "#:condition": "Benches have a separate inscription question", "condition": { @@ -297,7 +299,8 @@ "cs": "Nápis na této desce zní:

{inscription}

", "de": "Die Inschrift auf diesem Denkmal lautet:

{inscription}

", "fr": "L'inscription sur cette plaque indique:

{inscription}

", - "sl": "Napis na tem spomeniku je:

{inscription}

" + "sl": "Napis na tem spomeniku je:

{inscription}

", + "uk": "Напис на цьому меморіалі:

{inscription}

" }, "freeform": { "key": "inscription", @@ -311,7 +314,8 @@ "de": "Dieses Denkmal hat keine Inschrift", "fr": "Il n'y a aucune inscription sur cette plaque", "nl": "Dit gedenkteken heeft geen opschrift", - "sl": "Ta spomenik nima napisa" + "sl": "Ta spomenik nima napisa", + "uk": "Цей пам'ятник не має напису" }, "addExtraTags": [ "inscription=" diff --git a/assets/layers/nature_reserve/nature_reserve.json b/assets/layers/nature_reserve/nature_reserve.json index 4dae74e07..9630f0cd8 100644 --- a/assets/layers/nature_reserve/nature_reserve.json +++ b/assets/layers/nature_reserve/nature_reserve.json @@ -110,7 +110,8 @@ "nl": "De toegankelijkheid van dit gebied is: {access:description}", "de": "Zugang zu diesem Naturschutzgebiet: {access:description}", "ca": "Accés a aquesta reserva natural: {access:description}", - "cs": "Přístup do této přírodní rezervace: {access:description}" + "cs": "Přístup do této přírodní rezervace: {access:description}", + "uk": "Доступ до цього природного заповідника: {access:description}" }, "question": { "en": "Is this nature reserve accessible to the public?", diff --git a/assets/layers/note/note.json b/assets/layers/note/note.json index ad6e9cf29..6ffe493d0 100644 --- a/assets/layers/note/note.json +++ b/assets/layers/note/note.json @@ -389,7 +389,8 @@ "ca": "Editat per última vega pel contribuïdor {search}", "cs": "Naposledy upravil přispěvatel {search}", "da": "Senest redigeret af bidragsyder {search}", - "fr": "Dernière modification par {search}" + "fr": "Dernière modification par {search}", + "hu": "Legutóbb {search} felhasználó szerkesztette" } } ] @@ -406,7 +407,8 @@ ], "question": { "en": "Not edited as last by {search}", - "de": "Nicht zuletzt bearbeitet von {search}" + "de": "Nicht zuletzt bearbeitet von {search}", + "hu": "Legutóbb nem {search} felhasználó szerkesztette" } } ] @@ -470,7 +472,8 @@ ], "question": { "en": "Last edited before {search}", - "de": "Zuletzt bearbeitet vor dem {search}" + "de": "Zuletzt bearbeitet vor dem {search}", + "hu": "Legutóbb {search} előtt szerkesztették" } } ] @@ -488,7 +491,8 @@ ], "question": { "en": "Last edited after {search}", - "de": "Zuletzt bearbeitet nach dem {search}" + "de": "Zuletzt bearbeitet nach dem {search}", + "hu": "Legutóbb {search} után szerkesztették" } } ] diff --git a/assets/layers/questions/questions.json b/assets/layers/questions/questions.json index bb6cdb40c..f5fbda128 100644 --- a/assets/layers/questions/questions.json +++ b/assets/layers/questions/questions.json @@ -361,7 +361,8 @@ "es": "¿Cual es el sitio web de {title()}?", "zh_Hans": "{title()} 的网站为何?", "da": "Hvad er webstedet for {title()}?", - "cs": "Jaká je webová stránka {title()}?" + "cs": "Jaká je webová stránka {title()}?", + "uk": "Який веб-сайт має {title()}?" }, "render": { "*": "{website}" @@ -760,7 +761,8 @@ "zh_Hans": "{title()} 的开放时间为何?", "da": "Hvad er åbningstiderne for {title()}?", "id": "Kapan jam buka {title()}?", - "cs": "Jaká je otevírací doba {title()}?" + "cs": "Jaká je otevírací doba {title()}?", + "uk": "Які години роботи {title()}?" }, "render": { "de": "

Öffnungszeiten

{opening_hours_table(opening_hours)}", @@ -1768,7 +1770,8 @@ "es": "¿En qué nivel se encuentra esta característica?", "nb_NO": "Hvilken etasje befinner funksjonen seg i?", "da": "På hvilket niveau er denne funktion placeret?", - "cs": "V jaké úrovni se tento prvek nachází?" + "cs": "V jaké úrovni se tento prvek nachází?", + "uk": "На якому поверсі знаходиться цей об'єкт?" }, "render": { "en": "Located on the {level}th floor", @@ -1792,7 +1795,8 @@ "da": "Beliggende på {level}. etage", "nb_NO": "Ligger i {level} etasje", "id": "Terletak di lantai {level}", - "cs": "Nachází se v {level}. patře" + "cs": "Nachází se v {level}. patře", + "uk": "Розташований на {level} поверсі" }, "freeform": { "key": "level", @@ -1822,7 +1826,8 @@ "fil": "Nasa ilalim ng lupa", "es": "Localizado bajo tierra", "da": "Placeret under jorden", - "cs": "Nachází se v podzemí" + "cs": "Nachází se v podzemí", + "uk": "Розташований під землею" }, "hideInAnswer": true }, @@ -1850,7 +1855,8 @@ "fil": "Nasa unang palapag", "es": "Localizado en la planta baja", "da": "Beliggende i stueetagen", - "cs": "Nachází se v přízemí" + "cs": "Nachází se v přízemí", + "uk": "Розташований на рівні землі" } }, { @@ -1878,7 +1884,8 @@ "fil": "Nasa unang palapag", "es": "Localizado en la planta baja", "da": "Beliggende i stueetagen", - "cs": "Nachází se v přízemí" + "cs": "Nachází se v přízemí", + "uk": "Розташований на рівні землі" } }, { @@ -1905,7 +1912,8 @@ "fil": "Nasa unang palapag", "es": "Localizado en la primera planta", "da": "Beliggende på første sal", - "cs": "Nachází se v prvním patře" + "cs": "Nachází se v prvním patře", + "uk": "Розташований на другому поверсі" } }, { @@ -1926,7 +1934,8 @@ "pt": "Localizado no primeiro nível da cave", "da": "Beliggende på første kælderetage", "cs": "Nachází se v prvním suterénu", - "pl": "Położone na pierwszym poziomie piwnicy" + "pl": "Położone na pierwszym poziomie piwnicy", + "uk": "Розташований на першому підвальному поверсі" } } ] @@ -2327,7 +2336,8 @@ "es": "El nombre de red es {internet_access:ssid}", "fr": "Le nom du réseau est {internet_access:ssid}", "ca": "El nom de la xarxa és {internet_access:ssid}", - "pl": "Nazwa sieci to {internet_access:ssid}" + "pl": "Nazwa sieci to {internet_access:ssid}", + "uk": "Назва мережі: {internet_access:ssid}" } }, { @@ -2781,7 +2791,8 @@ "nl": "Scan deze code om deze locatie op een ander apparaat te zien", "de": "QR Code scannen, um diesen Ort auf einem anderen Gerät zu öffnen", "sl": "Skenirajte to kodo, da odprete ta kraj na drugi napravi", - "da": "Skan denne kode for at åbne dette sted på en anden enhed" + "da": "Skan denne kode for at åbne dette sted på en anden enhed", + "hu": "Szkenneld be ezt a kódot, hogy egy másik eszközön is meg tudd nyitni a helyet" } } }, @@ -2816,7 +2827,8 @@ "de": "Zuletzt bearbeitet am {_last_edit:timestamp} von {_last_edit:contributor}", "ca": "Darrera edició el {_last_edit:timestamp} per {_last_edit:contributor}", "cs": "Naposledy upravil {_last_edit:timestamp} {_last_edit:contributor}", - "da": "Senest redigeret {_last_edit:timestamp} af {_last_edit:contributor}" + "da": "Senest redigeret {_last_edit:timestamp} af {_last_edit:contributor}", + "hu": "Legutóbb {_last_edit:contributor} szerkesztette ekkor: {_last_edit:timestamp}" }, "class": "subtle font-small" } diff --git a/assets/layers/recycling/recycling.json b/assets/layers/recycling/recycling.json index 17d64056a..d35229ee4 100644 --- a/assets/layers/recycling/recycling.json +++ b/assets/layers/recycling/recycling.json @@ -595,7 +595,8 @@ "it": "Che tipo di raccolta è questo?", "ca": "Quin tipus de reciclatge és aquest?", "fr": "De quel type d’installation de recyclage s’agit-il ?", - "cs": "O jaký typ recyklace se jedná?" + "cs": "O jaký typ recyklace se jedná?", + "uk": "Що це за тип переробки?" }, "mappings": [ { @@ -611,7 +612,8 @@ "it": "Questo è un contenitore per il riciclo di rifiuti", "ca": "Açò és un contenidor de reciclatge", "fr": "C'est un conteneur de recyclage", - "cs": "Jedná se o recyklační kontejner" + "cs": "Jedná se o recyklační kontejner", + "uk": "Це контейнер для вторинної переробки" } }, { @@ -627,7 +629,8 @@ "it": "Questo è un centro per la raccola e riciclo di rifiuti", "ca": "Açò és un centre de reciclatge", "fr": "C'est une déchèterie", - "cs": "Jedná se o recyklační centrum" + "cs": "Jedná se o recyklační centrum", + "uk": "Це центр переробки сміття" } }, { @@ -639,7 +642,8 @@ "it": "Contenitore per lo smaltimento del secco", "ca": "Contenidor per a residus no reciclables (fracció resta)", "fr": "Conteneur à déchets pour déchets résiduels", - "cs": "Kontejner na zbytkový odpad" + "cs": "Kontejner na zbytkový odpad", + "uk": "Контейнер для утилізації залишкових відходів" }, "addExtraTags": [ "recycling:batteries=", @@ -679,7 +683,8 @@ "ca": "Aquest és un punt de recollida. El rebuig es col·loca aquí sense col·locar-lo en un contenidor dedicat.", "de": "Dies ist ein Abholpunkt. Abfälle werden hier abgelegt, ohne sie in einen speziellen Behälter zu geben.", "fr": "C'est un point de ramassage. Les déchets y sont déposés sans être placés dans un conteneur spécifique.", - "cs": "Jedná se o místo vyzvednutí. Odpadní materiál se zde ukládá bez umístění do vyhrazeného kontejneru." + "cs": "Jedná se o místo vyzvednutí. Odpadní materiál se zde ukládá bez umístění do vyhrazeného kontejneru.", + "uk": "Це пункт прийому відходів. Відходи розміщують тут, не поміщаючи їх у спеціальний контейнер." } }, { @@ -693,7 +698,8 @@ "ca": "Es tracta d’un abocador on s’apilen els residus.", "de": "Dies ist eine Deponie, auf der die Abfälle gelagert werden.", "fr": "C'est une décharge où les déchets sont empilés.", - "cs": "Jedná se o skládku, kde se ukládá odpadní materiál." + "cs": "Jedná se o skládku, kde se ukládá odpadní materiál.", + "uk": "Це звалище, куди складають відходи." } } ] @@ -708,7 +714,8 @@ "it": "Come si chiama questo centro raccolta e riciclo rifiuti?", "ca": "Quin és el nom d’aquest centre de reciclatge?", "fr": "Quel est le nom de cette déchèterie ?", - "cs": "Jak se toto recyklační centrum jmenuje?" + "cs": "Jak se toto recyklační centrum jmenuje?", + "uk": "Як називається цей центр переробки?" }, "render": { "en": "This recycling centre is named {name}", @@ -718,7 +725,8 @@ "it": "Questo centro raccolta e riciclo rifiuti si chiama {name}", "ca": "Aquest centre de reciclatge s'anomena {name}", "fr": "Le nom de cette déchèterie est {name}", - "cs": "Toto recyklační středisko se jmenuje {name}" + "cs": "Toto recyklační středisko se jmenuje {name}", + "uk": "Цей центр переробки має назву {name}" }, "freeform": { "key": "name" @@ -734,7 +742,8 @@ "it": "Questo centro raccolta e riciclo rifiuti non ha un nome specifico", "ca": "Aquest centre de reciclatge no té un nom específic", "fr": "Cette déchèterie n'a pas de nom spécifique", - "cs": "Toto recyklační centrum nemá konkrétní název" + "cs": "Toto recyklační centrum nemá konkrétní název", + "uk": "Цей центр переробки не має конкретної назви" } } ], @@ -777,7 +786,8 @@ "it": "Questo contenitore è al chiuso", "ca": "Aquest contenidor està situat a l'interior", "fr": "Ce conteneur est situé en intérieur", - "cs": "Tento kontejner je umístěn uvnitř" + "cs": "Tento kontejner je umístěn uvnitř", + "uk": "Цей контейнер знаходиться в приміщенні" } }, { @@ -790,7 +800,8 @@ "it": "Questo contenitore è all'aperto", "ca": "Aquest contenidor està situat a l'aire lliure", "fr": "Ce conteneur est situé à l'extérieur", - "cs": "Tento kontejner je umístěn venku" + "cs": "Tento kontejner je umístěn venku", + "uk": "Цей контейнер розташований на відкритому повітрі" } } ] @@ -805,7 +816,8 @@ "it": "Cosa si può riciclare qui?", "ca": "Què es pot reciclar aquí?", "fr": "Que peut-on recycler ici ?", - "cs": "Co se zde dá recyklovat?" + "cs": "Co se zde dá recyklovat?", + "uk": "Що тут можна переробити?" }, "multiAnswer": true, "mappings": [ @@ -820,7 +832,8 @@ "it": "Batterie", "ca": "Aquí es poden reciclar piles", "fr": "Les piles et batteries domestiques peuvent être recyclées ici", - "cs": "Baterie se zde dají recyklovat" + "cs": "Baterie se zde dají recyklovat", + "uk": "Батарейки" }, "icon": { "path": "./assets/layers/recycling/batteries.svg", @@ -837,7 +850,8 @@ "it": "Cartoni per bevande", "ca": "Aquí es poden reciclar els cartons de begudes", "fr": "Les briques alimentaires en carton peuvent être recyclées ici", - "cs": "Nápojové kartony se zde dají recyklovat" + "cs": "Nápojové kartony se zde dají recyklovat", + "uk": "Коробки з-під напоїв" }, "icon": { "path": "./assets/layers/recycling/beverage_cartons.svg", @@ -855,7 +869,8 @@ "it": "Lattine", "ca": "Aquí es poden reciclar llaunes", "fr": "Les boites de conserve et canettes peuvent être recyclées ici", - "cs": "Plechovky se zde dají recyklovat" + "cs": "Plechovky se zde dají recyklovat", + "uk": "Бляшанки" }, "icon": { "path": "./assets/layers/recycling/cans.svg", @@ -873,7 +888,8 @@ "it": "Abiti", "ca": "Aquí es pot reciclar roba", "fr": "Les vêtements peuvent être recyclés ici", - "cs": "Oblečení se zde dá recyklovat" + "cs": "Oblečení se zde dá recyklovat", + "uk": "Одяг" }, "icon": { "path": "./assets/layers/recycling/clothes.svg", @@ -891,7 +907,8 @@ "it": "Olio da cucina", "ca": "Aquí es pot reciclar oli de cuina", "fr": "Les huiles de friture peuvent être recyclées ici", - "cs": "Olej na vaření zde lze recyklovat" + "cs": "Olej na vaření zde lze recyklovat", + "uk": "Олія для приготування їжі" }, "icon": { "path": "./assets/layers/recycling/cooking_oil.svg", @@ -909,7 +926,8 @@ "it": "Olio di motore", "ca": "Aquí es pot reciclar oli de motor", "fr": "Les huiles de moteur peuvent être recyclées ici", - "cs": "Motorový olej zde lze recyklovat" + "cs": "Motorový olej zde lze recyklovat", + "uk": "Моторна олива" }, "icon": { "path": "./assets/layers/recycling/engine_oil.svg", @@ -925,7 +943,8 @@ "de": "Hier können Leuchtstoffröhren recycelt werden", "ca": "Aquí es poden reciclar tub fluroescents", "fr": "Les lampes fluorescentes peuvent être recyclées ici", - "cs": "Zářivky se zde dají recyklovat" + "cs": "Zářivky se zde dají recyklovat", + "uk": "Флуоресцентні лампи" }, "icon": { "path": "./assets/layers/recycling/fluorescent_tubes.svg", @@ -943,7 +962,8 @@ "ca": "Aquí es poden reciclar residus verds", "fr": "Les déchets verts peuvent être recyclés ici", "cs": "Zelený odpad se zde dá recyklovat", - "es": "Los residuos orgánicos pueden reciclarse aquí" + "es": "Los residuos orgánicos pueden reciclarse aquí", + "uk": "Зелені відходи" }, "icon": { "path": "./assets/layers/recycling/garden_waste.svg", @@ -980,7 +1000,8 @@ "it": "Bottiglie di vetro", "ca": "Aquí es poden reciclar ampolles de vidre", "fr": "Les bouteilles en verre et bocaux peuvent être recyclés ici", - "cs": "Skleněné lahve se zde dají recyklovat" + "cs": "Skleněné lahve se zde dají recyklovat", + "uk": "Скляні пляшки" }, "icon": { "path": "./assets/layers/recycling/glass_bottles.svg", @@ -998,7 +1019,8 @@ "it": "Vetro", "ca": "Aquí es pot reciclar vidre", "fr": "Tout type de verre peut être recyclé ici", - "cs": "Sklo se zde dá recyklovat" + "cs": "Sklo se zde dá recyklovat", + "uk": "Скло" }, "icon": { "path": "./assets/layers/recycling/glass.svg", @@ -1015,7 +1037,8 @@ "ca": "Aquí es poden reciclar bombetes", "fr": "Les lampes basse consommation peuvent être recyclées ici", "cs": "Žárovky se zde dají recyklovat", - "es": "Las bombillas pueden reciclarse aquí" + "es": "Las bombillas pueden reciclarse aquí", + "uk": "Лампочки" }, "icon": { "path": "./assets/layers/recycling/light_bulbs.svg", @@ -1033,7 +1056,8 @@ "it": "Giornali", "ca": "Aquí es poden reciclar diaris", "fr": "Les journaux peuvent être recyclés ici", - "cs": "Noviny se zde dají recyklovat" + "cs": "Noviny se zde dají recyklovat", + "uk": "Газети" }, "icon": { "path": "./assets/layers/recycling/newspaper.svg", @@ -1051,7 +1075,8 @@ "it": "Carta", "ca": "Aquí es pot reciclar paper", "fr": "Le papier peut être recyclé ici", - "cs": "Papír se zde dá recyklovat" + "cs": "Papír se zde dá recyklovat", + "uk": "Папір" }, "icon": { "path": "./assets/layers/recycling/paper.svg", @@ -1069,7 +1094,8 @@ "it": "Bottiglie di platica", "ca": "Aquí es poden reciclar ampolles de plàstic", "fr": "Les bouteilles en plastique peuvent être recyclées ici", - "cs": "Plastové lahve se zde dají recyklovat" + "cs": "Plastové lahve se zde dají recyklovat", + "uk": "Пластикові пляшки" }, "icon": { "path": "./assets/layers/recycling/plastic_bottles.svg", @@ -1087,7 +1113,8 @@ "it": "Confezioni di plastica", "ca": "Aquí es poden reciclar envasos de plàstic", "fr": "Les emballages plastiques peuvent être recyclés ici", - "cs": "Plastové obaly se zde dají recyklovat" + "cs": "Plastové obaly se zde dají recyklovat", + "uk": "Пластикова упаковка" }, "icon": { "path": "./assets/layers/recycling/plastic_packaging.svg", @@ -1106,7 +1133,8 @@ "ca": "Aquí es pot reciclar plàstic", "fr": "Le plastique peut être recyclé ici", "pl": "Plastik może być tutaj poddany recyklingowi", - "cs": "Plast se zde dá recyklovat" + "cs": "Plast se zde dá recyklovat", + "uk": "Пластик" }, "icon": { "path": "./assets/layers/recycling/plastic.svg", @@ -1119,7 +1147,8 @@ "then": { "en": "Plastic packaging, metal packaging and drink cartons (PMD) can be recycled here", "nl": "Plastic verpakkingen, metalen verpakkingen en drankpakken kunnen hier gerecycled worden", - "de": "Kunststoffverpackungen, Metallverpackungen und Getränkekartons (Tetrapak) können hier recycelt werden" + "de": "Kunststoffverpackungen, Metallverpackungen und Getränkekartons (Tetrapak) können hier recycelt werden", + "uk": "Пластикова упаковка, металева упаковка та упаковка для напоїв (PMD)" }, "icon": { "path": "./assets/layers/recycling/pmd.svg", @@ -1135,7 +1164,8 @@ "de": "Druckerpatronen können hier recycelt werden", "ca": "Els cartutxos d'impressora es poden reciclar aquí", "fr": "Les cartouches d'imprimante peuvent être recyclées ici", - "cs": "Náplně do tiskáren lze recyklovat zde" + "cs": "Náplně do tiskáren lze recyklovat zde", + "uk": "Картриджі для принтерів" }, "icon": { "path": "./assets/layers/recycling/printer_cartridges.svg", @@ -1153,7 +1183,8 @@ "es": "Aquí se puede reciclar chatarra", "it": "Rottami metallici", "fr": "La ferraille peut être recyclée ici", - "cs": "Kovový šrot lze zde recyklovat" + "cs": "Kovový šrot lze zde recyklovat", + "uk": "Металобрухт" }, "icon": { "path": "./assets/layers/recycling/scrap_metal.svg", @@ -1171,7 +1202,8 @@ "it": "Scarpe", "ca": "Aquí es poden reciclar sabates", "fr": "Les chaussures peuvent être recyclées ici", - "cs": "Boty se zde dají recyklovat" + "cs": "Boty se zde dají recyklovat", + "uk": "Взуття" }, "icon": { "path": "./assets/layers/recycling/shoes.svg", @@ -1188,7 +1220,8 @@ "it": "Piccoli elettrodomestici", "ca": "Aquí es poden reciclar petits electrodomèstics", "fr": "Les petits appareils électriques peuvent être recyclés ici", - "cs": "Drobné elektrospotřebiče se zde dají recyklovat" + "cs": "Drobné elektrospotřebiče se zde dají recyklovat", + "uk": "Малі електричні прилади" }, "icon": { "path": "./assets/layers/recycling/small_electrical_appliances.svg", @@ -1223,7 +1256,8 @@ "it": "Aghi e oggetti appuntiti", "ca": "Aquí es poden reciclar agulles", "fr": "Les aiguilles peuvent être recyclées ici", - "cs": "Jehly se zde dají recyklovat" + "cs": "Jehly se zde dají recyklovat", + "uk": "Голки" }, "icon": { "path": "./assets/layers/recycling/needles.svg", @@ -1240,7 +1274,8 @@ "it": "Secco", "ca": "Ací es pot reciclar el rebuig", "fr": "Les autres déchets peuvent être déposés ici", - "cs": "Zbytkový odpad zde lze recyklovat" + "cs": "Zbytkový odpad zde lze recyklovat", + "uk": "Залишкові відходи" }, "icon": { "path": "./assets/layers/waste_disposal/waste_disposal.svg", @@ -1257,7 +1292,8 @@ "ca": "Aquí es poden reciclar bicicletes", "de": "Fahrräder können hier recycelt werden", "fr": "Les vélos peuvent être recyclés ici", - "cs": "Kola se zde dají recyklovat" + "cs": "Kola se zde dají recyklovat", + "uk": "Велосипеди" }, "icon": { "path": "./assets/layers/recycling/bicycles.svg", @@ -1314,7 +1350,8 @@ "es": "¿A qué horas abre esta facilidad de reciclado?", "it": "Quali sono gli orari di apertura di questo impianto di raccolta e riciclo?", "ca": "Quin és l'horari d'obertura d'aquesta instal·lació de reciclatge?", - "cs": "Jaká je otevírací doba tohoto recyklačního zařízení?" + "cs": "Jaká je otevírací doba tohoto recyklačního zařízení?", + "uk": "Які години роботи цього об'єкту з переробки?" } } }, diff --git a/assets/layers/slow_roads/slow_roads.json b/assets/layers/slow_roads/slow_roads.json index 7978280ed..23be18b70 100644 --- a/assets/layers/slow_roads/slow_roads.json +++ b/assets/layers/slow_roads/slow_roads.json @@ -4,7 +4,8 @@ "en": "Paths, carfree and slow roads", "nl": "Paadjes, trage wegen en autoluwe straten", "de": "Pfade, autofreie und geschwindigkeitsreduzierte Straßen", - "fr": "Chemins, routes non carrossables et routes à vitesse réduite" + "fr": "Chemins, routes non carrossables et routes à vitesse réduite", + "es": "Caminos, carreteras sin tráfico y lentas" }, "description": { "en": "All carfree roads", diff --git a/assets/layers/speed_display/speed_display.json b/assets/layers/speed_display/speed_display.json index 6d8dc7afe..8e3e26d83 100644 --- a/assets/layers/speed_display/speed_display.json +++ b/assets/layers/speed_display/speed_display.json @@ -91,7 +91,8 @@ "de": "Wie lautet der Text auf dieser Geschwindigkeitsanzeige?", "ca": "Quin text es mostra al radar pedagògic?", "nl": "Wat is de tekst op dit snelheidsdisplay?", - "cs": "Jaký je text na tomto ukazateli rychlosti?" + "cs": "Jaký je text na tomto ukazateli rychlosti?", + "uk": "Який текст відображається на цьому дисплеї швидкості?" }, "freeform": { "key": "inscription", @@ -101,7 +102,8 @@ "de": "Text auf der Geschwindigkeitsanzeige (z. B. \"Ihre Geschwindigkeit\")", "nl": "Tekst op snelheidsdisplay (b.v. 'Uw snelheid')", "ca": "Text al radar pedagògic (p. e. 'La seva velocitat')", - "cs": "Text na displeji rychlosti (např. \"Vaše rychlost\")" + "cs": "Text na displeji rychlosti (např. \"Vaše rychlost\")", + "uk": "Текст на дисплеї швидкості (наприклад, \"Ваша швидкість\")" } }, "render": { @@ -109,7 +111,8 @@ "de": "Der Text auf dieser Geschwindigkeitsanzeige ist {inscription}", "nl": "De tekst op dit snelheidsdisplay is {inscription}", "ca": "El text a aquest radar pedagogic és {inscription}", - "cs": "Text na tomto ukazateli rychlosti je {inscription}" + "cs": "Text na tomto ukazateli rychlosti je {inscription}", + "uk": "Текст на цьому дисплеї швидкості має вигляд {inscription}" } } ], diff --git a/assets/layers/toilet/toilet.json b/assets/layers/toilet/toilet.json index d6c92d4b0..cae62d04e 100644 --- a/assets/layers/toilet/toilet.json +++ b/assets/layers/toilet/toilet.json @@ -686,7 +686,8 @@ "question": { "en": "Where are the free menstrual products located?", "nl": "Waar bevinden de gratis menstruatieproducten zich?", - "de": "Wo befinden sich die kostenlosen Menstruationsprodukte?" + "de": "Wo befinden sich die kostenlosen Menstruationsprodukte?", + "uk": "Де знаходяться безкоштовні менструальні засоби?" }, "condition": { "or": [ @@ -708,7 +709,8 @@ "then": { "en": "The free, menstrual products are located in the toilet for women", "nl": "De gratis menstruatieproducten bevinden zich in het vrouwentoilet", - "de": "Die kostenlosen Menstruationsprodukte befinden sich in der Toilette für Frauen" + "de": "Die kostenlosen Menstruationsprodukte befinden sich in der Toilette für Frauen", + "uk": "Безкоштовні менструальні засоби знаходяться в туалеті для жінок" }, "if": "toilets:menstrual_products:location=female_toilet", "alsoShowIf": "toilets:menstrual_products:location=" @@ -717,7 +719,8 @@ "then": { "en": "The free, menstrual products are located in the toilet for men", "nl": "De gratis menstruatieproducten bevinden zich in het mannentoilet", - "de": "Die kostenlosen Menstruationsprodukte befinden sich in der Toilette für Männer" + "de": "Die kostenlosen Menstruationsprodukte befinden sich in der Toilette für Männer", + "uk": "Безкоштовні менструальні засоби знаходяться в туалеті для чоловіків" }, "if": "toilets:menstrual_products:location=male_toilet" }, @@ -726,7 +729,8 @@ "then": { "en": "The free, menstrual products are located in the toilet for wheelchair users", "nl": "De gratis menstruatieproducten bevinden zich in het rolstoeltoegankelijke toilet", - "de": "Die kostenlosen Menstruationsprodukte befinden sich auf der Toilette für Rollstuhlfahrende" + "de": "Die kostenlosen Menstruationsprodukte befinden sich auf der Toilette für Rollstuhlfahrende", + "uk": "Безкоштовні менструальні засоби знаходяться в туалеті для людей на візках" } } ] @@ -791,7 +795,8 @@ "es": "¿Dónde está el cambiador?", "da": "Hvor er puslebordet placeret?", "ca": "On està el canviador?", - "cs": "Kde je umístěn přebalovací pult?" + "cs": "Kde je umístěn přebalovací pult?", + "uk": "Де знаходиться пеленальний столик?" }, "render": { "en": "A changing table is located at {changing_table:location}", diff --git a/assets/layers/toilet_at_amenity/toilet_at_amenity.json b/assets/layers/toilet_at_amenity/toilet_at_amenity.json index 1fe207835..7a4d8be93 100644 --- a/assets/layers/toilet_at_amenity/toilet_at_amenity.json +++ b/assets/layers/toilet_at_amenity/toilet_at_amenity.json @@ -331,7 +331,8 @@ "en": "When is the amenity where these toilets are located open?", "de": "Wann ist der Ort, an dem sich diese Toiletten befinden, geöffnet?", "ca": "Quan està oberta la instal·lació on es troben aquests lavabos?", - "cs": "Kdy je otevřeno zařízení, kde se tyto toalety nacházejí?" + "cs": "Kdy je otevřeno zařízení, kde se tyto toalety nacházejí?", + "uk": "Коли відкрито приміщення, де розташовані ці туалети?" } } }, diff --git a/assets/layers/unit/unit.json b/assets/layers/unit/unit.json index 7a57fa913..efeb90bb5 100644 --- a/assets/layers/unit/unit.json +++ b/assets/layers/unit/unit.json @@ -118,7 +118,8 @@ "human": { "en": "{quantity} Volt", "nl": "{quantity} volt", - "de": "{quantity} Volt" + "de": "{quantity} Volt", + "hu": "{quantity} Volt" } } ], @@ -139,7 +140,8 @@ "en": "{quantity} A", "nl": "{quantity} A", "ca": "{quantity} A", - "de": "{quantity} A" + "de": "{quantity} A", + "hu": "{quantity} A" } } ], @@ -181,7 +183,8 @@ "fr": "un mètre", "nl": "één meter", "de": "ein Meter", - "ca": "un metre" + "ca": "un metre", + "hu": "egy méter" } }, { @@ -201,13 +204,15 @@ "cs": "{quantity} centimetr", "pl": "{quantity} centymetr", "ru": "{quantity} сантиметры", - "pa_PK": " {quantity}سینٹیمیٹر" + "pa_PK": " {quantity}سینٹیمیٹر", + "hu": "{quantity} centiméter" }, "humanSingular": { "en": "one centimeter", "nl": "één centimeter", "ca": "un centímetre", - "de": "ein Zentimeter" + "de": "ein Zentimeter", + "hu": "egy centiméter" } }, { @@ -223,13 +228,15 @@ "ru": "{quantity} миллиметры", "ca": "{quantity} mil·límetres", "cs": "{quantity} milimetry", - "pa_PK": "{quantity} ملیمیٹر" + "pa_PK": "{quantity} ملیمیٹر", + "hu": "{quantity} milliméter" }, "humanSingular": { "en": "one millimeter", "nl": "één millimeter", "de": "ein Millimeter", - "ca": "un mil·límetre" + "ca": "un mil·límetre", + "hu": "egy milliméter" } }, { @@ -252,7 +259,8 @@ "eu": "{quantity} ·hanka", "pl": "{quantity} stopy", "nb_NO": "{quantity} fot", - "pa_PK": "{quantity} ؜ فوٹ" + "pa_PK": "{quantity} ؜ فوٹ", + "hu": "{quantity} láb" } } ] @@ -276,7 +284,8 @@ "de": "{quantity} Kilometer/Stunde", "cs": "{quantity} kilometry/hodinu", "pa_PK": "{quantity}ہر گھنٹہ وچ کیلومیٹر", - "fr": "{quantity} kilomètres/heure" + "fr": "{quantity} kilomètres/heure", + "hu": "{quantity} kilméter/óra" }, "humanShort": { "en": "{quantity} km/h", @@ -287,7 +296,8 @@ "cs": "{quantity} km/h", "pa_PK": "{quantity}ہر گھنٹے وچ کیلومیٹر", "ru": "{quantity} км/ч", - "fr": "{quantity} km/h" + "fr": "{quantity} km/h", + "hu": "{quantity} km/h" } }, { @@ -310,7 +320,8 @@ "de": "{quantity} Meilen/Stunde", "cs": "{quantity} míle/hodinu", "fr": "{quantity} miles/heure", - "pa_PK": "{quantity} ہر گھنٹہ وچ میل" + "pa_PK": "{quantity} ہر گھنٹہ وچ میل", + "hu": "{quantity} mérföld/óra" }, "humanShort": { "en": "{quantity} mph", @@ -320,7 +331,8 @@ "de": "{quantity} mph", "cs": "{quantity} mph", "pa_PK": "{quantity}ہر گھنٹہ وچ میل", - "fr": "{quantity} mph" + "fr": "{quantity} mph", + "hu": "{quantity} mf/h" } } ] @@ -344,14 +356,16 @@ "nl": "{quantity} minuten", "ca": "{quantity} minuts", "de": "{quantity} Minuten", - "fr": "{quantity} minutes" + "fr": "{quantity} minutes", + "hu": "{quantity} perc" }, "humanSingular": { "en": "one minute", "nl": "één minuut", "ca": "un minut", "de": "eine Minute", - "fr": "une minute" + "fr": "une minute", + "hu": "egy perc" } }, { @@ -371,14 +385,16 @@ "nl": "{quantity} uren", "ca": "{quantity} hores", "de": "{quantity} Stunden", - "fr": "{quantity} heures" + "fr": "{quantity} heures", + "hu": "{quantity} óra" }, "humanSingular": { "en": "one hour", "nl": "één uur", "ca": "una hora", "de": "eine Stunde", - "fr": "une heure" + "fr": "une heure", + "hu": "egy óra" } }, { @@ -395,13 +411,15 @@ "nl": "{quantity} day", "ca": "{quantity} dies", "de": "{quantity} Tage", - "fr": "{quantity} jours" + "fr": "{quantity} jours", + "hu": "{quantity} nap" }, "humanSingular": { "en": "one day", "nl": "één dag", "de": "ein Tag", - "fr": "un jour" + "fr": "un jour", + "hu": "egy nap" } }, { @@ -412,13 +430,15 @@ "en": "one week", "nl": "één week", "de": "ein Woche", - "fr": "une semaine" + "fr": "une semaine", + "hu": "egy hét" }, "human": { "en": "{quantity} weeks", "nl": "{quantity} weken", "de": "{quantity} Wochen", - "fr": "{quantity} semaines" + "fr": "{quantity} semaines", + "hu": "{quantity} hét" } }, { @@ -429,13 +449,15 @@ "en": "one month", "nl": "één maand", "de": "ein Monat", - "fr": "un mois" + "fr": "un mois", + "hu": "egy hónap" }, "human": { "en": "{quantity} months", "nl": "{quantity} maanden", "de": "{quantity} Monate", - "fr": "{quantity} mois" + "fr": "{quantity} mois", + "hu": "{quantity} hónap" } }, { @@ -447,14 +469,16 @@ "nl": "één jaar", "de": "ein Jahr", "fr": "un an", - "ca": "un any" + "ca": "un any", + "hu": "egy év" }, "human": { "en": "{quantity} years", "nl": "{quantity} jaar", "de": "{quantity} Jahre", "fr": "{quantity} années", - "ca": "{quantity} anys" + "ca": "{quantity} anys", + "hu": "{quantity} év" } } ] diff --git a/assets/layers/usersettings/usersettings.json b/assets/layers/usersettings/usersettings.json index 848633e15..967791d01 100644 --- a/assets/layers/usersettings/usersettings.json +++ b/assets/layers/usersettings/usersettings.json @@ -7,7 +7,8 @@ "pt": "Uma camada especial que não deve ser mostrada em um mapa, mas que é usada para definir as configurações do usuário", "ca": "Una capa especial que no està pensada per mostrar-se en un mapa, però que s'utilitza per configurar la configuració de l'usuari", "cs": "Speciální vrstva, která není určena k zobrazení na mapě, ale slouží k nastavení uživatelských nastavení", - "uk": "Спеціальний шар, який не призначений для відображення на карті, але використовується для встановлення користувацьких налаштувань" + "uk": "Спеціальний шар, який не призначений для відображення на карті, але використовується для встановлення користувацьких налаштувань", + "hu": "Egy speciális réteg, amelyet nem a térképen való megjelenítésre szántak, hanem a felhasználói beállítások megadására szolgál" }, "source": "special", "calculatedTags": [ @@ -30,7 +31,9 @@ "fi": "Asetukset", "zh_Hant": "設定", "sl": "Nastavitve", - "ru": "Настройки" + "ru": "Настройки", + "hu": "Beállítások", + "uk": "Налаштування" } }, "pointRendering": null, @@ -93,7 +96,8 @@ "nl": "Voeg een profielbeschrijving toe", "pl": "Dodaj opis profilu", "pt": "Adicionar uma descrição do perfil", - "zh_Hant": "新增個人檔敘述" + "zh_Hant": "新增個人檔敘述", + "hu": "Profilleírás hozzáadása" } } } @@ -124,7 +128,8 @@ "nl": "Pas je profielbeschrijving aan", "pl": "Edytuj opis swojego profilu", "pt": "Editar a descrição do seu perfil", - "zh_Hant": "編輯你的個人檔敘述" + "zh_Hant": "編輯你的個人檔敘述", + "hu": "Profilleírás szerkesztése" } } } @@ -144,7 +149,8 @@ "nl": "Een link naar je Mastodon-profiel werd gevonden: {_mastodon_link}", "fr": "Un lien vers votre profil Mastodon a été trouvé : {_mastodon_link}", "ca": "S'ha trobat un enllaç al vostre perfil de Mastodon: {_mastodon_link}", - "cs": "Byl nalezen odkaz na váš profil Mastodon: {_mastodon_link}" + "cs": "Byl nalezen odkaz na váš profil Mastodon: {_mastodon_link}", + "hu": "Megtaláltuk a Mastodon-profilodra mutató linket : {_mastodon_link}" }, "icon": "mastodon" }, @@ -155,7 +161,8 @@ "de": "Wir haben einen Link gefunden, der aussieht wie ein Mastodon-Konto, aber nicht verifiziert ist. Bearbeiten Sie Ihre Profilbeschreibung und fügen Sie dort Folgendes ein: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>", "nl": "Je profielbeschrijving bevat een link die vermoedelijk naar je Mastodon gaat, maar deze link is niet verifieerdbaar voor Mastodon.Pas je profielbeschrijving aan en plaats er de volgende code: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>", "ca": "Hem trobat un enllaç al que sembla ser un compte de mastodon, però no està verificat. Editeu la descripció del vostre perfil i col·loqueu-hi el següent: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>", - "cs": "Našli jsme odkaz na to, co vypadá jako účet mastodon, ale je neověřený. Upravte popis svého profilu a umístěte tam následující: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>" + "cs": "Našli jsme odkaz na to, co vypadá jako účet mastodon, ale je neověřený. Upravte popis svého profilu a umístěte tam následující: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>", + "hu": "Találtunk egy linket, ami egy Mastodon-fióknak tűnik, de nem ellenőrzött. Lépj be a profilod szerkesztéséhez, és add meg a következőt: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>" }, "icon": "invalid" } @@ -179,7 +186,8 @@ "nl": "Je hebt {_csCount} verschillende keren bijgedragen! Dat is indrukwekkend!", "da": "Du har lavet ændringer ved {_csCount} forskellige begivenheder! Det er fantastisk!", "es": "Has hecho cambios en {_csCount} ocasiones diferentes. ¡Es alucinante!", - "cs": "Změny jste provedli při {_csCount} různých příležitostech! To je úžasné!" + "cs": "Změny jste provedli při {_csCount} různých příležitostech! To je úžasné!", + "hu": "Eddig {_csCount} szerkesztést végeztél a térképen! Fantasztikus!" }, "icon": "party" } @@ -198,7 +206,8 @@ "en": "You have contributed to translating MapComplete with {_translation_contributions} commits! That's awesome!", "nl": "Je hebt MapComplete helpen vertalen met {_translation_contributions} commits! Dat is fantastisch! Bedankt hiervoor!", "de": "Du hast mit {_translation_contributions} Änderungen zur Übersetzung von MapComplete beigetragen! Das ist großartig!", - "ca": "Has contribuït a traduir MapComplete amb {_translation_contributions} canvis! Això és fantàstic!" + "ca": "Has contribuït a traduir MapComplete amb {_translation_contributions} canvis! Això és fantàstic!", + "hu": "A MapComplete fordításához {_translation_contributions} elemmel járultál hozzá! Fantasztikus!" }, "icon": "party" } @@ -219,7 +228,8 @@ "nl": "Je hebt mee geprogrammeerd aan MapComplete met {_code_contributions} commits! Das supercool van je! Bedankt hiervoor!", "ca": "Heu aportat codi a MapComplete amb {_code_contributions} commits! Això és increïble!", "cs": "Přispěli jste do MapComplete kódem s {_code_contributions} revizemi! To je úžasné!", - "da": "Du har bidraget kode til MapComplete med {_code_contributions} commits! Det er fantastisk!" + "da": "Du har bidraget kode til MapComplete med {_code_contributions} commits! Det er fantastisk!", + "hu": "{_code_contributions} elemmel járultál hozzá a MapComplete kódjához! Fantasztikus!" }, "icon": "party", "hideInAnswer": true @@ -241,7 +251,8 @@ "ca": "L'idioma es va establir mitjançant un paràmetre d'URL i l'usuari no pot definir-lo.", "cs": "Jazyk byl nastaven pomocí parametru URL a uživatel jej nemůže nastavit.²", "nl": "De taal werd ingesteld via een URL-parameter en kan niet manueel ingesteld worden.", - "da": "Sproget er angivet vha. en URL-parameter og kan ikke angives af brugeren." + "da": "Sproget er angivet vha. en URL-parameter og kan ikke angives af brugeren.", + "hu": "A nyelv egy URL-paraméterrel lett megadva ezért felhasználó nem tudja beállítani." } } ] @@ -261,7 +272,8 @@ "de": "Deinen Posteingang öffnen", "ca": "Obre la teva safata d'entrada", "cs": "Otevřít schránku", - "da": "Åbn din indbakke" + "da": "Åbn din indbakke", + "hu": "Beérkezett levelet megnyitása" } } } @@ -277,7 +289,8 @@ "ca": "Tens {_unreadMessages} missatges
Obri la safata d'entrada", "cs": "Máte {_unreadMessages}
Otevřít schránku", "nl": "Je hebt {_unreadMessages} ongelezen berichten
Ga naar je inbox", - "da": "Du har {_unreadMessages} beskeder
Åbn din indbakke" + "da": "Du har {_unreadMessages} beskeder
Åbn din indbakke", + "hu": "{_unreadMessages} üzeneted van
Nyisd meg a bejövő üzeneteket" }, "href": "{_backend}/messages/inbox" } @@ -296,7 +309,8 @@ "ca": "Obriu la vostra configuració a OpenStreetMap.org", "cs": "Otevřít vaše nastavení na OpenStreetMap.org", "nl": "Open je instellingen op OpenStreetMap.org", - "da": "Åbn dine indstillinger på OpenStreetMap.org" + "da": "Åbn dine indstillinger på OpenStreetMap.org", + "hu": "Beállításaid megnyitása az OpenStreetMap.org-on" }, "href": "{_backend}/account/edit" } @@ -312,7 +326,9 @@ "id": "title-map", "render": { "en": "

Configure map

", - "de": "

Karte konfigurieren

" + "de": "

Karte konfigurieren

", + "hu": "

Térkép beállítása

", + "uk": "

Налаштування карти

" } }, { @@ -322,7 +338,8 @@ "nl": "Wanneer moet de toegankelijkheidsmode ingeschakeld worden?", "de": "Welche Barrierefrei-Funktionen sollen angewendet werden?", "da": "Hvilke tilgængelighedsfunktioner skal anvendes?", - "uk": "Які елементи доступності слід застосовувати?" + "uk": "Які елементи доступності слід застосовувати?", + "hu": "Milyen elérhetőségi jellemzők jelenjenek meg?" }, "mappings": [ { @@ -335,7 +352,9 @@ "de": "Barrierefrei-Modus aktivieren, wenn Pfeiltasten zum Navigieren in der Karte verwendet werden", "da": "Aktiver tilgængelighedsfunktioner, når piletaster anvendes til at navigere i kortet", "cs": "Povolení funkcí přístupnosti při použití kláves se šipkami k navigaci na mapě", - "uk": "Увімкніть функції доступності, коли для навігації по карті використовуються клавіші зі стрілками" + "uk": "Увімкніть функції доступності, коли для навігації по карті використовуються клавіші зі стрілками", + "fr": "Activer les fonctions d'accessibilité lorsque les touches directionnelles sont utilisées pour naviguer sur la carte", + "hu": "Elérhető objektumok megjelenítése, amikor a térképen a nyílbillentyűkkel navigálsz" } }, { @@ -346,7 +365,8 @@ "nl": "Schakel de toegankelijkheidsmode altijd aan", "de": "Barrierefrei-Modus immer aktivieren", "da": "Aktiver altid tilgængelighedsfunktioner", - "uk": "Завжди вмикайте функції доступності" + "uk": "Завжди вмикайте функції доступності", + "hu": "Mindig jelenjenek meg az elérhető objektumok" } }, { @@ -356,7 +376,8 @@ "nl": "Gebruik geen toegankelijkheidsmode", "de": "Barrierefrei-Modus niemals aktivieren", "da": "Aktiver aldrig tilgængelighedsfunktioner", - "uk": "Ніколи не вмикайте функції доступності" + "uk": "Ніколи не вмикайте функції доступності", + "hu": "Soha ne jelenjenek meg az elérhető objektumok" } } ] @@ -376,7 +397,8 @@ "de": "Diese thematische Karte hat einen vordefinierten Hintergrund. Ihr Standardhintergrund wird nicht angewendet", "cs": "Tato tematická mapa má předdefinovanou sadu podkladových vrstev. Vaše výchozí nastavení tématu se nepoužije", "nl": "Deze kaart heeft een achtegrondlaag instgesteld. Je instellingen worden niet gebruikt", - "da": "Dette temakort har angivet et foruddefineret baggrundslag. Dine standardindstillinger benyttes ikke" + "da": "Dette temakort har angivet et foruddefineret baggrundslag. Dine standardindstillinger benyttes ikke", + "hu": "Ennek a tematikus térképnek van előre meghatározott háttérrétegkészlete. Itt ezért nem alkalmazható az általad beállított alapértelmezett tematikustérkép-beállítás" } }, { @@ -388,11 +410,13 @@ "cs": "Jaká vrstva pozadí by se měla zobrazovat ve výchozím nastavení?", "nl": "Welke achtergrondlaag moet altijd gebruikt worden?", "fr": "Quel fond souhaitez-vous utiliser par défaut ?", - "da": "Hvilket baggrundslag skal vises som standard?" + "da": "Hvilket baggrundslag skal vises som standard?", + "hu": "Milyen háttérréteg jelenjék meg alapértelmezés szerint?" }, "questionHint": { "en": "To set a specific background as default, select it in the background menu first after which it will appear here.", - "de": "Um einen bestimmten Hintergrund als Standard festzulegen, wähle ihn zuerst im Hintergrundmenü aus, danach wird er hier angezeigt." + "de": "Um einen bestimmten Hintergrund als Standard festzulegen, wähle ihn zuerst im Hintergrundmenü aus, danach wird er hier angezeigt.", + "hu": "Ha szeretnél egy adott hátteret alapértelmezettként beállítani, először válaszd ki a háttér menüben, és ez után fog itt megjelenni." }, "condition": "_theme:backgroundLayer=", "mappings": [ @@ -408,7 +432,8 @@ "nl": "Gebruik de standaard achtergrondlaag", "fr": "Utiliser le fond par défaut", "da": "Vis standardbaggrundslaget", - "uk": "Використовуйте фоновий шар за замовчуванням" + "uk": "Використовуйте фоновий шар за замовчуванням", + "hu": "Alapértelmezett háttér: az alapértelmezés szerinti" } }, { @@ -424,7 +449,8 @@ "nl": "Gebruik OpenStreetMap-carto altijd als achtergrondlaag", "fr": "Utiliser le fond OpenStreetMap-carto par défaut", "da": "Brug OpenStreetMap-carto som standardlag", - "uk": "Використовувати OpenStreetMap-carto як шар за замовчуванням" + "uk": "Використовувати OpenStreetMap-carto як шар за замовчуванням", + "hu": "Alapértelmezett háttér: OpenStreetMap-carto" } }, { @@ -438,7 +464,8 @@ "nl": "Gebruik luchtfoto's als standaard achtergrondlaag", "fr": "Utiliser le fond imagerie aérienne par défaut", "da": "Brug luftfoto som standardbaggrund", - "uk": "Використовувати аерофотознімки як фон за замовчуванням" + "uk": "Використовувати аерофотознімки як фон за замовчуванням", + "hu": "Alapértelmezett háttér: légi felvételek" } }, { @@ -452,7 +479,8 @@ "nl": "Gebruik altijd een kaart die niet op OpenStreetMap gebaseerd is als achtergrond", "fr": "Utiliser un fond non basé sur OpenStreetMap par défaut", "da": "Brug et ikke-OpenSreetMap-baseret kort som standardbaggrund", - "uk": "Використовувати мапу на основі не-openstreetmap як фон за замовчуванням" + "uk": "Використовувати мапу на основі не-openstreetmap як фон за замовчуванням", + "hu": "Alapértelmezett háttér: egy nem OpenStreetMap-alapú réteg" } }, { @@ -465,7 +493,8 @@ "nl": "Gebruik altijd {__current_background} als achtergrondlaag. Dit is de huidige achtergrond.", "fr": "Utiliser le fond actuel ({__current_background}) par défaut", "da": "Brug det nuværende baggrundslag ({__current_background}) som standardbaggrund", - "uk": "Використовувати поточний фоновий шар ({__current_background}) як фон за замовчуванням" + "uk": "Використовувати поточний фоновий шар ({__current_background}) як фон за замовчуванням", + "hu": "Alapértelmezett háttér: a jelenlegi {__current_background} háttérréteg" }, "hideInAnswer": { "or": [ @@ -485,7 +514,8 @@ "nl": "Gebruik {mapcomplete-preferred-background-layer} altijd als achtergrondlaag", "fr": "Utiliser le fond ({mapcomplete-preferred-background-layer})par défaut", "da": "Brug baggrundslaget {mapcomplete-preferred-background-layer} som standardbaggrund", - "uk": "Використовувати фоновий шар {mapcomplete-preferred-background-layer} як фон за замовчуванням" + "uk": "Використовувати фоновий шар {mapcomplete-preferred-background-layer} як фон за замовчуванням", + "hu": "Alapértelmezett háttér: {mapcomplete-preferred-background-layer} háttérréteg" }, "hideInAnswer": true } @@ -497,14 +527,18 @@ "en": "Should a crosshair be shown in the center of the display?", "cs": "Měl by se uprostřed displeje zobrazovat kříž?", "de": "Soll ein Fadenkreuz in der Mitte des Bildschirms angezeigt werden?", - "nl": "Moet er een kruisje getoond worden in het centrum van je display?" + "nl": "Moet er een kruisje getoond worden in het centrum van je display?", + "hu": "Megjelenjék-e egy célkereszt a kijelző közepén?", + "uk": "Чи потрібно показувати перехрестя в центрі дисплея?" }, "questionHint": { "en": "This can help to accurately position a new element", "cs": "To může pomoci přesněji umístit nový prvek", "de": "Dies kann dazu beitragen, ein neues Element genau zu positionieren", "nl": "Dit kan helpen om nieuwe elementen accuraat te plaatsen", - "ca": "Això pot ajudar a posicionar amb precisió un nou element" + "ca": "Això pot ajudar a posicionar amb precisió un nou element", + "hu": "Segíthet egy-egy új elem pontos elhelyezésében", + "uk": "Це може допомогти точно позиціонувати новий елемент" }, "mappings": [ { @@ -534,7 +568,9 @@ "ca": "El nord hauria d'estar sempre amunt?", "cs": "Měl by být sever vždy nahoře?", "nl": "Moet het noorden altijd naar boven getoond worden?", - "da": "Skal nord altid pege opad?" + "da": "Skal nord altid pege opad?", + "hu": "Északnak mindig fent kell lennie?", + "uk": "Чи завжди північ має бути вгорі?" }, "mappings": [ { @@ -548,7 +584,9 @@ "fr": "Autoriser la rotation de la carte", "da": "Tillad rotation af kortet", "cs": "Umožnit otáčení mapy", - "nl": "Sta kaartrotatie toe" + "nl": "Sta kaartrotatie toe", + "hu": "Térkép forgatásának engedélyezése", + "uk": "Дозволити обертати карту" } }, { @@ -561,7 +599,9 @@ "ca": "Mantingueu sempre el nord apuntant cap amunt", "cs": "Sever vždy směřujte nahoru", "nl": "Hou het noorden altijd naar boven", - "da": "Nord peger altid opad" + "da": "Nord peger altid opad", + "hu": "Mindig fölfelé legyen észak", + "uk": "Завжди тримати північ угорі" } } ] @@ -572,7 +612,9 @@ "en": "Should a scale bar be shown on the map?", "nl": "Moet er een schaalbalk weergegeven worden?", "de": "Kartenmaßstab anzeigen?", - "cs": "Má být na mapě zobrazeno měřítko?" + "cs": "Má být na mapě zobrazeno měřítko?", + "hu": "Megjelenjék-e a méretarány a térképen?", + "uk": "Чи потрібно показувати масштабну лінійку на карті?" }, "mappings": [ { @@ -580,7 +622,9 @@ "then": { "en": "Show the scalebar", "nl": "Toon de schaalbalk", - "de": "Maßstab anzeigen" + "de": "Maßstab anzeigen", + "hu": "Méretarány megjelenítése", + "uk": "Показати масштабну лінійку" } }, { @@ -590,7 +634,10 @@ "en": "Hide the scalebar", "nl": "Verberg de schaalbalk", "de": "Maßstab ausblenden", - "cs": "Skrytí panelu měřítka" + "cs": "Skrytí panelu měřítka", + "fr": "Masquer la barre d'échelle", + "hu": "Méretarány elrejtése", + "uk": "Приховати лінійку масштабу" } } ] @@ -600,7 +647,8 @@ "render": { "en": "

Editing settings

", "de": "

Einstellungen bearbeiten

", - "cs": "

Úprava nastavení

" + "cs": "

Úprava nastavení

", + "hu": "

Szerkesztési beállítások

" } }, { @@ -614,7 +662,8 @@ "nl": "Moeten onbeantwoorde vragen om beurt of allemaal samen getoond worden?", "cs": "Mají se otázky pro neznámá datová pole zobrazovat jednotlivě, nebo společně?", "da": "Skal spørgsmål for ukendte oplysninger vises ét ad gangen eller alle på én gang?", - "uk": "Чи повинні питання для невідомих полів даних з'являтися по одному або разом?" + "uk": "Чи повинні питання для невідомих полів даних з'являтися по одному або разом?", + "hu": "Egyenként vagy együttesen jelenjenek meg az ismeretlen adatokra vonatkozó kérdések?" }, "mappings": [ { @@ -628,7 +677,8 @@ "nl": "Toon alle onbeantwoorde vragen", "cs": "Zobrazit všechny otázky v infoboxu dohromady", "da": "Vis alle spørgsmål i infoboksen på én gang", - "uk": "Показати всі питання в інфобокс разом" + "uk": "Показати всі питання в інфобокс разом", + "hu": "Az infoboxban szereplő összes kérdés egyszerre jelenjék meg" } }, { @@ -642,7 +692,8 @@ "nl": "Toon de vragen één per één", "cs": "Zobrazit otázky jednu po druhé", "da": "Vis spørgsmål ét ad gangen", - "uk": "Показувати питання по черзі" + "uk": "Показувати питання по черзі", + "hu": "A kérdések egymás után jelenjenek meg" } } ] @@ -655,7 +706,9 @@ "fr": "Afficher les attributs OpenStreetMap bruts ?", "ca": "Mostra les etiquetes d'OpenStreetMap en brut?", "cs": "Zobrazit nezpracované/raw tagy OpenStreetMap?", - "nl": "Moeten de data-attributen getoond worden?" + "nl": "Moeten de data-attributen getoond worden?", + "hu": "Megjelenjenek-e a nyers OpenStreetMap-címkék?", + "uk": "Показувати необроблені OpenStreetMap-теги?" }, "questionHint": { "en": "Tags are attributes that every element has. This is the technical data that is stored in the database. You don't need this information to edit with MapComplete, but advanced users might want to use this as reference.", @@ -663,7 +716,9 @@ "ca": "Les etiquetes són atributs que té cada element. Aquestes són les dades tècniques que s'emmagatzemen a la base de dades. No necessiteu aquesta informació per editar amb MapComplete, però és possible que els usuaris avançats la vulguin fer servir com a referència.", "cs": "Tagy jsou atributy, které má každý element. Jedná se o technické údaje, které jsou uloženy v databázi. K úpravám pomocí MapComplete tyto informace nepotřebujete, ale pokročilí uživatelé by je mohli chtít použít jako referenci.", "nl": "Data-attributen zijn stukjes data die elk element in OpenStreetMap heeft. Dit is technische data die in de databank komt. Je hoeft deze informatie niet te kennen om aanpassingen te maken met MapComplete, maar geavanceerde gebruikers kunnen dit als referentie gebruiken.", - "fr": "LesTags ou étiquettes sont des attributs rattachés à chaque élément. C'est une donnée technique qui est stockée dans une base de données. Vous n'avez pas besoin de connaître ces étiquettes pour utiliser MapComplete, mais certains utilisateurs préfèrent les afficher." + "fr": "LesTags ou étiquettes sont des attributs rattachés à chaque élément. C'est une donnée technique qui est stockée dans une base de données. Vous n'avez pas besoin de connaître ces étiquettes pour utiliser MapComplete, mais certains utilisateurs préfèrent les afficher.", + "hu": "A címkék olyan tulajdonságok, amelyekkel minden térképobjektum rendelkezik. Ezek az adatbázisban tárolt technikai adatok. A MapComplete programmal történő szerkesztéshez nincs szükség ezekre az adatokra, haladó felhasználóknak azonban érdemes lehet referenciaként használniuk.", + "uk": "Теги - це атрибути, які має кожен елемент. Це технічні дані, які зберігаються в базі даних. Вам не потрібна ця інформація для редагування у MapComplete, але досвідчені користувачі можуть використовувати її як довідкову." }, "mappings": [ { @@ -674,7 +729,9 @@ "ca": "No mostris mai les etiquetes.", "cs": "Nikdy nezobrazovat tagy.", "nl": "Verberg data-attributen", - "fr": "Ne jamais voir les étiquettes." + "fr": "Ne jamais voir les étiquettes.", + "hu": "Soha ne jelenjenek meg a címkék.", + "uk": "Ніколи не показуйте теги." } }, { @@ -685,7 +742,9 @@ "ca": "Mostra les etiquetes que s'aplicaran un cop hagi fet {__userjourney_tagsVisibleAt} conjunts de canvis", "cs": "Zobrazit tagy, které budou použity, jakmile provedu {__userjourney_tagsVisibleAt} sady změn", "nl": "Toon data-attributen bij wijzigingen indien je meer dan {__userjourney_tagsVisibleAt} changesets hebt gemaakt", - "fr": "Voir les étiquettes au bout de {__userjourney_tagsVisibleAt} modifications" + "fr": "Voir les étiquettes au bout de {__userjourney_tagsVisibleAt} modifications", + "hu": "Azok a címkék jelenjenek meg, amelyeket {__userjourney_tagsVisibleAt} módosításkészlet elvégzését követően alkalmaztam", + "uk": "Показати теги, які будуть застосовані після того, як я зроблю {__userjourney_tagsVisibleAt} набори змін" } }, { @@ -696,7 +755,9 @@ "ca": "Mostra les etiquetes que s'aplicaran en fer un canvi", "cs": "Zobrazení tagů, které budou použity při provádění změny", "nl": "Toon de data-attributen die toegepast zullen worden bij wijzigingen", - "fr": "Voir les étiquettes quand je fais une modification" + "fr": "Voir les étiquettes quand je fais une modification", + "hu": "A módosításkor alkalmazott címkék jelenjenek meg", + "uk": "Показати теги, які будуть застосовані при внесенні змін" } }, { @@ -707,7 +768,9 @@ "ca": "Mostra les etiquetes que s'aplicaran a l'hora de fer un canvi i mostra la taula d'etiquetes a cada element", "cs": "Zobrazení tagů, které se použijí při provádění změny, a zobrazení tabulky tagů u každé funkce", "nl": "Toon de data-attributen die toegepast zullen worden bij wijzigingen en toon een tabel met alle data-attributen bij elk object.", - "fr": "Voir les étiquettes quand je fais une modification et toujours voir les étiquettes pour chaque élément" + "fr": "Voir les étiquettes quand je fais une modification et toujours voir les étiquettes pour chaque élément", + "hu": "A módosításkor alkalmazott címkék jelenjenek meg és minden objektumon jelenjék meg a címketáblázat", + "uk": "Показати теги, які будуть застосовані при внесенні змін, і показати таблицю тегів для кожного елемента" } } ] @@ -717,7 +780,8 @@ "question": { "en": "How should the menu to add a new feature be opened?", "de": "Wie soll das Menü zum Hinzufügen eines neuen Objekts geöffnet werden?", - "uk": "Як має відкриватися меню для додавання нового об'єкту?" + "uk": "Як має відкриватися меню для додавання нового об'єкту?", + "hu": "Hogyan nyíljék meg az új objektum hozzáadására szolgáló menü?" }, "mappings": [ { @@ -725,7 +789,8 @@ "then": { "en": "Adding a new feature is done with the button at the bottom left. Clicking the map does nothing", "de": "Das Hinzufügen eines neuen Objekts erfolgt über die Schaltfläche unten links. Ein Klick auf die Karte bewirkt nichts", - "uk": "Додавання нового об'єкта здійснюється за допомогою кнопки внизу ліворуч. Клацання по карті нічого не робить" + "uk": "Додавання нового об'єкта здійснюється за допомогою кнопки внизу ліворуч. Клацання по карті нічого не робить", + "hu": "Új objektum a bal alsó sarokban található gombbal vihető fel. A térképre kattintva nem történik semmi" } }, { @@ -734,7 +799,8 @@ "en": "When clicking or tapping the map, a marker pops up where a new feature is added", "de": "Wenn Sie auf die Karte klicken oder tippen, wird eine Markierung eingeblendet, an der ein neues Objekt hinzugefügt wird", "ca": "En fer clic o tocar el mapa, apareix un marcador on s'afegeix un element nou", - "uk": "При натисканні або торканні мапи з'являється маркер, де додається новий об'єкт" + "uk": "При натисканні або торканні мапи з'являється маркер, де додається новий об'єкт", + "hu": "Kattintás vagy koppintás után a térképen megjelenik egy jelölő ott, ahol fel lehet vinni egy új objektumot" } }, { @@ -742,7 +808,8 @@ "then": { "en": "When right-clicking or long-pressing the map, a marker pops up where a new feature can be added", "de": "Beim Rechtsklick oder einem langen Druck auf die Karte erscheint eine Markierung, mit der ein neues Objekt hinzugefügt werden kann", - "uk": "При натисканні правою кнопкою миші або тривалому утриманні мапи з'являється маркер, де можна додати новий об'єкт" + "uk": "При натисканні правою кнопкою миші або тривалому утриманні мапи з'являється маркер, де можна додати новий об'єкт", + "hu": "Jobb egérgombbal kattintva vagy hosszú nyomás után a térképen megjelenik egy jelölő ott, ahol fel lehet vinni egy új objektumot" } }, { @@ -750,7 +817,8 @@ "then": { "en": "When clicking or tapping the map, a marker pops up where a new feature can be added. Additionally, a button at the bottom left is shown", "de": "Beim Klicken oder Tippen auf die Karte wird eine Markierung eingeblendet, an der ein neues Objekt hinzugefügt werden kann. Zusätzlich wird unten links eine Schaltfläche angezeigt", - "uk": "При натисканні або торканні мапи з'являється маркер, де можна додати новий об'єкт. Крім того, внизу ліворуч відображається кнопка" + "uk": "При натисканні або торканні мапи з'являється маркер, де можна додати новий об'єкт. Крім того, внизу ліворуч відображається кнопка", + "hu": "Kattintás vagy koppintás után a térképen megjelenik egy jelölő ott, ahol fel lehet vinni egy új objektumot. Ezen kívül a bal alsó sarokban is látható egy gomb" } }, { @@ -759,7 +827,8 @@ "en": "When right-clicking or long-pressing the map, a marker pops up where a new feature can be added. Additionally, a button at the bottom left is shown", "de": "Beim Rechtsklick oder einem langen Druck auf die Karte erscheint eine Markierung, an der ein neues Objekt hinzugefügt werden kann. Zusätzlich wird unten links eine Schaltfläche angezeigt", "cs": "Po kliknutí pravým tlačítkem myši nebo dlouhém stisknutí mapy se zobrazí značka, kam lze přidat nový prvek. Kromě toho se vlevo dole zobrazí tlačítko", - "uk": "При натисканні правою кнопкою миші або тривалому утриманні на мапі з'являється маркер, куди можна додати новий об'єкт. Крім того, внизу ліворуч відображається кнопка" + "uk": "При натисканні правою кнопкою миші або тривалому утриманні на мапі з'являється маркер, куди можна додати новий об'єкт. Крім того, внизу ліворуч відображається кнопка", + "hu": "Jobb egérgombbal kattintva vagy hosszú nyomás után a térképen megjelenik egy jelölő ott, ahol fel lehet vinni egy új objektumot. Ezen kívül a bal alsó sarokban is látható egy gomb" } } ] @@ -769,7 +838,8 @@ "render": { "en": "

Privacy and legal

", "de": "

Datenschutz und Rechtliches

", - "cs": "

Soukromí a právní záležitosti

" + "cs": "

Soukromí a právní záležitosti

", + "hu": "

Adatvédelem és jog

" } }, { @@ -780,7 +850,8 @@ "then": { "en": "This theme is sensitive. Making changes will not indicate if you were nearby explicitly.", "de": "Dieses Thema ist sensibel. Wenn du Änderungen vornimmst, wird nicht angezeigt, ob du explizit in der Nähe warst.", - "nl": "Dit thema ligt gevoelig. Wijzigingen aan objecten zullen niet aangeven of je in de buurt was." + "nl": "Dit thema ligt gevoelig. Wijzigingen aan objecten zullen niet aangeven of je in de buurt was.", + "hu": "Ez a tematikus térkép érzékeny. A módosításból nem fog kiderülni, hogy kifejezetten a közelben voltál-e." } } ] @@ -790,12 +861,16 @@ "question": { "en": "When making changes, should a rough indication be given how far away you were from the object?", "de": "Sollte bei Änderungen eine grobe Angabe gemacht werden, wie weit du vom Objekt entfernt warst?", - "nl": "Mag er opgeslaan worden hoever je je van een object bevindt wanneer je aanpassingen maakt aan dit object?" + "nl": "Mag er opgeslaan worden hoever je je van een object bevindt wanneer je aanpassingen maakt aan dit object?", + "hu": "A szerkesztéseknél megjelenjék-e egy hozzávetőleges jelzés arról, hogy milyen messze voltál az objektumtól?", + "uk": "Вносячи зміни, чи потрібно приблизно вказувати, як далеко ви знаходилися від об'єкта?" }, "questionHint": { "en": "If you make a change to one or more objects and you enabled your location, a rough indication of where you made will be saved: it is indicated if you were closer then 25m, 500m, 5km or further away then 5km. This helps mappers understand your context when making changes, but gives an indication of where you were at this time. ", "de": "Wenn du eine Änderung an einem oder mehreren Objekten vornimmst und deinen Standort aktivierst, wird eine ungefähre Angabe darüber gespeichert, wo du dich befunden hast: Es wird angezeigt, ob du näher als 25m, 500m, 5km oder weiter als 5km entfernt warst. Das hilft den Kartierern, deinen Kontext zu verstehen, wenn du Änderungen vornimmst, gibt aber auch einen Hinweis darauf, wo du zu diesem Zeitpunkt warst. ", - "nl": "Wanneer je een wijziging maakt aan één of meer interessepunten en als MapComplete toont waar je bent, dan kan opgeslaan worden hoever je je ongeveer van deze objecten bevindt. Er wordt aangeduid of je dichter dan 25m, 500m, 5km of verder dan 5km was. Dit helpt om andere bijdragers te begrijpen hoe je je bijdragen deed, maar geeft natuurlijk ook aan waar je op dat moment was. " + "nl": "Wanneer je een wijziging maakt aan één of meer interessepunten en als MapComplete toont waar je bent, dan kan opgeslaan worden hoever je je ongeveer van deze objecten bevindt. Er wordt aangeduid of je dichter dan 25m, 500m, 5km of verder dan 5km was. Dit helpt om andere bijdragers te begrijpen hoe je je bijdragen deed, maar geeft natuurlijk ook aan waar je op dat moment was. ", + "hu": "Ha módosítasz egy objektumot, és engedélyezed a helymeghatározást, akkor a rendszer elmenti a körülbelüli helyedet: jelzi, hogy 25 méteren, 500 méteren vagy 5 kilométeren belül vagy 5 km-nél távolabb voltál. A többi térképész így jobban látja, milyen körülmények között szerkesztetted a térképet, de egyúttal azt is jelzi, hogy hol voltál ebben az időpontban. ", + "uk": "Якщо ви вносите зміни до одного або декількох об'єктів і ввімкнули функцію визначення вашого місцезнаходження, буде збережено приблизну інформацію про те, де ви перебували: чи були ви ближче 25 м, 500 м, 5 км або далі 5 км від об'єкта. Це допомагає картографам зрозуміти ваш контекст при внесенні змін, але дає уявлення про те, де ви були в цей час. " }, "mappings": [ { @@ -805,7 +880,9 @@ "en": "When making changes to OpenStreetMap, do not indicate how far away you were from the changed objects.", "de": "Wenn du Änderungen an OpenStreetMap vornimmst, gibst du nicht an, wie weit du von den geänderten Objekten entfernt warst.", "nl": "Geef niet aan hoever je je van de gewijzigde objecten bevindt wanneer je wijzigingen maakt met MapComplete.", - "cs": "Při provádění změn v OpenStreetMap neuvádějte, jak daleko jste byli od změněných objektů." + "cs": "Při provádění změn v OpenStreetMap neuvádějte, jak daleko jste byli od změněných objektů.", + "hu": "Az OpenStreetMap szerkesztésekor ne jelenjék meg, hogy milyen messze voltál a módosított objektumoktól.", + "uk": "Вносячи зміни до OpenStreetMap, не вказуйте, як далеко ви знаходилися від змінених об'єктів." } }, { @@ -815,7 +892,9 @@ "en": "When making changes to OpenStreetMap, roughly indicate how far away you were from the changed objects. This helps other contributors to understand how you made the change", "de": "Gebe bei Änderungen an OpenStreetMap an, wie weit du ungefähr von den geänderten Objekten entfernt warst. Das hilft anderen Mitwirkenden zu verstehen, wie du die Änderung vorgenommen hast", "nl": "Geef aan hoever je je ongeveer bevindt ten opzichte van objecten die je wijzigt in OpenStreetMap. Dit helpt andere bijdagers te begrijpen welke wijzigingen je waarom maakt.", - "cs": "Při provádění změn v OpenStreetMap zhruba uveďte, jak daleko jste byli od změněných objektů. To pomůže ostatním přispěvatelům pochopit, jak jste změnu provedli" + "cs": "Při provádění změn v OpenStreetMap zhruba uveďte, jak daleko jste byli od změněných objektů. To pomůže ostatním přispěvatelům pochopit, jak jste změnu provedli", + "hu": "Az OpenStreetMap szerkesztésekor hozzávetőlegesen jelenjék meg, hogy milyen messze voltál a módosított objektumoktól. A többi térképész így jobban látja, hogy hogyan végezted a szerkesztést", + "uk": "Вносячи зміни до OpenStreetMap, приблизно вказуйте, як далеко ви знаходилися від змінених об'єктів. Це допоможе іншим учасникам зрозуміти, як ви внесли зміни" } } ] @@ -824,11 +903,15 @@ "id": "sync-visited-themes", "question": { "en": "Should the thematic maps you visit be saved?", - "de": "Sollen besuchte Themenkarten gespeichert werden?" + "de": "Sollen besuchte Themenkarten gespeichert werden?", + "hu": "El legyenek-e mentve a meglátogatott tematikus térképek?", + "uk": "Чи потрібно зберігати тематичні карти, які ви відвідуєте?" }, "questionHint": { "en": "If you visit a map about a certain topic, MapComplete can remember this and offer this as suggestion.", - "de": "Besuchte Themenkarten, können von MapComplete gespeichert und als Vorschlag angeboten werden." + "de": "Besuchte Themenkarten, können von MapComplete gespeichert und als Vorschlag angeboten werden.", + "hu": "A MapComplete meg tudja jegyezni, hogy megnéztél egy bizonyos témájú térképet, és ezt később felajánlja javaslatként.", + "uk": "Якщо ви відвідуєте мапу на певну тему, MapComplete може запам'ятати це і запропонувати її як підказку." }, "mappings": [ { @@ -837,21 +920,27 @@ "then": { "en": "Save the visited thematic maps and sync them via openstreetmap.org. OpenStreetMap and all apps you use can see this history", "de": "Besuchte Themenkarten speichern und über opentreetmap.org synchronisieren. OpenStreetMap und alle verwendeten Programme können diesen Verlauf sehen", - "cs": "Uložit navštívené tematické mapy a synchronizovat je prostřednictvím openstreetmap.org. OpenStreetMap a všechny aplikace, které používáte, mohou tuto historii vidět" + "cs": "Uložit navštívené tematické mapy a synchronizovat je prostřednictvím openstreetmap.org. OpenStreetMap a všechny aplikace, které používáte, mohou tuto historii vidět", + "hu": "Az openstreetmap.org-on legyenek elmentve és szinkronizálva az általam megnézett tematikus térképek. Ezeket az előzményeket az OpenStreetMap és az összes hozzá kapcsolódó alkalmazás láthatja", + "uk": "Зберігайте відвідані тематичні карти та синхронізуйте їх через openstreetmap.org. OpenStreetMap та всі програми, якими ви користуєтесь, можуть бачити цю історію" } }, { "if": "mapcomplete-preference-theme-history=local", "then": { "en": "Save the visited thematic maps on my device", - "de": "Besuchte Themenkarten auf meinem Gerät speichern" + "de": "Besuchte Themenkarten auf meinem Gerät speichern", + "hu": "A saját készülékemen legyenek elmentve a meglátogatott tematikus térképek", + "uk": "Зберегти відвідані тематичні карти на моєму пристрої" } }, { "if": "mapcomplete-preference-theme-history=no", "then": { "en": "Don't save visited thematic maps", - "de": "Besuchte Themenkarten nicht auf meinem Gerät speichern" + "de": "Besuchte Themenkarten nicht auf meinem Gerät speichern", + "hu": "Ne legyenek elmentve a megnézett térképek", + "uk": "Не зберігайте відвідані тематичні мапи" } } ] @@ -860,11 +949,15 @@ "id": "sync-visited-locations", "question": { "en": "Should the locations you search for and inspect be remembered?", - "de": "Sollen die gesuchten und überprüften Orte gespeichert werden?" + "de": "Sollen die gesuchten und überprüften Orte gespeichert werden?", + "hu": "Meg legyenek-e jegyezve az általad megkeresett és szerkesztett helyek?", + "uk": "Чи потрібно запам'ятовувати місця, які ви шукаєте та оглядаєте?" }, "questionHint": { "en": "Those locations will be offered in the search menu", - "de": "Diese Orte werden im Suchmenü angezeigt" + "de": "Diese Orte werden im Suchmenü angezeigt", + "hu": "Ezek a helyszínek a keresési menüben lesznek felajánlva", + "uk": "Ці локації будуть запропоновані в меню пошуку" }, "mappings": [ { @@ -872,21 +965,27 @@ "alsoShowIf": "mapcomplete-preference-search-history=", "then": { "en": "Save the locations you search for and inspect and sync them via openstreetmap.org. OpenStreetMap and all apps you use can see this history", - "de": "Gesuchte und überprüfte Orte speichern und über openstreetmap.org synchronisieren. OpenStreetMap und alle verwendeten Programme können diesen Verlauf sehen" + "de": "Gesuchte und überprüfte Orte speichern und über openstreetmap.org synchronisieren. OpenStreetMap und alle verwendeten Programme können diesen Verlauf sehen", + "hu": "Az openstreetmap.org-on legyenek elmentve az általam megkeresett és szerkesztett helyek. Ezeket az előzményeket az OpenStreetMap és az összes hozzá kapcsolódó alkalmazás láthatja", + "uk": "Зберігайте місця, які ви шукаєте, переглядайте та синхронізуйте їх через openstreetmap.org. OpenStreetMap та всі програми, якими ви користуєтесь, можуть бачити цю історію" } }, { "if": "mapcomplete-preference-search-history=local", "then": { "en": "Save the locations you search for and inspect on my device", - "de": "Gesuchte und überprüfte Orte auf meinem Gerät speichern" + "de": "Gesuchte und überprüfte Orte auf meinem Gerät speichern", + "hu": "A saját készülékemen legyenek elmentve az általam megkeresett és szerkesztett helyek", + "uk": "Зберігайте місця, які ви шукаєте та перевіряєте, на моєму пристрої" } }, { "if": "mapcomplete-preference-search-history=no", "then": { "en": "Don't save the locations you search for and inspect ", - "de": "Gesuchte und überprüfte Orte nicht auf meinem Gerät speichern " + "de": "Gesuchte und überprüfte Orte nicht auf meinem Gerät speichern ", + "hu": "Ne legyenek elmentve az általam megkeresett és szerkesztett helyek ", + "uk": "Не зберігайте місця, які ви шукаєте та перевіряєте " } } ] @@ -895,7 +994,8 @@ "id": "title-id", "render": { "en": "

Mangrove ID management

", - "de": "

Mangrove ID Management

" + "de": "

Mangrove ID Management

", + "hu": "

Mangrove ID (azonosító) kezelése

" } }, { @@ -913,7 +1013,8 @@ "da": "Hent den private nøgle til din Mangrove-konto", "ca": "Baixeu la clau privada del vostre compte de Mangrove", "cs": "Stáhnout soukromý klíč pro Mangrove účet", - "nl": "Download de private sleutel van je Mangrove-account" + "nl": "Download de private sleutel van je Mangrove-account", + "hu": "Mangrove-fiók privát kulcsának letöltése" } }, "class": "button", @@ -923,7 +1024,9 @@ "da": "Enhver, der har denne fil, kan lave ændringer med din identitet", "ca": "Qualsevol persona que tingui aquest fitxer pot fer ressenyes amb la vostra identitat", "cs": "Kdokoli, kdo má tento soubor, může provádět recenze s vaší identitou", - "nl": "Iedereen die dit bestand bezit, kan met jouw identiteit wijzigingen maken" + "nl": "Iedereen die dit bestand bezit, kan met jouw identiteit wijzigingen maken", + "hu": "Bárki, aki birtokában van ennek a fájlnak, a te személyazonosságoddal írhat értékelést", + "uk": "Будь-хто, хто має цей файл, може робити рецензії з вашим ім'ям" } } }, @@ -937,14 +1040,17 @@ "en": "Import a mangrove private key from backup", "nl": "Herstel een Mangrove Private sleutel van backup", "de": "Privaten Mangrove-Schlüssel aus Backup importieren", - "da": "Indlæs en privat Mangrove-nøgle fra backup" + "da": "Indlæs en privat Mangrove-nøgle fra backup", + "hu": "Mangrove privát kulcs importálása biztonsági mentésből" } }, "after": { "en": "Uploading a private key erases your current private key. If you made reviews with it, download your current private key first", "de": "Wenn du einen privaten Schlüssel hochlädst, wird dein aktueller privater Schlüssel gelöscht. Wenn du damit Rezensionen geschrieben hast, lade zuerst deinen aktuellen privaten Schlüssel hoch", "nl": "Een private sleutel uploaden overschrijft je huidige private sleutel. Indien je beoordelingen maakte met je huidige private sleutel, download deze dan eerst", - "da": "Når du uploader en privat nøgle, slettes din nuværende private nøgle. Hvis du har brugt den til at lave anmeldelser, kan du downloade din private nøgle først" + "da": "Når du uploader en privat nøgle, slettes din nuværende private nøgle. Hvis du har brugt den til at lave anmeldelser, kan du downloade din private nøgle først", + "hu": "A privát kulcs feltöltése törli a jelenlegi privát kulcsot. Ha írtál vele értékelést, akkor először töltsd le a jelenlegi privát kulcsodat", + "uk": "Завантаження приватного ключа видаляє ваш поточний приватний ключ. Якщо ви робили відгуки з його допомогою, спочатку завантажте поточний приватний ключ" } } }, @@ -968,7 +1074,9 @@ "fr": "Voulez-vous aider à traduire MapComplete ?", "pt": "Quer ajudar na tradução do MapComplete?", "nl": "Wil je MapComplete helpen vertalen?", - "da": "Vil du hjælpe med at oversætte MapComplete?" + "da": "Vil du hjælpe med at oversætte MapComplete?", + "hu": "Szívesen segítenél a MapComplete fordításában?", + "uk": "Ви хочете допомогти з перекладом MapComplete?" }, "mappings": [ { @@ -981,7 +1089,9 @@ "fr": "Ne pas afficher de bouton pour modifier les traductions rapidement", "pt": "Não mostrar um botão para alterar as traduções rapidamente", "cs": "Nezobrazovat tlačítko pro rychlou změnu překladů", - "nl": "Verberg de knoppen om een vertaling in te voeren." + "nl": "Verberg de knoppen om een vertaling in te voeren.", + "hu": "Ne jelenjék meg a fordítások gyors módosítására szolgáló gomb", + "uk": "Не показувати кнопку для швидкої зміни перекладу" } }, { @@ -995,7 +1105,9 @@ "pt": "Mostrar um botão para abrir traduções rapidamente ao usar o MapComplete em uma tela grande", "da": "Vis en knap for hurtigt at åbne oversættelser, når du bruger MapComplete på en stor skærm", "cs": "Zobrazit tlačítko pro rychlé otevření překladů při použití MapComplete na velké obrazovce", - "nl": "Toon een knopje om snel vertalingen te maken indien MapComplete op een groot scherm is geopend" + "nl": "Toon een knopje om snel vertalingen te maken indien MapComplete op een groot scherm is geopend", + "hu": "Jelenítsen meg egy gombot a fordítások gyors megnyitásához a MapComplete nagy képernyőn történő használatakor", + "uk": "Показати кнопку для швидкого відкриття перекладів при використанні MapComplete на великому екрані" } }, { @@ -1008,7 +1120,9 @@ "fr": "Toujours afficher les boutons de traduction, même sur mobile", "pt": "Mostrar sempre os botões de tradução, inclusive no celular", "cs": "Vždy zobrazovat tlačítka překladu, a to i na mobilu", - "nl": "Toon een knopje om snel vertalingen te maken op mobiel en op grote schermen" + "nl": "Toon een knopje om snel vertalingen te maken op mobiel en op grote schermen", + "hu": "Mindig jelenítse meg a fordítási gombokat, mobilon is", + "uk": "Завжди показуйте кнопки перекладу, в тому числі на мобільних пристроях" } } ] @@ -1036,7 +1150,8 @@ "fr": "Cliquez sur l'icône \"traduire\" à côté d'une chaîne de caractères pour saisir ou mettre à jour la chaine de texte. Vous aurez besoin d'un compte Weblate pour cela. Créez-en un avec votre nom d'utilisateur OSM pour déverrouiller automatiquement le mode traduction.", "nl": "Klik op het 'vertaal'-icoontje die naast een stukje tekst staat om deze tekst te vertalen of aan te passen. Hiervoor heb je een (gratis) Weblate-account nodig. Indien je jouw account maakt met dezelfde naam als je OSM-gebruikersnaam, dan zullen de vertaalknoppen automatisch verschijnen.", "zh_Hant": "點字串旁邊的 'translate'-icon 來輸入或是更新一段文字。你需要 Weblate 帳號。用你 OSM 帳號名稱來創建帳號,並且自動解鎖翻譯模式。", - "cs": "Chcete-li zadat nebo aktualizovat část textu, klikněte na ikonu „přeložit“ vedle řetězce. K tomu potřebujete účet Weblate. Vytvořte si jej s vaším uživatelským jménem OSM, abyste automaticky odemkli režim překladu." + "cs": "Chcete-li zadat nebo aktualizovat část textu, klikněte na ikonu „přeložit“ vedle řetězce. K tomu potřebujete účet Weblate. Vytvořte si jej s vaším uživatelským jménem OSM, abyste automaticky odemkli režim překladu.", + "hu": "Kattints a „fordítás” ikonra egy-egy szövegrészlet mellett, ha szeretnéd lefordítani vagy kijavítani. Ehhez szükséged lesz egy Weblate-fiókra. Létrehozhatsz egyet az OSM-felhasználóneveddel, hogy automatikusan fel legyen oldva a fordítási mód." }, "icon": "./assets/svg/translate.svg" } @@ -1058,7 +1173,8 @@ "nl": "Vertalingen voor {_theme} in {_language} zijn momenteel op {_translation_percentage}%: van {_translation_total} teksten zijn er reeds {_translation_translated_count} vertaald", "zh_Hant": "{_theme} 的 {_language} 翻譯目前是 {_translation_percentage}%:{_translation_total} 中的 {_translation_translated_count} 已經翻譯了", "fr": "Les traductions pour {_theme} en {_language} sont à {_translation_percentage} % : {_translation_translated_count} chaînes traduites sur {_translation_total}", - "cs": "Překlady pro {_theme} v {_language} jsou na úrovni {_translation_percentage}%: {_translation_translated_count} z {_translation_total} je přeloženo" + "cs": "Překlady pro {_theme} v {_language} jsou na úrovni {_translation_percentage}%: {_translation_translated_count} z {_translation_total} je přeloženo", + "hu": "A {_theme} térkép {_language} nyelvű fordításai {_translation_percentage}%-ban készen vannak: {_translation_total} szövegrészletből {_translation_translated_count} kész" }, "condition": { "or": [ @@ -1077,7 +1193,8 @@ "de": "Vollständig übersetzt", "ca": "Completament traduït", "pt": "Traduzido completamente", - "cs": "Kompletně přeloženo" + "cs": "Kompletně přeloženo", + "hu": "Teljesen le van fordítva" } } ] @@ -1110,7 +1227,8 @@ "id": "debug-title", "render": { "en": "

Debugging options

", - "de": "

Debugging-Optionen

" + "de": "

Debugging-Optionen

", + "hu": "

Hibakeresési lehetőségek

" } }, { @@ -1121,12 +1239,14 @@ "type": "clear_caches", "text": { "en": "Clear caches", - "de": "Caches löschen" + "de": "Caches löschen", + "hu": "Gyorsítótárak törlése" } }, "after": { "en": "Clearing the caches will delete locally downloaded data and code. You will remain logged in and your settings will be preserved. No data should be lost", - "de": "Wenn du die Caches löschst, werden lokal heruntergeladene Daten und Code gelöscht. Du bleibst eingeloggt und deine Einstellungen bleiben erhalten. Es sollten keine Daten verloren gehen" + "de": "Wenn du die Caches löschst, werden lokal heruntergeladene Daten und Code gelöscht. Du bleibst eingeloggt und deine Einstellungen bleiben erhalten. Es sollten keine Daten verloren gehen", + "hu": "A gyorsítótárak törlése a helyileg letöltött adatokat és kódot is törli. Ugyanakkor továbbra is bejelentkezve maradsz, és a beállításaid megmaradnak. Adat nem vész el" } } }, @@ -1144,7 +1264,8 @@ "cs": "Zobrazit informace o ladění v nastavení?", "ca": "Vols mostrar la informació de depuració de la configuració de l'usuari?", "pt": "Mostrar informações de depuração das configurações do usuário?", - "nl": "Moet debug-informatie getoond worden?" + "nl": "Moet debug-informatie getoond worden?", + "hu": "Megjelenjenek-e a felhasználónak a hibakeresési (debug) információk?" }, "mappings": [ { @@ -1156,7 +1277,8 @@ "cs": "Zobrazit informace o ladění", "ca": "Mostra la informació de depuració", "pt": "Mostrar informações de depuração", - "nl": "Toon debug-informatie" + "nl": "Toon debug-informatie", + "hu": "Jelenjenek meg a hibakeresési információk" } }, { @@ -1168,7 +1290,8 @@ "cs": "Nezobrazovat informace o ladění", "ca": "No mostris informació de depuració", "pt": "Não mostrar informações de depuração", - "nl": "Verberg debug-informatie" + "nl": "Verberg debug-informatie", + "hu": "Ne jelenjenek meg hibakeresési információk" } }, { @@ -1180,7 +1303,8 @@ "pt": "Não mostrar informações de depuração", "da": "Vis ikke debug-informationer", "ca": "No mostris informació de depuració", - "nl": "Verberg debug-informatie" + "nl": "Verberg debug-informatie", + "hu": "Ne jelenjenek meg hibakeresési információk" }, "hideInAnswer": true } diff --git a/assets/layers/waste_disposal/waste_disposal.json b/assets/layers/waste_disposal/waste_disposal.json index ef29ef7fa..3834adb81 100644 --- a/assets/layers/waste_disposal/waste_disposal.json +++ b/assets/layers/waste_disposal/waste_disposal.json @@ -98,7 +98,8 @@ "ca": "Quin tipus de contenidor de brossa és aquest?", "cs": "Co je to za odpadkový koš?", "pl": "Co to za pojemnik na śmieci?", - "zh_Hant": "這是甚麼種類的廢棄物處理箱?" + "zh_Hant": "這是甚麼種類的廢棄物處理箱?", + "uk": "Що це за контейнер для сміття?" }, "mappings": [ { @@ -108,7 +109,8 @@ "de": "Dies ist eine Mülltonne oder ein Müllcontainer für (Haushalts-)Abfälle", "ca": "Es tracta d'un contenidor mitjà o gran per a dipositar residus (domèstics)", "cs": "Jedná se o střední až velkou popelnici na (domovní) odpad", - "zh_Hant": "這是一個中型至大型的桶,用於處理(家庭)廢棄物" + "zh_Hant": "這是一個中型至大型的桶,用於處理(家庭)廢棄物", + "uk": "Це середній або великий контейнер для утилізації (побутових) відходів" } }, { @@ -119,7 +121,8 @@ "ca": "En realitat es tracta d'un contenidor de reciclatge", "cs": "To je vlastně recyklační kontejner", "pl": "W rzeczywistości jest to pojemnik do recyklingu", - "zh_Hant": "這實際上是一個回收容器" + "zh_Hant": "這實際上是一個回收容器", + "uk": "Це, власне, контейнер для переробки сміття" }, "addExtraTags": [ "recycling_type=container" @@ -138,7 +141,8 @@ "da": "Adgang: {access}", "ca": "Accés: {access}", "cs": "Přístup: {access}", - "zh_Hant": "存取:{access}" + "zh_Hant": "存取:{access}", + "uk": "Доступ: {access}" }, "question": { "en": "Who can use this waste disposal bin?", @@ -150,7 +154,8 @@ "ca": "Qui pot utilitzar aquest contenidor de brossa?", "cs": "Kdo může používat tento koš na odpadky?", "pl": "Kto może korzystać z tego pojemnika na odpady?", - "zh_Hant": "誰可以使用這個廢棄物處理桶?" + "zh_Hant": "誰可以使用這個廢棄物處理桶?", + "uk": "Хто може користуватися цим сміттєвим баком?" }, "freeform": { "key": "access", @@ -169,7 +174,8 @@ "ca": "Aquest contenidor es pot utilitzat per qualsevol", "cs": "Tento koš může používat kdokoli", "pl": "Z tego pojemnika może korzystać każdy", - "zh_Hant": "這個桶可以給任何人使用" + "zh_Hant": "這個桶可以給任何人使用", + "uk": "Цим смітником може користуватися будь-хто" } }, { @@ -184,7 +190,8 @@ "ca": "Aquest contenidor és privat", "cs": "Tento koš je soukromý", "pl": "Ten kosz jest prywatny", - "zh_Hant": "這個桶是私人的" + "zh_Hant": "這個桶是私人的", + "uk": "Ця скринька є приватною" } }, { @@ -199,7 +206,8 @@ "ca": "Aquest contenidor és només per als residents", "cs": "Tento koš je určen pouze pro obyvatele", "pl": "Ten kosz jest przeznaczony wyłącznie dla mieszkańców", - "zh_Hant": "這個桶僅供居民使用" + "zh_Hant": "這個桶僅供居民使用", + "uk": "Цей контейнер призначений лише для мешканців" } } ] @@ -216,7 +224,8 @@ "ca": "On es troba aquest contenidor?", "cs": "Kde se nachází tento kontejner?", "pl": "Gdzie znajduje się ten kontener?", - "zh_Hant": "這個容器位於哪裡?" + "zh_Hant": "這個容器位於哪裡?", + "uk": "Де знаходиться цей контейнер?" }, "mappings": [ { @@ -231,7 +240,8 @@ "ca": "Aquest contenidor està soterrat", "cs": "Jedná se o podzemní kontejner", "pl": "To jest podziemny kontener", - "zh_Hant": "這是一個地下容器" + "zh_Hant": "這是一個地下容器", + "uk": "Це підземний контейнер" } }, { @@ -246,7 +256,8 @@ "ca": "Aquest contenidor està situat a l'interior", "cs": "Tento kontejner se nachází uvnitř", "pl": "Kontener ten znajduje się w pomieszczeniu zamkniętym", - "zh_Hant": "這個容器位於室內" + "zh_Hant": "這個容器位於室內", + "uk": "Цей контейнер знаходиться в приміщенні" } }, { @@ -261,7 +272,8 @@ "ca": "Aquest contenidor està situat a l'aire lliure", "cs": "Tento kontejner se nachází venku", "pl": "Kontener ten znajduje się na zewnątrz", - "zh_Hant": "這個容器位於室外" + "zh_Hant": "這個容器位於室外", + "uk": "Цей контейнер розташований на відкритому повітрі" } } ] diff --git a/assets/themes/atm/atm.json b/assets/themes/atm/atm.json index a1d1fe39a..969cfd0de 100644 --- a/assets/themes/atm/atm.json +++ b/assets/themes/atm/atm.json @@ -22,7 +22,7 @@ }, "description": { "en": "This map shows ATMs to withdraw or deposit money", - "de": "Diese Karte zeigt Geldautomaten zum Abheben oder Einzahlen von Geld", + "de": "Diese Karte zeigt Geldautomaten zum Abheben oder Einzahlen von Bargeld", "fr": "Cette carte montre les DABs pour retirer ou déposer de l'argent", "nl": "Deze kaart toont geldautomaten waar je geld kunt afhalen of geld op de bank kan zetten", "ca": "Aquest mapa mostra caixers automàtics per a retirar o ingresar diners", diff --git a/assets/themes/bicycle_rental/bicycle_rental.json b/assets/themes/bicycle_rental/bicycle_rental.json index 757739666..f69583d3b 100644 --- a/assets/themes/bicycle_rental/bicycle_rental.json +++ b/assets/themes/bicycle_rental/bicycle_rental.json @@ -24,7 +24,7 @@ "description": { "en": "On this map, you'll find the many bicycle rental stations as they are known by OpenStreetMap", "nl": "Op deze kaart vind je verschillende fietsverhuurpunten en fietsverhuurzaken", - "de": "Auf dieser Karte finden Sie alle Fahrradverleihstationen, die in OpenStreetMap eingetragen wurden", + "de": "Eine Karte mit allen Fahrradverleihstationen, die in OpenStreetMap eingetragen wurden", "fr": "Vous trouverez sur cette carte toutes les stations de location de vélo telles qu'elles sont référencées dans OpenStreetMap", "es": "En este mapa, encontrará las numerosas estaciones de alquiler de bicicletas tal y como las conoce OpenStreetMap", "da": "På dette kort finder du de mange cykeludlejningsstationer, som OpenStreetMap kender dem", diff --git a/assets/themes/clock/clock.json b/assets/themes/clock/clock.json index a4a4e9d83..092f976c4 100644 --- a/assets/themes/clock/clock.json +++ b/assets/themes/clock/clock.json @@ -21,7 +21,7 @@ "nl": "Kaart met alle openbare klokken", "ca": "Mapa amb tots els rellotges públics", "de": "Eine Karte mit öffentlichen Uhren", - "es": "Mapa mostrando todos los relojes públicos", + "es": "Mapa que muestra todos los relojes públicos", "cs": "Mapa zobrazující veřejné hodiny", "fr": "Carte affichant toutes les horloges publiques", "pl": "Mapa pokazująca wszystkie zegary publiczne", diff --git a/assets/themes/indoors/indoors.json b/assets/themes/indoors/indoors.json index 21e5f52cb..63ef8d0cd 100644 --- a/assets/themes/indoors/indoors.json +++ b/assets/themes/indoors/indoors.json @@ -20,7 +20,7 @@ }, "description": { "en": "On this map, publicly accessible indoor places are shown", - "de": "Diese Karte zeigt öffentlich zugängliche Innenräume", + "de": "Eine Karte mit öffentlich zugänglichen Innenräumen", "fr": "Sur cette carte, les lieux intérieurs accessibles au public sont montrés", "da": "På dette kort er offentligt tilgængelige indendørs steder vist", "nl": "Op deze kaart worden publiek toegankelijke binnenruimtes getoond", diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.proto.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.proto.json index 225de1127..581250ae3 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.proto.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.proto.json @@ -3,12 +3,14 @@ "title": { "en": "Changes made with MapComplete", "de": "Änderungen mit MapComplete", - "cs": "Změny provedené pomocí MapComplete" + "cs": "Změny provedené pomocí MapComplete", + "es": "Cambios realizados con MapComplete" }, "shortDescription": { "en": "Shows changes made by MapComplete", "de": "Zeigt die von MapComplete vorgenommenen Änderungen an", - "cs": "Zobrazuje změny provedené nástrojem MapComplete" + "cs": "Zobrazuje změny provedené nástrojem MapComplete", + "es": "Muestra los cambios realizados por MapComplete" }, "description": { "en": "This maps shows all the changes made with MapComplete", @@ -27,7 +29,8 @@ "id": "mapcomplete-changes", "name": { "en": "Changeset centers", - "de": "Changeset-Zentren" + "de": "Changeset-Zentren", + "es": "Centro del conjunto de cambios" }, "minzoom": 0, "source": { @@ -39,7 +42,8 @@ "render": { "en": "Changeset for {theme}", "de": "Änderungssatz für {theme}", - "cs": "Sada změn pro {theme}" + "cs": "Sada změn pro {theme}", + "es": "Conjunto de cambios para {theme}" } }, "description": { @@ -54,7 +58,8 @@ "render": { "en": "Changeset {id}", "de": "Änderungssatz {id}", - "cs": "Sada změn {id}" + "cs": "Sada změn {id}", + "es": "Conjunto de cambios {id}" } }, { @@ -62,7 +67,8 @@ "question": { "en": "What contributor did make this change?", "de": "Wer hat zu dieser Änderung beigetragen?", - "cs": "Který přispěvatel provedl tuto změnu?" + "cs": "Který přispěvatel provedl tuto změnu?", + "es": "¿Qué colaborador realizó este cambio?" }, "freeform": { "key": "user" @@ -70,7 +76,8 @@ "render": { "en": "Change made by {user}", "de": "Änderung vorgenommen von {user}", - "cs": "Změna provedena uživatelem {user}" + "cs": "Změna provedena uživatelem {user}", + "es": "Cambio realizado por {user}" } }, { @@ -78,14 +85,16 @@ "question": { "en": "What theme was used to make this change?", "de": "Welches Thema wurde für diese Änderung verwendet?", - "cs": "Jaký motiv byl použit k provedení této změny?" + "cs": "Jaký motiv byl použit k provedení této změny?", + "es": "¿Qué tema se utilizó para hacer este cambio?" }, "freeform": { "key": "theme" }, "render": { "en": "Change with theme {theme}", - "de": "Änderung mit Thema {theme}" + "de": "Änderung mit Thema {theme}", + "es": "Cambiar con el tema {theme}" } }, { @@ -96,12 +105,14 @@ "question": { "en": "What locale (language) was this change made in?", "de": "In welcher Sprache (Locale) wurde diese Änderung vorgenommen?", - "cs": "V jakém prostředí (jazyce) byla tato změna provedena?" + "cs": "V jakém prostředí (jazyce) byla tato změna provedena?", + "es": "¿En qué idioma (ubicación) se realizó este cambio?" }, "render": { "en": "User locale is {locale}", "de": "Die Benutzersprache ist {locale}", - "cs": "Uživatelské prostředí je {locale}" + "cs": "Uživatelské prostředí je {locale}", + "es": "La configuración local del usuario es {locale}" } }, { @@ -109,12 +120,14 @@ "render": { "en": "Change with with {host}", "de": "Änderung mit {host}", - "cs": "Změnit pomocí {host}" + "cs": "Změnit pomocí {host}", + "es": "Cambiado con {host}" }, "question": { "en": "What host (website) was this change made with?", "de": "Bei welchem Host (Website) wurde diese Änderung vorgenommen?", - "cs": "U jakého hostitele (webové stránky) byla tato změna provedena?" + "cs": "U jakého hostitele (webové stránky) byla tato změna provedena?", + "es": "¿Con qué host (página web) se hizo este cambio?" }, "freeform": { "key": "host" @@ -137,12 +150,14 @@ "question": { "en": "What version of MapComplete was used to make this change?", "de": "Welche Version von MapComplete wurde verwendet, um diese Änderung vorzunehmen?", - "cs": "Jaká verze aplikace MapComplete byla použita k provedení této změny?" + "cs": "Jaká verze aplikace MapComplete byla použita k provedení této změny?", + "es": "¿Qué versión de MapComplete se utilizó para hacer este cambio?" }, "render": { "en": "Made with {editor}", "de": "Erstellt mit {editor}", - "cs": "Vytvořeno pomocí {editor}" + "cs": "Vytvořeno pomocí {editor}", + "es": "Hecho con {editor}" }, "freeform": { "key": "editor" @@ -344,7 +359,8 @@ "question": { "en": "Exclude GRB theme", "de": "GRB-Thema ausschließen", - "cs": "Vyloučit motiv GRB" + "cs": "Vyloučit motiv GRB", + "es": "Excluir tema GRB" } } ] @@ -375,7 +391,8 @@ "render": { "en": "More statistics can be found here", "de": "Weitere Statistiken findest du hier", - "cs": "Další statistiky najdete zde" + "cs": "Další statistiky najdete zde", + "es": "Se pueden encontrar más estadísticas aquí" } }, { diff --git a/assets/themes/postboxes/postboxes.json b/assets/themes/postboxes/postboxes.json index 60c98e099..2843e7935 100644 --- a/assets/themes/postboxes/postboxes.json +++ b/assets/themes/postboxes/postboxes.json @@ -21,7 +21,7 @@ "zh_Hant": "在這份地圖你能找到與新增有關郵局與郵筒的資料,你可以用這份地圖找到寄送您下張明信片的地方!:)
發現錯誤或是有郵筒遺漏嗎?你可以用免費的開放街圖帳號來編輯。", "hu": "Ezen a térképen postahivatalok és postaládák adatait találod és viheted föl. A térkép segítségével utánanézhetsz, hogy hol adhatod fel a következő képeslapodat! :)
Hibát találtál, vagy hiányzik egy postaláda? A térképet mindössze egy ingyenes OpenStreetMap-fiókkal szerkesztheted.", "de": "Auf dieser Karte können Sie Daten von Poststellen und Briefkästen finden und ergänzen. Sie können diese Karte nutzen, um herauszufinden, wo Sie Ihre nächste Postkarte versenden können :)
Haben Sie einen Fehler entdeckt oder fehlt ein Briefkasten? Sie können die Kartenddaten mit einem kostenlosen OpenStreetMap-Konto bearbeiten.", - "es": "En este mapa podrás buscar y añadir los datos de las oficinas de correos y buzones de correo. ¡Puedes utilizar este mapa para encontrar dónde enviar tu próxima postal! :)
¿Has detectado un error o falta algún buzón de correo? Puedes editar este mapa con una cuenta gratuita de OpenStreetMap.", + "es": "En este mapa puedes encontrar y añadir datos de oficinas postales y buzones. ¡Puedes usar este mapa para encontrar donde enviar tu próxima postal! : )
¿Se ha detectado un error o falta un buzón? Puede editar este mapa con una cuenta gratuita de OpenStreetMap.", "nl": "Op deze kaart kan je informatie over brievenbussen en postkantoren vinden en toevoegen. Je kan deze kaart gebruiken om te achterhalen waar je je volgende postkaart naar kan sturen! :)
Zie je een fout of ontbreekt een brievenbus? Dan kan je deze kaart aanpassen met een gratis OpenStreetMap account. ", "fr": "Trouvez et ajoutez des bureaux de poste et boîtes à lettres sur cette carte. Utilisez cette carte où vous pouvez envoyer vos cartes postales ! :)
Vous avez trouvez une erreur ou une boîte à lettres est manquante ? Vous pouvez modifier cette carte avec un compte OpenStreetMap gratuit.", "da": "På dette kort kan du finde og tilføje data for posthuse og postkasser. Du kan bruge dette kort til at finde, hvor du kan sende dit næste postkort! :)
Har du fundet en fejl, eller mangler der en postboks? Du kan redigere dette kort med en gratis OpenStreetMap-konto. ", diff --git a/langs/es.json b/langs/es.json index 2efbd969e..088a29cdf 100644 --- a/langs/es.json +++ b/langs/es.json @@ -898,4 +898,4 @@ "startsWithQ": "Un identificador wikidata comienza con Q y es seguido por un número" } } -} +} \ No newline at end of file diff --git a/langs/hu.json b/langs/hu.json index c95498742..882c51202 100644 --- a/langs/hu.json +++ b/langs/hu.json @@ -898,4 +898,4 @@ "startsWithQ": "A Wikidata-azonosító Q-val kezdődik, amelyet egy szám követ" } } -} +} \ No newline at end of file diff --git a/langs/layers/ca.json b/langs/layers/ca.json index 8f0d51429..e6a5d6c34 100644 --- a/langs/layers/ca.json +++ b/langs/layers/ca.json @@ -9605,4 +9605,4 @@ "render": "Turbina eòlica" } } -} +} \ No newline at end of file diff --git a/langs/layers/cs.json b/langs/layers/cs.json index 914f0e60d..9f7157c6e 100644 --- a/langs/layers/cs.json +++ b/langs/layers/cs.json @@ -10082,4 +10082,4 @@ "render": "větrná turbína" } } -} +} \ No newline at end of file diff --git a/langs/layers/da.json b/langs/layers/da.json index bf57cb955..b792b89ad 100644 --- a/langs/layers/da.json +++ b/langs/layers/da.json @@ -3061,4 +3061,4 @@ "render": "vindmølle" } } -} +} \ No newline at end of file diff --git a/langs/layers/de.json b/langs/layers/de.json index dd0a6170d..d276d71a5 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -12499,4 +12499,4 @@ "render": "Windrad" } } -} +} \ No newline at end of file diff --git a/langs/layers/en.json b/langs/layers/en.json index 99f89f4e5..40a36590c 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -1561,6 +1561,9 @@ "1": { "then": "Rack with side loops" }, + "10": { + "then": "A lean-to bracket with possibility to use a lock through eyelet. The seat tube can be held by the stand by an anchor" + }, "2": { "then": "Wheelbenders / rack" }, diff --git a/langs/layers/eo.json b/langs/layers/eo.json index 04629a910..7d3920b24 100644 --- a/langs/layers/eo.json +++ b/langs/layers/eo.json @@ -275,4 +275,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/es.json b/langs/layers/es.json index 2c6e5d842..93af59597 100644 --- a/langs/layers/es.json +++ b/langs/layers/es.json @@ -5362,4 +5362,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/eu.json b/langs/layers/eu.json index c4ebc07d3..a4c4ae655 100644 --- a/langs/layers/eu.json +++ b/langs/layers/eu.json @@ -364,4 +364,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/fr.json b/langs/layers/fr.json index 0eb96dca3..da7b4f512 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -7556,4 +7556,4 @@ "render": "éolienne" } } -} +} \ No newline at end of file diff --git a/langs/layers/gl.json b/langs/layers/gl.json index c839468c5..580bc7503 100644 --- a/langs/layers/gl.json +++ b/langs/layers/gl.json @@ -387,4 +387,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/he.json b/langs/layers/he.json index 8aa56d69c..0a846d94e 100644 --- a/langs/layers/he.json +++ b/langs/layers/he.json @@ -484,4 +484,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/hu.json b/langs/layers/hu.json index a9a3ad627..39cefc964 100644 --- a/langs/layers/hu.json +++ b/langs/layers/hu.json @@ -1457,4 +1457,4 @@ "render": "szélerőmű" } } -} +} \ No newline at end of file diff --git a/langs/layers/id.json b/langs/layers/id.json index 53d1a894e..a9705aa62 100644 --- a/langs/layers/id.json +++ b/langs/layers/id.json @@ -1015,4 +1015,4 @@ "render": "turbin angin" } } -} +} \ No newline at end of file diff --git a/langs/layers/it.json b/langs/layers/it.json index 2a98d4f90..a14974143 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -3349,4 +3349,4 @@ "render": "pala eolica" } } -} +} \ No newline at end of file diff --git a/langs/layers/ja.json b/langs/layers/ja.json index e897e9fe0..680606f46 100644 --- a/langs/layers/ja.json +++ b/langs/layers/ja.json @@ -843,4 +843,4 @@ "render": "店" } } -} +} \ No newline at end of file diff --git a/langs/layers/nb_NO.json b/langs/layers/nb_NO.json index eb6996b64..c39755553 100644 --- a/langs/layers/nb_NO.json +++ b/langs/layers/nb_NO.json @@ -993,4 +993,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/nl.json b/langs/layers/nl.json index b4b2eb141..a385b3b2e 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -1063,6 +1063,9 @@ "1": { "then": "Rek met zijbeugel" }, + "10": { + "then": "Een aanleunbeugel met klem waarbij je de zadelbuis in een anker kan vastklikken. Er is meestal een oog om een slot door te steken" + }, "2": { "then": "Wielrek/lussen" }, @@ -9893,4 +9896,4 @@ "render": "windturbine" } } -} +} \ No newline at end of file diff --git a/langs/layers/pa_PK.json b/langs/layers/pa_PK.json index 8a76bba11..9d40b11da 100644 --- a/langs/layers/pa_PK.json +++ b/langs/layers/pa_PK.json @@ -309,4 +309,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/pl.json b/langs/layers/pl.json index 28868c5a3..e7ae159ec 100644 --- a/langs/layers/pl.json +++ b/langs/layers/pl.json @@ -3714,4 +3714,4 @@ "render": "turbina wiatrowa" } } -} +} \ No newline at end of file diff --git a/langs/layers/pt.json b/langs/layers/pt.json index 70314ea32..258334e12 100644 --- a/langs/layers/pt.json +++ b/langs/layers/pt.json @@ -2036,4 +2036,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/pt_BR.json b/langs/layers/pt_BR.json index edf2c3681..329bc33fd 100644 --- a/langs/layers/pt_BR.json +++ b/langs/layers/pt_BR.json @@ -1709,4 +1709,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/ru.json b/langs/layers/ru.json index 39c7bd28b..24d5ea510 100644 --- a/langs/layers/ru.json +++ b/langs/layers/ru.json @@ -2205,4 +2205,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/uk.json b/langs/layers/uk.json index 2351d626e..0e649a6a9 100644 --- a/langs/layers/uk.json +++ b/langs/layers/uk.json @@ -977,4 +977,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/zgh.json b/langs/layers/zgh.json index 7b34b5c4d..4f6fcb199 100644 --- a/langs/layers/zgh.json +++ b/langs/layers/zgh.json @@ -6,4 +6,4 @@ "artwork": { "description": "ⵢⴰⵜ ⵜⴽⴰⵕⴹⴰ ⵉⵕⵥⵎⵏ ⵅⴼ ⵉⵙⴼⵔⵉⵙⵏ, ⵉⵖⵔⴰⵙⵏ ⴷ ⵜⵡⵓⵔⵉⵡⵉⵏ ⵜⵉⵏⴰⵥⵓⵕⵉⵏ ⵢⴰⴹⵏⵉⵏ ⴳ ⵓⵎⴰⴹⴰⵍ" } -} +} \ No newline at end of file diff --git a/langs/layers/zh_Hans.json b/langs/layers/zh_Hans.json index ab86f12eb..bb86f5113 100644 --- a/langs/layers/zh_Hans.json +++ b/langs/layers/zh_Hans.json @@ -686,4 +686,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/layers/zh_Hant.json b/langs/layers/zh_Hant.json index 8309093ea..513e0c6c0 100644 --- a/langs/layers/zh_Hant.json +++ b/langs/layers/zh_Hant.json @@ -1158,4 +1158,4 @@ "render": "風機" } } -} +} \ No newline at end of file diff --git a/langs/themes/ca.json b/langs/themes/ca.json index 36f9e2cce..5be8957e0 100644 --- a/langs/themes/ca.json +++ b/langs/themes/ca.json @@ -1075,4 +1075,4 @@ "shortDescription": "Un mapa amb papereres", "title": "Papepera" } -} +} \ No newline at end of file diff --git a/langs/themes/cs.json b/langs/themes/cs.json index 78623aa7f..64d803b61 100644 --- a/langs/themes/cs.json +++ b/langs/themes/cs.json @@ -1413,4 +1413,4 @@ "shortDescription": "Mapa odpadkových košů", "title": "Odpadkové koše" } -} +} \ No newline at end of file diff --git a/langs/themes/da.json b/langs/themes/da.json index ec2a068e7..f780a079f 100644 --- a/langs/themes/da.json +++ b/langs/themes/da.json @@ -846,4 +846,4 @@ "shortDescription": "Et kort over skraldespande", "title": "Skraldespande" } -} +} \ No newline at end of file diff --git a/langs/themes/de.json b/langs/themes/de.json index 26994ea93..9d8560b4f 100644 --- a/langs/themes/de.json +++ b/langs/themes/de.json @@ -1415,4 +1415,4 @@ "shortDescription": "Eine Karte mit Abfalleimern", "title": "Abfalleimer" } -} +} \ No newline at end of file diff --git a/langs/themes/es.json b/langs/themes/es.json index fa7c28734..797111a95 100644 --- a/langs/themes/es.json +++ b/langs/themes/es.json @@ -1415,4 +1415,4 @@ "shortDescription": "Un mapa con papeleras", "title": "Papeleras" } -} +} \ No newline at end of file diff --git a/langs/themes/eu.json b/langs/themes/eu.json index 0ff070ae6..3f9fad545 100644 --- a/langs/themes/eu.json +++ b/langs/themes/eu.json @@ -216,4 +216,4 @@ "waste": { "title": "Hondakinak eta birziklapena" } -} +} \ No newline at end of file diff --git a/langs/themes/fil.json b/langs/themes/fil.json index 9c48d05e1..1b6aa16ca 100644 --- a/langs/themes/fil.json +++ b/langs/themes/fil.json @@ -58,4 +58,4 @@ "blind_osm": { "title": "OSM para sa mga bulag" } -} +} \ No newline at end of file diff --git a/langs/themes/fr.json b/langs/themes/fr.json index 0680da154..fdffc9531 100644 --- a/langs/themes/fr.json +++ b/langs/themes/fr.json @@ -1004,4 +1004,4 @@ "shortDescription": "Une carte des poubelles", "title": "Poubelles" } -} +} \ No newline at end of file diff --git a/langs/themes/hu.json b/langs/themes/hu.json index 0ed9ff4cb..ffc4238bf 100644 --- a/langs/themes/hu.json +++ b/langs/themes/hu.json @@ -530,4 +530,4 @@ "shortDescription": "Szemeteskosarakat ábrázoló térkép", "title": "Kukatérkép" } -} +} \ No newline at end of file diff --git a/langs/themes/id.json b/langs/themes/id.json index 4ca326538..98d1b3547 100644 --- a/langs/themes/id.json +++ b/langs/themes/id.json @@ -90,4 +90,4 @@ "waste_basket": { "title": "Keranjang Sampah" } -} +} \ No newline at end of file diff --git a/langs/themes/it.json b/langs/themes/it.json index a5a8e3593..016494c80 100644 --- a/langs/themes/it.json +++ b/langs/themes/it.json @@ -674,4 +674,4 @@ "shortDescription": "Una cartina dei cestini dei rifiuti", "title": "Cestino dei rifiuti" } -} +} \ No newline at end of file diff --git a/langs/themes/ja.json b/langs/themes/ja.json index f3616888d..bf15d1ff7 100644 --- a/langs/themes/ja.json +++ b/langs/themes/ja.json @@ -250,4 +250,4 @@ "shortDescription": "すべての樹木をマッピングする", "title": "樹木" } -} +} \ No newline at end of file diff --git a/langs/themes/nb_NO.json b/langs/themes/nb_NO.json index 894e96209..70228236d 100644 --- a/langs/themes/nb_NO.json +++ b/langs/themes/nb_NO.json @@ -582,4 +582,4 @@ "shortDescription": "Oversikt over søppelkurver", "title": "Søppelkurv" } -} +} \ No newline at end of file diff --git a/langs/themes/nl.json b/langs/themes/nl.json index 789dd4c40..12a1b877d 100644 --- a/langs/themes/nl.json +++ b/langs/themes/nl.json @@ -1149,4 +1149,4 @@ "shortDescription": "Een kaart met vuilnisbakken", "title": "Vuilnisbakken" } -} +} \ No newline at end of file diff --git a/langs/themes/pl.json b/langs/themes/pl.json index 9fd09501a..347bf3fdd 100644 --- a/langs/themes/pl.json +++ b/langs/themes/pl.json @@ -1035,4 +1035,4 @@ "shortDescription": "Mapa koszy na śmieci", "title": "Kosz na śmieci" } -} +} \ No newline at end of file diff --git a/langs/themes/pt.json b/langs/themes/pt.json index 3a08ba818..f4e2e485a 100644 --- a/langs/themes/pt.json +++ b/langs/themes/pt.json @@ -36,4 +36,4 @@ "description": "Mapeie todas as árvores!", "title": "Árvores" } -} +} \ No newline at end of file diff --git a/langs/themes/pt_BR.json b/langs/themes/pt_BR.json index 0538ac11f..f73157758 100644 --- a/langs/themes/pt_BR.json +++ b/langs/themes/pt_BR.json @@ -41,4 +41,4 @@ "description": "Mapeie todas as árvores!", "title": "Árvores" } -} +} \ No newline at end of file diff --git a/langs/themes/ru.json b/langs/themes/ru.json index b607086a6..45a603a39 100644 --- a/langs/themes/ru.json +++ b/langs/themes/ru.json @@ -274,4 +274,4 @@ "waste_basket": { "title": "Корзины для мусора" } -} +} \ No newline at end of file diff --git a/langs/themes/uk.json b/langs/themes/uk.json index 009580b0b..f9072856a 100644 --- a/langs/themes/uk.json +++ b/langs/themes/uk.json @@ -278,4 +278,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/themes/zgh.json b/langs/themes/zgh.json index 81f94eda6..6bb855698 100644 --- a/langs/themes/zgh.json +++ b/langs/themes/zgh.json @@ -30,4 +30,4 @@ } } } -} +} \ No newline at end of file diff --git a/langs/themes/zh_Hans.json b/langs/themes/zh_Hans.json index d0599e8cd..0c6302b08 100644 --- a/langs/themes/zh_Hans.json +++ b/langs/themes/zh_Hans.json @@ -129,4 +129,4 @@ "campersite": { "description": "该网站汇总了所有官方的露营车停留点以及可以倾倒灰水和黑水的地点。你可以添加有关提供的服务和费用的详细信息,并且可以上传图片和撰写评论。" } -} +} \ No newline at end of file diff --git a/langs/themes/zh_Hant.json b/langs/themes/zh_Hant.json index 6643da7eb..a76a102df 100644 --- a/langs/themes/zh_Hant.json +++ b/langs/themes/zh_Hant.json @@ -491,4 +491,4 @@ "shortDescription": "垃圾筒的地圖", "title": "垃圾筒" } -} +} \ No newline at end of file diff --git a/langs/uk.json b/langs/uk.json index 39be02043..ac1ac7478 100644 --- a/langs/uk.json +++ b/langs/uk.json @@ -388,4 +388,4 @@ "description": "Години роботи" } } -} +} \ No newline at end of file diff --git a/scripts/Script.ts b/scripts/Script.ts index ee71041a9..b8fb4f400 100644 --- a/scripts/Script.ts +++ b/scripts/Script.ts @@ -24,7 +24,7 @@ export default abstract class Script { }) .catch((e) => { console.log(`ERROR in script ${process.argv[1]}:`, e) - // process.exit(1) + // process.exit(1) }) } diff --git a/scripts/ScriptUtils.ts b/scripts/ScriptUtils.ts index ac3a915bb..66ca1e815 100644 --- a/scripts/ScriptUtils.ts +++ b/scripts/ScriptUtils.ts @@ -105,14 +105,15 @@ export default class ScriptUtils { const specialfiles = ["./assets/themes/mapcomplete-changes/mapcomplete-changes.proto.json"] const blacklist = ["assets/themes/mapcomplete-changes/mapcomplete-changes.json"] - const filtered = normalFiles.filter(path => !blacklist.some(black => path.endsWith(black))) + const filtered = normalFiles.filter( + (path) => !blacklist.some((black) => path.endsWith(black)) + ) return filtered.concat(specialfiles) - } public static getThemeFiles(useTranslationPaths = false): { - parsed: ThemeConfigJson; - path: string; + parsed: ThemeConfigJson + path: string raw: string }[] { return this.getThemePaths(useTranslationPaths).map((path) => { @@ -209,17 +210,17 @@ export default class ScriptUtils { path: urlObj.pathname + urlObj.search, port: urlObj.port, - headers: headers + headers: headers, }, (res) => { const parts: string[] = [] res.setEncoding("utf8") - res.on("data", function(chunk) { + res.on("data", function (chunk) { // @ts-ignore parts.push(chunk) }) - res.addListener("end", function() { + res.addListener("end", function () { if (res.statusCode === 301 || res.statusCode === 302) { console.log("Got a redirect:", res.headers.location) resolve({ redirect: res.headers.location }) @@ -237,7 +238,7 @@ export default class ScriptUtils { }) } ) - request.on("error", function(e) { + request.on("error", function (e) { reject(e) }) } catch (e) { diff --git a/scripts/downloadEli.ts b/scripts/downloadEli.ts index 2fa15f362..68abee9d0 100644 --- a/scripts/downloadEli.ts +++ b/scripts/downloadEli.ts @@ -98,24 +98,21 @@ class DownloadEli extends Script { } layer = { properties: layer.properties, type: layer.type, geometry: layer.geometry } - if(layer.geometry === null){ + if (layer.geometry === null) { keptGlobalLayers.push(layer) - }else{ + } else { keptLayers.push(layer) } } const contents = '{"type":"FeatureCollection",\n "features": [\n' + - keptLayers - .map((l) => JSON.stringify(l)) - .join(",\n") + + keptLayers.map((l) => JSON.stringify(l)).join(",\n") + "\n]}" - const contentsGlobal = - keptGlobalLayers - .filter((l) => l.properties.id !== "Bing") - .map(l => l.properties) + const contentsGlobal = keptGlobalLayers + .filter((l) => l.properties.id !== "Bing") + .map((l) => l.properties) const bing = keptGlobalLayers.find((l) => l.properties.id === "Bing") if (bing) { @@ -126,9 +123,10 @@ class DownloadEli extends Script { } fs.writeFileSync(target, contents, { encoding: "utf8" }) console.log("Written", keptLayers.length + ", entries to the ELI") - fs.writeFileSync(targetGlobal, JSON.stringify(contentsGlobal,null, " "), { encoding: "utf8" }) + fs.writeFileSync(targetGlobal, JSON.stringify(contentsGlobal, null, " "), { + encoding: "utf8", + }) console.log("Written", keptGlobalLayers.length + ", entries to the global ELI") - } } diff --git a/scripts/fetchLanguages.ts b/scripts/fetchLanguages.ts index 25b51fa38..e01558500 100644 --- a/scripts/fetchLanguages.ts +++ b/scripts/fetchLanguages.ts @@ -46,7 +46,9 @@ async function fetchRegularLanguages() { const url = Wikidata.wds.sparqlQuery(sparql) // request the generated URL with your favorite HTTP request library - const result = await Utils.downloadJson<{results: {bindings: any[]}}>(url, { "User-Agent": "MapComplete script" }) + const result = await Utils.downloadJson<{ results: { bindings: any[] } }>(url, { + "User-Agent": "MapComplete script", + }) const bindings = result.results.bindings // Traditional chinese = 繁體中文 or 正體中文 @@ -98,7 +100,9 @@ async function fetchSpecial(id: number, code: string): Promise(url, { "User-Agent": "MapComplete script" }) + const result = await Utils.downloadJson<{ results: { bindings: any[] } }>(url, { + "User-Agent": "MapComplete script", + }) const bindings = result.results.bindings bindings.forEach((binding) => (binding["code"] = { value: code })) return bindings @@ -134,7 +138,9 @@ async function getOfficialLanguagesPerCountry(): Promise> }` const url = Wikidata.wds.sparqlQuery(sparql) - const result = await Utils.downloadJson<{results: {bindings: any[]}}>(url, { "User-Agent": "MapComplete script" }) + const result = await Utils.downloadJson<{ results: { bindings: any[] } }>(url, { + "User-Agent": "MapComplete script", + }) const bindings: { countryCode: { value: string }; languageCode: { value: string } }[] = result.results.bindings for (const binding of bindings) { diff --git a/scripts/generateDocs.ts b/scripts/generateDocs.ts index ab6ccc680..ccdda66c8 100644 --- a/scripts/generateDocs.ts +++ b/scripts/generateDocs.ts @@ -458,9 +458,7 @@ export class GenerateDocs extends Script { private generateForTheme(theme: ThemeConfig): void { const allLayers = AllSharedLayers.getSharedLayersConfigs() const layersToShow = theme.layers.filter( - (l) => - l.id !== "favourite" && - Constants.added_by_default.indexOf(l.id) < 0 + (l) => l.id !== "favourite" && Constants.added_by_default.indexOf(l.id) < 0 ) const layersToInline = layersToShow.filter((l) => !allLayers.has(l.id)) const el = [ diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index 61301eeeb..74cfa9a43 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -34,9 +34,7 @@ import Translations from "../src/UI/i18n/Translations" import { Translatable } from "../src/Models/ThemeConfig/Json/Translatable" import { ValidateThemeAndLayers } from "../src/Models/ThemeConfig/Conversion/ValidateThemeAndLayers" import { ExtractImages } from "../src/Models/ThemeConfig/Conversion/FixImages" -import { - TagRenderingConfigJson, -} from "../src/Models/ThemeConfig/Json/TagRenderingConfigJson" +import { TagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/TagRenderingConfigJson" // This scripts scans 'src/assets/layers/*.json' for layer definition files and 'src/assets/themes/*.json' for theme definition files. // It spits out an overview of those to be used to load them @@ -59,7 +57,7 @@ class ParseLayer extends Conversion< convert( path: string, - context: ConversionContext, + context: ConversionContext ): { parsed: LayerConfig raw: LayerConfigJson @@ -88,7 +86,7 @@ class ParseLayer extends Conversion< context .enter("source") .err( - "No source is configured. (Tags might be automatically derived if presets are given)", + "No source is configured. (Tags might be automatically derived if presets are given)" ) return undefined } @@ -119,7 +117,7 @@ class AddIconSummary extends DesugaringStep<{ raw: LayerConfigJson; parsed: Laye const fixed = json.raw const layerConfig = json.parsed const pointRendering: PointRenderingConfig = layerConfig.mapRendering.find((pr) => - pr.location.has("point"), + pr.location.has("point") ) const defaultTags = layerConfig.GetBaseTags() fixed["_layerIcon"] = Utils.NoNull( @@ -134,7 +132,7 @@ class AddIconSummary extends DesugaringStep<{ raw: LayerConfigJson; parsed: Laye result["color"] = c } return result - }), + }) ) return { raw: fixed, parsed: layerConfig } } @@ -156,7 +154,7 @@ class LayerOverviewUtils extends Script { private static extractLayerIdsFrom( themeFile: ThemeConfigJson, - includeInlineLayers = true, + includeInlineLayers = true ): string[] { const publicLayerIds: string[] = [] if (!Array.isArray(themeFile.layers)) { @@ -211,11 +209,14 @@ class LayerOverviewUtils extends Script { return false } - static mergeKeywords(into: Record, source: Readonly>){ + static mergeKeywords( + into: Record, + source: Readonly> + ) { for (const key in source) { - if(into[key]){ + if (into[key]) { into[key].push(...source[key]) - }else{ + } else { into[key] = source[key] } } @@ -225,13 +226,13 @@ class LayerOverviewUtils extends Script { const keywords: Record = {} function addWord(language: string, word: string | string[]) { - if(Array.isArray(word)){ - word.forEach(w => addWord(language, w)) + if (Array.isArray(word)) { + word.forEach((w) => addWord(language, w)) return } word = Utils.SubstituteKeys(word, {})?.trim() - if(!word){ + if (!word) { return } if (!keywords[language]) { @@ -240,8 +241,10 @@ class LayerOverviewUtils extends Script { keywords[language].push(word) } - function addWords(tr: string | Record | Record | TagRenderingConfigJson) { - if(!tr){ + function addWords( + tr: string | Record | Record | TagRenderingConfigJson + ) { + if (!tr) { return } if (typeof tr === "string") { @@ -283,32 +286,30 @@ class LayerOverviewUtils extends Script { | LayerConfigJson | string | { - builtin - } - )[] + builtin + } + )[] }[], sharedLayers: Map ) { - const layerKeywords : Record> = {} + const layerKeywords: Record> = {} sharedLayers.forEach((layer, id) => { - layerKeywords[id] = this.layerKeywords(layer) + layerKeywords[id] = this.layerKeywords(layer) }) const perId = new Map() for (const theme of themes) { - const keywords: Record = {} for (const layer of theme.layers ?? []) { const l = layer - if(sharedLayers.has(l.id)){ + if (sharedLayers.has(l.id)) { continue } LayerOverviewUtils.mergeKeywords(keywords, this.layerKeywords(l)) - } - const data = { + const data = { id: theme.id, title: theme.title, shortDescription: LayerOverviewUtils.cleanTranslation(theme.shortDescription), @@ -316,7 +317,7 @@ class LayerOverviewUtils extends Script { hideFromOverview: theme.hideFromOverview, mustHaveLanguage: theme.mustHaveLanguage, keywords, - layers: theme.layers.filter(l => sharedLayers.has(l["id"])).map(l => l["id"]) + layers: theme.layers.filter((l) => sharedLayers.has(l["id"])).map((l) => l["id"]), } perId.set(theme.id, data) } @@ -338,7 +339,7 @@ class LayerOverviewUtils extends Script { writeFileSync( "./src/assets/generated/theme_overview.json", JSON.stringify({ layers: layerKeywords, themes: sorted }, null, " "), - { encoding: "utf8" }, + { encoding: "utf8" } ) } @@ -350,7 +351,7 @@ class LayerOverviewUtils extends Script { writeFileSync( `${LayerOverviewUtils.themePath}${theme.id}.json`, JSON.stringify(theme, null, " "), - { encoding: "utf8" }, + { encoding: "utf8" } ) } @@ -361,12 +362,12 @@ class LayerOverviewUtils extends Script { writeFileSync( `${LayerOverviewUtils.layerPath}${layer.id}.json`, JSON.stringify(layer, null, " "), - { encoding: "utf8" }, + { encoding: "utf8" } ) } static asDict( - trs: QuestionableTagRenderingConfigJson[], + trs: QuestionableTagRenderingConfigJson[] ): Map { const d = new Map() for (const tr of trs) { @@ -379,12 +380,12 @@ class LayerOverviewUtils extends Script { getSharedTagRenderings( doesImageExist: DoesImageExist, bootstrapTagRenderings: Map, - bootstrapTagRenderingsOrder: string[], + bootstrapTagRenderingsOrder: string[] ): QuestionableTagRenderingConfigJson[] getSharedTagRenderings( doesImageExist: DoesImageExist, bootstrapTagRenderings: Map = null, - bootstrapTagRenderingsOrder: string[] = [], + bootstrapTagRenderingsOrder: string[] = [] ): QuestionableTagRenderingConfigJson[] { const prepareLayer = new PrepareLayer( { @@ -395,7 +396,7 @@ class LayerOverviewUtils extends Script { }, { addTagRenderingsToContext: true, - }, + } ) const path = "assets/layers/questions/questions.json" @@ -415,7 +416,7 @@ class LayerOverviewUtils extends Script { return this.getSharedTagRenderings( doesImageExist, dict, - sharedQuestions.tagRenderings.map((tr) => tr["id"]), + sharedQuestions.tagRenderings.map((tr) => tr["id"]) ) } @@ -455,8 +456,8 @@ class LayerOverviewUtils extends Script { if (contents.indexOf(" 0) { console.warn( "The SVG at " + - path + - " contains a `text`-tag. This is highly discouraged. Every machine viewing your theme has their own font libary, and the font you choose might not be present, resulting in a different font being rendered. Solution: open your .svg in inkscape (or another program), select the text and convert it to a path", + path + + " contains a `text`-tag. This is highly discouraged. Every machine viewing your theme has their own font libary, and the font you choose might not be present, resulting in a different font being rendered. Solution: open your .svg in inkscape (or another program), select the text and convert it to a path" ) errCount++ } @@ -472,14 +473,14 @@ class LayerOverviewUtils extends Script { args .find((a) => a.startsWith("--themes=")) ?.substring("--themes=".length) - ?.split(",") ?? [], + ?.split(",") ?? [] ) const layerWhitelist = new Set( args .find((a) => a.startsWith("--layers=")) ?.substring("--layers=".length) - ?.split(",") ?? [], + ?.split(",") ?? [] ) const forceReload = args.some((a) => a == "--force") @@ -501,8 +502,8 @@ class LayerOverviewUtils extends Script { priviliged.delete("last_click") priviliged.delete("search") - const isBoostrapping = AllSharedLayers.getSharedLayersConfigs().size == 0 - if (!isBoostrapping && priviliged.size > 0) { + const isBoostrapping = AllSharedLayers.getSharedLayersConfigs().size == 0 + if (!isBoostrapping && priviliged.size > 0) { throw ( "Priviliged layer " + Array.from(priviliged).join(", ") + @@ -515,11 +516,11 @@ class LayerOverviewUtils extends Script { sharedLayers, recompiledThemes, forceReload, - themeWhitelist, + themeWhitelist ) new ValidateThemeEnsemble().convertStrict( - Array.from(sharedThemes.values()).map((th) => new ThemeConfig(th, true)), + Array.from(sharedThemes.values()).map((th) => new ThemeConfig(th, true)) ) if (recompiledThemes.length > 0) { @@ -527,7 +528,7 @@ class LayerOverviewUtils extends Script { "./src/assets/generated/known_layers.json", JSON.stringify({ layers: Array.from(sharedLayers.values()).filter((l) => l.id !== "favourite"), - }), + }) ) } @@ -548,7 +549,7 @@ class LayerOverviewUtils extends Script { const proto: ThemeConfigJson = JSON.parse( readFileSync("./assets/themes/mapcomplete-changes/mapcomplete-changes.proto.json", { encoding: "utf8", - }), + }) ) const protolayer = ( proto.layers.filter((l) => l["id"] === "mapcomplete-changes")[0] @@ -565,12 +566,12 @@ class LayerOverviewUtils extends Script { layers: ScriptUtils.getLayerFiles().map((f) => f.parsed), themes: ScriptUtils.getThemeFiles().map((f) => f.parsed), }, - ConversionContext.construct([], []), + ConversionContext.construct([], []) ) for (const [_, theme] of sharedThemes) { theme.layers = theme.layers.filter( - (l) => Constants.added_by_default.indexOf(l["id"]) < 0, + (l) => Constants.added_by_default.indexOf(l["id"]) < 0 ) } @@ -579,7 +580,7 @@ class LayerOverviewUtils extends Script { "./src/assets/generated/known_themes.json", JSON.stringify({ themes: Array.from(sharedThemes.values()), - }), + }) ) } @@ -591,7 +592,7 @@ class LayerOverviewUtils extends Script { private parseLayer( doesImageExist: DoesImageExist, prepLayer: PrepareLayer, - sharedLayerPath: string, + sharedLayerPath: string ): { raw: LayerConfigJson parsed: LayerConfig @@ -602,7 +603,7 @@ class LayerOverviewUtils extends Script { const parsed = parser.convertStrict(sharedLayerPath, context) const result = AddIconSummary.singleton.convertStrict( parsed, - context.inOperation("AddIconSummary"), + context.inOperation("AddIconSummary") ) return { ...result, context } } @@ -610,7 +611,7 @@ class LayerOverviewUtils extends Script { private buildLayerIndex( doesImageExist: DoesImageExist, forceReload: boolean, - whitelist: Set, + whitelist: Set ): Map { // First, we expand and validate all builtin layers. These are written to src/assets/generated/layers // At the same time, an index of available layers is built. @@ -665,17 +666,17 @@ class LayerOverviewUtils extends Script { console.log( "Recompiled layers " + - recompiledLayers.join(", ") + - " and skipped " + - skippedLayers.length + - " layers. Detected " + - warningCount + - " warnings", + recompiledLayers.join(", ") + + " and skipped " + + skippedLayers.length + + " layers. Detected " + + warningCount + + " warnings" ) // We always need the calculated tags of 'usersettings', so we export them separately this.extractJavascriptCodeForLayer( state.sharedLayers.get("usersettings"), - "./src/Logic/State/UserSettingsMetaTagging.ts", + "./src/Logic/State/UserSettingsMetaTagging.ts" ) return sharedLayers @@ -692,8 +693,8 @@ class LayerOverviewUtils extends Script { private extractJavascriptCode(themeFile: ThemeConfigJson) { const allCode = [ "import {Feature} from 'geojson'", - "import { ExtraFuncType } from \"../../../Logic/ExtraFunctions\";", - "import { Utils } from \"../../../Utils\"", + 'import { ExtraFuncType } from "../../../Logic/ExtraFunctions";', + 'import { Utils } from "../../../Utils"', "export class ThemeMetaTagging {", " public static readonly themeName = " + JSON.stringify(themeFile.id), "", @@ -705,8 +706,8 @@ class LayerOverviewUtils extends Script { allCode.push( " public metaTaggging_for_" + - id + - "(feat: Feature, helperFunctions: Record Function>) {", + id + + "(feat: Feature, helperFunctions: Record Function>) {" ) allCode.push(" const {" + ExtraFunctions.types.join(", ") + "} = helperFunctions") for (const line of code) { @@ -717,10 +718,10 @@ class LayerOverviewUtils extends Script { if (!isStrict) { allCode.push( " Utils.AddLazyProperty(feat.properties, '" + - attributeName + - "', () => " + - expression + - " ) ", + attributeName + + "', () => " + + expression + + " ) " ) } else { attributeName = attributeName.substring(0, attributeName.length - 1).trim() @@ -765,7 +766,7 @@ class LayerOverviewUtils extends Script { const code = l.calculatedTags ?? [] allCode.push( - " public metaTaggging_for_" + l.id + "(feat: {properties: Record}) {", + " public metaTaggging_for_" + l.id + "(feat: {properties: Record}) {" ) for (const line of code) { const firstEq = line.indexOf("=") @@ -775,10 +776,10 @@ class LayerOverviewUtils extends Script { if (!isStrict) { allCode.push( " Utils.AddLazyProperty(feat.properties, '" + - attributeName + - "', () => " + - expression + - " ) ", + attributeName + + "', () => " + + expression + + " ) " ) } else { attributeName = attributeName.substring(0, attributeName.length - 2).trim() @@ -803,14 +804,14 @@ class LayerOverviewUtils extends Script { sharedLayers: Map, recompiledThemes: string[], forceReload: boolean, - whitelist: Set, + whitelist: Set ): Map { console.log(" ---------- VALIDATING BUILTIN THEMES ---------") const themeFiles = ScriptUtils.getThemeFiles() const fixed = new Map() const publicLayers = LayerOverviewUtils.publicLayerIdsFrom( - themeFiles.map((th) => th.parsed), + themeFiles.map((th) => th.parsed) ) const trs = this.getSharedTagRenderings(new DoesImageExist(licensePaths, existsSync)) @@ -850,15 +851,15 @@ class LayerOverviewUtils extends Script { LayerOverviewUtils.themePath + "/" + themePath.substring(themePath.lastIndexOf("/")) const usedLayers = Array.from( - LayerOverviewUtils.extractLayerIdsFrom(themeFile, false), + LayerOverviewUtils.extractLayerIdsFrom(themeFile, false) ).map((id) => LayerOverviewUtils.layerPath + id + ".json") if (!forceReload && !this.shouldBeUpdated([themePath, ...usedLayers], targetPath)) { fixed.set( themeFile.id, JSON.parse( - readFileSync(LayerOverviewUtils.themePath + themeFile.id + ".json", "utf8"), - ), + readFileSync(LayerOverviewUtils.themePath + themeFile.id + ".json", "utf8") + ) ) ScriptUtils.erasableLog("Skipping", themeFile.id) skippedThemes.push(themeFile.id) @@ -869,23 +870,23 @@ class LayerOverviewUtils extends Script { new PrevalidateTheme().convertStrict( themeFile, - ConversionContext.construct([themePath], ["PrepareLayer"]), + ConversionContext.construct([themePath], ["PrepareLayer"]) ) try { themeFile = new PrepareTheme(convertState, { skipDefaultLayers: true, }).convertStrict( themeFile, - ConversionContext.construct([themePath], ["PrepareLayer"]), + ConversionContext.construct([themePath], ["PrepareLayer"]) ) new ValidateThemeAndLayers( new DoesImageExist(licensePaths, existsSync, knownTagRenderings), themePath, true, - knownTagRenderings, + knownTagRenderings ).convertStrict( themeFile, - ConversionContext.construct([themePath], ["PrepareLayer"]), + ConversionContext.construct([themePath], ["PrepareLayer"]) ) if (themeFile.icon.endsWith(".svg")) { @@ -935,7 +936,7 @@ class LayerOverviewUtils extends Script { const usedImages = Utils.Dedup( new ExtractImages(true, knownTagRenderings) .convertStrict(themeFile) - .map((x) => x.path), + .map((x) => x.path) ) usedImages.sort() @@ -954,7 +955,7 @@ class LayerOverviewUtils extends Script { if (whitelist.size == 0) { this.writeSmallOverview( Array.from(fixed.values()).map((t) => { - return { + return { ...t, hideFromOverview: t.hideFromOverview ?? false, shortDescription: @@ -968,10 +969,10 @@ class LayerOverviewUtils extends Script { console.log( "Recompiled themes " + - recompiledThemes.join(", ") + - " and skipped " + - skippedThemes.length + - " themes", + recompiledThemes.join(", ") + + " and skipped " + + skippedThemes.length + + " themes" ) return fixed diff --git a/scripts/generateLayouts.ts b/scripts/generateLayouts.ts index 4066360bc..d62c2008f 100644 --- a/scripts/generateLayouts.ts +++ b/scripts/generateLayouts.ts @@ -331,7 +331,7 @@ class GenerateLayouts extends Script { "https://api.openstreetmap.org", "https://pietervdvn.goatcounter.com", "https://api.panoramax.xyz", - "https://panoramax.mapcomplete.org" + "https://panoramax.mapcomplete.org", ].concat(...(await this.eliUrls())) SpecialVisualizations.specialVisualizations.forEach((sv) => { diff --git a/scripts/generateTranslations.ts b/scripts/generateTranslations.ts index 23097aaa6..b9c08b51e 100644 --- a/scripts/generateTranslations.ts +++ b/scripts/generateTranslations.ts @@ -49,7 +49,7 @@ class TranslationPart { } for (const translationsKey in translations) { const v = translations[translationsKey] - if(Array.isArray(v) && context .endsWith("keywords")){ + if (Array.isArray(v) && context.endsWith("keywords")) { continue } if (typeof v != "string") { diff --git a/src/Logic/Actors/TitleHandler.ts b/src/Logic/Actors/TitleHandler.ts index 387aab898..bb4f70ca5 100644 --- a/src/Logic/Actors/TitleHandler.ts +++ b/src/Logic/Actors/TitleHandler.ts @@ -5,10 +5,7 @@ import { Feature } from "geojson" import { SpecialVisualizationState } from "../../UI/SpecialVisualization" export default class TitleHandler { - constructor( - selectedElement: Store, - state: SpecialVisualizationState - ) { + constructor(selectedElement: Store, state: SpecialVisualizationState) { const currentTitle: Store = selectedElement.map( (selected) => { const lng = Locale.language.data @@ -34,7 +31,6 @@ export default class TitleHandler { const el = document.createElement("span") el.innerHTML = title return el.textContent + " | " + defaultTitle - }, [Locale.language] ) diff --git a/src/Logic/BBox.ts b/src/Logic/BBox.ts index a5fa3c46a..5077842ee 100644 --- a/src/Logic/BBox.ts +++ b/src/Logic/BBox.ts @@ -6,7 +6,7 @@ import { Feature, Polygon } from "geojson" export class BBox { static global: BBox = new BBox([ [-180, -90], - [180, 90] + [180, 90], ]) readonly maxLat: number readonly maxLon: number @@ -53,7 +53,7 @@ export class BBox { static fromLeafletBounds(bounds) { return new BBox([ [bounds.getWest(), bounds.getNorth()], - [bounds.getEast(), bounds.getSouth()] + [bounds.getEast(), bounds.getSouth()], ]) } @@ -74,7 +74,7 @@ export class BBox { // Note: x is longitude f["bbox"] = new BBox([ [minX, minY], - [maxX, maxY] + [maxX, maxY], ]) } return f["bbox"] @@ -94,7 +94,7 @@ export class BBox { } return new BBox([ [maxLon, maxLat], - [minLon, minLat] + [minLon, minLat], ]) } @@ -121,7 +121,7 @@ export class BBox { public unionWith(other: BBox) { return new BBox([ [Math.max(this.maxLon, other.maxLon), Math.max(this.maxLat, other.maxLat)], - [Math.min(this.minLon, other.minLon), Math.min(this.minLat, other.minLat)] + [Math.min(this.minLon, other.minLon), Math.min(this.minLat, other.minLat)], ]) } @@ -174,7 +174,7 @@ export class BBox { return new BBox([ [lon - s / 2, lat - s / 2], - [lon + s / 2, lat + s / 2] + [lon + s / 2, lat + s / 2], ]) } @@ -231,29 +231,26 @@ export class BBox { const lonDiff = Math.min(maxIncrease / 2, Math.abs(this.maxLon - this.minLon) * factor) return new BBox([ [this.minLon - lonDiff, this.minLat - latDiff], - [this.maxLon + lonDiff, this.maxLat + latDiff] + [this.maxLon + lonDiff, this.maxLat + latDiff], ]) } padAbsolute(degrees: number): BBox { return new BBox([ [this.minLon - degrees, this.minLat - degrees], - [this.maxLon + degrees, this.maxLat + degrees] + [this.maxLon + degrees, this.maxLat + degrees], ]) } toLngLat(): [[number, number], [number, number]] { return [ [this.minLon, this.minLat], - [this.maxLon, this.maxLat] + [this.maxLon, this.maxLat], ] } toLngLatFlat(): [number, number, number, number] { - return [ - this.minLon, this.minLat, - this.maxLon, this.maxLat, - ] + return [this.minLon, this.minLat, this.maxLon, this.maxLat] } public asGeojsonCached() { @@ -267,7 +264,7 @@ export class BBox { return { type: "Feature", properties: properties, - geometry: this.asGeometry() + geometry: this.asGeometry(), } } @@ -280,9 +277,9 @@ export class BBox { [this.maxLon, this.minLat], [this.maxLon, this.maxLat], [this.minLon, this.maxLat], - [this.minLon, this.minLat] - ] - ] + [this.minLon, this.minLat], + ], + ], } } @@ -309,7 +306,7 @@ export class BBox { minLon, maxLon, minLat, - maxLat + maxLat, } } diff --git a/src/Logic/DetermineTheme.ts b/src/Logic/DetermineTheme.ts index 0fe9b5906..4d4c1bb29 100644 --- a/src/Logic/DetermineTheme.ts +++ b/src/Logic/DetermineTheme.ts @@ -109,7 +109,7 @@ export default class DetermineTheme { const dict = new Map() for (const tagRendering of questions.tagRenderings) { - dict.set(tagRendering.id, tagRendering) + dict.set(tagRendering.id, tagRendering) } return dict diff --git a/src/Logic/FeatureSource/Sources/FeatureSourceMerger.ts b/src/Logic/FeatureSource/Sources/FeatureSourceMerger.ts index d9aad6b63..296588984 100644 --- a/src/Logic/FeatureSource/Sources/FeatureSourceMerger.ts +++ b/src/Logic/FeatureSource/Sources/FeatureSourceMerger.ts @@ -9,7 +9,8 @@ import { OsmFeature } from "../../../Models/OsmFeature" * If multiple sources contain the same object (as determined by 'id'), only one copy of them is retained */ export default class FeatureSourceMerger - implements IndexedFeatureSource { + implements IndexedFeatureSource +{ public features: UIEventSource = new UIEventSource([]) public readonly featuresById: Store> protected readonly _featuresById: UIEventSource> @@ -118,10 +119,11 @@ export default class FeatureSourceMerger + Src extends UpdatableFeatureSource = UpdatableFeatureSource + > extends FeatureSourceMerger - implements IndexedFeatureSource, UpdatableFeatureSource { + implements IndexedFeatureSource, UpdatableFeatureSource +{ constructor(...sources: Src[]) { super(...sources) } diff --git a/src/Logic/FeatureSource/Sources/MvtSource.ts b/src/Logic/FeatureSource/Sources/MvtSource.ts index 6342fea90..619424df1 100644 --- a/src/Logic/FeatureSource/Sources/MvtSource.ts +++ b/src/Logic/FeatureSource/Sources/MvtSource.ts @@ -4,7 +4,6 @@ import { Store, UIEventSource } from "../../UIEventSource" import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource" import { MvtToGeojson } from "mvt-to-geojson" - export default class MvtSource implements FeatureSourceForTile, UpdatableFeatureSource { public readonly features: Store[]> public readonly x: number @@ -28,7 +27,7 @@ export default class MvtSource implements FeatureSourceForTile, UpdatableFeature y: number, z: number, layerName?: string, - isActive?: Store, + isActive?: Store ) { this._url = url this._layerName = layerName @@ -43,7 +42,7 @@ export default class MvtSource implements FeatureSourceForTile, UpdatableFeature } return fs }, - [isActive], + [isActive] ) } @@ -54,7 +53,6 @@ export default class MvtSource implements FeatureSourceForTile, UpdatableFeature await this.currentlyRunning } - private async download(): Promise { try { const result = await fetch(this._url) @@ -66,7 +64,7 @@ export default class MvtSource implements FeatureSourceForTile, UpdatableFeature const features = MvtToGeojson.fromBuffer(buffer, this.x, this.y, this.z) for (const feature of features) { const properties = feature.properties - if(!properties["osm_type"]){ + if (!properties["osm_type"]) { continue } let type: string = "node" @@ -90,6 +88,4 @@ export default class MvtSource implements FeatureSourceForTile, UpdatableFeature console.error("Could not download MVT " + this._url + " tile due to", e) } } - - } diff --git a/src/Logic/FeatureSource/TiledFeatureSource/SummaryTileSource.ts b/src/Logic/FeatureSource/TiledFeatureSource/SummaryTileSource.ts index 41c08ff74..4256f7abf 100644 --- a/src/Logic/FeatureSource/TiledFeatureSource/SummaryTileSource.ts +++ b/src/Logic/FeatureSource/TiledFeatureSource/SummaryTileSource.ts @@ -23,8 +23,7 @@ export class SummaryTileSourceRewriter implements FeatureSource { filteredLayers: ReadonlyMap ) { this.filteredLayers = Array.from(filteredLayers.values()).filter( - (l) => - Constants.priviliged_layers.indexOf(l.layerDef.id) < 0 + (l) => Constants.priviliged_layers.indexOf(l.layerDef.id) < 0 ) this._summarySource = summarySource filteredLayers.forEach((v) => { diff --git a/src/Logic/GeoOperations.ts b/src/Logic/GeoOperations.ts index 96062012a..806223740 100644 --- a/src/Logic/GeoOperations.ts +++ b/src/Logic/GeoOperations.ts @@ -95,8 +95,14 @@ export class GeoOperations { /** * Starting on `from`, travels `distance` meters in the direction of the `bearing` (default: 90) */ - static destination(from: Coord | [number,number],distance: number, bearing: number = 90): [number,number]{ - return <[number,number]> turf.destination(from, distance, bearing, {units: "meters"}).geometry.coordinates + static destination( + from: Coord | [number, number], + distance: number, + bearing: number = 90 + ): [number, number] { + return <[number, number]>( + turf.destination(from, distance, bearing, { units: "meters" }).geometry.coordinates + ) } static convexHull(featureCollection, options: { concavity?: number }) { @@ -928,13 +934,13 @@ export class GeoOperations { if (meters === undefined) { return "" } - meters = Utils.roundHuman( Math.round(meters)) + meters = Utils.roundHuman(Math.round(meters)) if (meters < 1000) { return Utils.roundHuman(meters) + "m" } if (meters >= 10000) { - const km = Utils.roundHuman(Math.round(meters / 1000)) + const km = Utils.roundHuman(Math.round(meters / 1000)) return km + "km" } diff --git a/src/Logic/ImageProviders/AllImageProviders.ts b/src/Logic/ImageProviders/AllImageProviders.ts index b33bf593f..3cc966414 100644 --- a/src/Logic/ImageProviders/AllImageProviders.ts +++ b/src/Logic/ImageProviders/AllImageProviders.ts @@ -34,10 +34,10 @@ export default class AllImageProviders { AllImageProviders.genericImageProvider, ] public static apiUrls: string[] = [].concat( - ...AllImageProviders.ImageAttributionSource.map((src) => src.apiUrls()), + ...AllImageProviders.ImageAttributionSource.map((src) => src.apiUrls()) ) public static defaultKeys = [].concat( - AllImageProviders.ImageAttributionSource.map((provider) => provider.defaultKeyPrefixes), + AllImageProviders.ImageAttributionSource.map((provider) => provider.defaultKeyPrefixes) ) private static providersByName = { imgur: Imgur.singleton, @@ -71,13 +71,12 @@ export default class AllImageProviders { */ public static LoadImagesFor( tags: Store>, - tagKey?: string[], + tagKey?: string[] ): Store { if (tags?.data?.id === undefined) { return undefined } - const source = new UIEventSource([]) const allSources: Store[] = [] for (const imageProvider of AllImageProviders.ImageAttributionSource) { @@ -86,11 +85,11 @@ export default class AllImageProviders { However, we override them if a custom image tag is set, e.g. 'image:menu' */ const prefixes = tagKey ?? imageProvider.defaultKeyPrefixes - const singleSource = tags.bindD(tags => imageProvider.getRelevantUrls(tags, prefixes)) + const singleSource = tags.bindD((tags) => imageProvider.getRelevantUrls(tags, prefixes)) allSources.push(singleSource) singleSource.addCallbackAndRunD((_) => { const all: ProvidedImage[] = [].concat(...allSources.map((source) => source.data)) - const dedup = Utils.DedupOnId(all, i => i?.id ?? i?.url) + const dedup = Utils.DedupOnId(all, (i) => i?.id ?? i?.url) source.set(dedup) }) } @@ -103,7 +102,7 @@ export default class AllImageProviders { */ public static loadImagesFrom(urls: string[]): Store { const tags = { - id:"na" + id: "na", } for (let i = 0; i < urls.length; i++) { const url = urls[i] diff --git a/src/Logic/ImageProviders/GenericImageProvider.ts b/src/Logic/ImageProviders/GenericImageProvider.ts index de369c478..2d2093acf 100644 --- a/src/Logic/ImageProviders/GenericImageProvider.ts +++ b/src/Logic/ImageProviders/GenericImageProvider.ts @@ -27,12 +27,14 @@ export default class GenericImageProvider extends ImageProvider { return undefined } - return [{ - key: key, - url: value, - provider: this, - id: value, - }] + return [ + { + key: key, + url: value, + provider: this, + id: value, + }, + ] } SourceIcon() { diff --git a/src/Logic/ImageProviders/ImageProvider.ts b/src/Logic/ImageProviders/ImageProvider.ts index 4588297c8..bf9fea781 100644 --- a/src/Logic/ImageProviders/ImageProvider.ts +++ b/src/Logic/ImageProviders/ImageProvider.ts @@ -9,15 +9,15 @@ export interface ProvidedImage { key: string provider: ImageProvider id: string - date?: Date, + date?: Date status?: string | "ready" /** * Compass angle of the taken image * 0 = north, 90° = East */ rotation?: number - lat?: number, - lon?: number, + lat?: number + lon?: number host?: string } @@ -26,8 +26,10 @@ export default abstract class ImageProvider { public abstract readonly name: string - public abstract SourceIcon(img?: {id: string, url: string, host?: string}, location?: { lon: number; lat: number }): BaseUIElement - + public abstract SourceIcon( + img?: { id: string; url: string; host?: string }, + location?: { lon: number; lat: number } + ): BaseUIElement /** * Gets all the relevant URLS for the given tags and for the given prefixes; @@ -35,12 +37,19 @@ export default abstract class ImageProvider { * @param tags * @param prefixes */ - public async getRelevantUrlsFor(tags: Record, prefixes: string[]): Promise { + public async getRelevantUrlsFor( + tags: Record, + prefixes: string[] + ): Promise { const relevantUrls: ProvidedImage[] = [] const seenValues = new Set() for (const key in tags) { - if (!prefixes.some((prefix) => key === prefix || key.match(new RegExp(prefix+":[0-9]+")))) { + if ( + !prefixes.some( + (prefix) => key === prefix || key.match(new RegExp(prefix + ":[0-9]+")) + ) + ) { continue } const values = Utils.NoEmpty(tags[key]?.split(";")?.map((v) => v.trim()) ?? []) @@ -50,10 +59,10 @@ export default abstract class ImageProvider { } seenValues.add(value) let images = this.ExtractUrls(key, value) - if(!Array.isArray(images)){ - images = await images + if (!Array.isArray(images)) { + images = await images } - if(images){ + if (images) { relevantUrls.push(...images) } } @@ -61,12 +70,17 @@ export default abstract class ImageProvider { return relevantUrls } - public getRelevantUrls(tags: Record, prefixes: string[]): Store { + public getRelevantUrls( + tags: Record, + prefixes: string[] + ): Store { return Stores.FromPromise(this.getRelevantUrlsFor(tags, prefixes)) } - - public abstract ExtractUrls(key: string, value: string): undefined | ProvidedImage[] | Promise + public abstract ExtractUrls( + key: string, + value: string + ): undefined | ProvidedImage[] | Promise public abstract DownloadAttribution(providedImage: { url: string diff --git a/src/Logic/ImageProviders/ImageUploadManager.ts b/src/Logic/ImageProviders/ImageUploadManager.ts index 0f0ac1a9d..d27613ee3 100644 --- a/src/Logic/ImageProviders/ImageUploadManager.ts +++ b/src/Logic/ImageProviders/ImageUploadManager.ts @@ -28,7 +28,10 @@ export class ImageUploadManager { private readonly _osmConnection: OsmConnection private readonly _changes: Changes public readonly isUploading: Store - private readonly _reportError: (message: (string | Error | XMLHttpRequest), extramessage?: string) => Promise + private readonly _reportError: ( + message: string | Error | XMLHttpRequest, + extramessage?: string + ) => Promise constructor( layout: ThemeConfig, @@ -38,7 +41,10 @@ export class ImageUploadManager { changes: Changes, gpsLocation: Store, allFeatures: IndexedFeatureSource, - reportError: (message: string | Error | XMLHttpRequest, extramessage?: string ) => Promise + reportError: ( + message: string | Error | XMLHttpRequest, + extramessage?: string + ) => Promise ) { this._uploader = uploader this._featureProperties = featureProperties @@ -56,7 +62,7 @@ export class ImageUploadManager { (startedCount) => { return startedCount > failed.data + done.data }, - [failed, done], + [failed, done] ) } @@ -105,7 +111,7 @@ export class ImageUploadManager { file: File, tagsStore: UIEventSource, targetKey: string, - noblur: boolean, + noblur: boolean ): Promise { const canBeUploaded = this.canBeUploaded(file) if (canBeUploaded !== true) { @@ -130,10 +136,16 @@ export class ImageUploadManager { } const properties = this._featureProperties.getStore(featureId) - const action = new LinkImageAction(featureId, uploadResult. key, uploadResult . value, properties, { - theme: tags?.data?.["_orig_theme"] ?? this._theme.id, - changeType: "add-image", - }) + const action = new LinkImageAction( + featureId, + uploadResult.key, + uploadResult.value, + properties, + { + theme: tags?.data?.["_orig_theme"] ?? this._theme.id, + changeType: "add-image", + } + ) await this._changes.applyAction(action) } @@ -153,34 +165,51 @@ export class ImageUploadManager { if (this._gps.data) { location = [this._gps.data.longitude, this._gps.data.latitude] } - if (location === undefined || location?.some(l => l === undefined)) { + if (location === undefined || location?.some((l) => l === undefined)) { const feature = this._indexedFeatures.featuresById.data.get(featureId) location = GeoOperations.centerpointCoordinates(feature) } try { - ({ key, value, absoluteUrl } = await this._uploader.uploadImage(blob, location, author, noblur)) + ;({ key, value, absoluteUrl } = await this._uploader.uploadImage( + blob, + location, + author, + noblur + )) } catch (e) { this.increaseCountFor(this._uploadRetried, featureId) console.error("Could not upload image, trying again:", e) try { - ({ key, value , absoluteUrl} = await this._uploader.uploadImage(blob, location, author, noblur)) + ;({ key, value, absoluteUrl } = await this._uploader.uploadImage( + blob, + location, + author, + noblur + )) this.increaseCountFor(this._uploadRetriedSuccess, featureId) } catch (e) { console.error("Could again not upload image due to", e) this.increaseCountFor(this._uploadFailed, featureId) - await this._reportError(e, JSON.stringify({ctx:"While uploading an image in the Image Upload Manager", featureId, author, targetKey})) + await this._reportError( + e, + JSON.stringify({ + ctx: "While uploading an image in the Image Upload Manager", + featureId, + author, + targetKey, + }) + ) return undefined } } console.log("Uploading image done, creating action for", featureId) key = targetKey ?? key - if(targetKey){ + if (targetKey) { // This is a non-standard key, so we use the image link directly value = absoluteUrl } this.increaseCountFor(this._uploadFinished, featureId) - return {key, absoluteUrl, value} - + return { key, absoluteUrl, value } } private getCounterFor(collection: Map>, key: string | "*") { diff --git a/src/Logic/ImageProviders/ImageUploader.ts b/src/Logic/ImageProviders/ImageUploader.ts index c178a6f3f..d8f8028ff 100644 --- a/src/Logic/ImageProviders/ImageUploader.ts +++ b/src/Logic/ImageProviders/ImageUploader.ts @@ -6,10 +6,14 @@ export interface ImageUploader { */ uploadImage( blob: File, - currentGps: [number,number], + currentGps: [number, number], author: string, noblur: boolean ): Promise } -export interface UploadResult{ key: string; value: string, absoluteUrl: string } +export interface UploadResult { + key: string + value: string + absoluteUrl: string +} diff --git a/src/Logic/ImageProviders/Imgur.ts b/src/Logic/ImageProviders/Imgur.ts index 80acda10a..c72ea54fd 100644 --- a/src/Logic/ImageProviders/Imgur.ts +++ b/src/Logic/ImageProviders/Imgur.ts @@ -19,7 +19,6 @@ export class Imgur extends ImageProvider { return [Imgur.apiUrl] } - SourceIcon(): BaseUIElement { return undefined } @@ -32,7 +31,7 @@ export class Imgur extends ImageProvider { key: key, provider: this, id: value, - } + }, ] } return undefined diff --git a/src/Logic/ImageProviders/Mapillary.ts b/src/Logic/ImageProviders/Mapillary.ts index 2cc370f32..a555aade9 100644 --- a/src/Logic/ImageProviders/Mapillary.ts +++ b/src/Logic/ImageProviders/Mapillary.ts @@ -118,7 +118,7 @@ export class Mapillary extends ImageProvider { } SourceIcon( - img: {id: string, url: string}, + img: { id: string; url: string }, location?: { lon: number lat: number @@ -182,7 +182,7 @@ export class Mapillary extends ImageProvider { key, rotation, lat: geometry.coordinates[1], - lon: geometry.coordinates[0] + lon: geometry.coordinates[0], } } } diff --git a/src/Logic/ImageProviders/Panoramax.ts b/src/Logic/ImageProviders/Panoramax.ts index 71d2a72a0..7d2a4bcf2 100644 --- a/src/Logic/ImageProviders/Panoramax.ts +++ b/src/Logic/ImageProviders/Panoramax.ts @@ -11,27 +11,35 @@ import SvelteUIElement from "../../UI/Base/SvelteUIElement" import Panoramax_bw from "../../assets/svg/Panoramax_bw.svelte" import Link from "../../UI/Base/Link" - export default class PanoramaxImageProvider extends ImageProvider { - public static readonly singleton = new PanoramaxImageProvider() private static readonly xyz = new PanoramaxXYZ() - private static defaultPanoramax = new AuthorizedPanoramax(Constants.panoramax.url, Constants.panoramax.token) + private static defaultPanoramax = new AuthorizedPanoramax( + Constants.panoramax.url, + Constants.panoramax.token + ) public defaultKeyPrefixes: string[] = ["panoramax"] public readonly name: string = "panoramax" - private static knownMeta: Record = {} + private static knownMeta: Record = {} - public SourceIcon(img?: { id: string, url: string, host?: string }, location?: { - lon: number; - lat: number; - }): BaseUIElement { + public SourceIcon( + img?: { id: string; url: string; host?: string }, + location?: { + lon: number + lat: number + } + ): BaseUIElement { const p = new Panoramax(img.host) - return new Link(new SvelteUIElement(Panoramax_bw), p.createViewLink({ - imageId: img?.id, - location, - }), true) + return new Link( + new SvelteUIElement(Panoramax_bw), + p.createViewLink({ + imageId: img?.id, + location, + }), + true + ) } public addKnownMeta(meta: ImageData) { @@ -43,25 +51,24 @@ export default class PanoramaxImageProvider extends ImageProvider { * @param id * @private */ - private async getInfoFromMapComplete(id: string): Promise<{ data: ImageData, url: string }> { + private async getInfoFromMapComplete(id: string): Promise<{ data: ImageData; url: string }> { const sequence = "6e702976-580b-419c-8fb3-cf7bd364e6f8" // We always reuse this sequence const url = `https://panoramax.mapcomplete.org/` const data = await PanoramaxImageProvider.defaultPanoramax.imageInfo(id, sequence) return { url, data } } - private async getInfoFromXYZ(imageId: string): Promise<{ data: ImageData, url: string }> { + private async getInfoFromXYZ(imageId: string): Promise<{ data: ImageData; url: string }> { const data = await PanoramaxImageProvider.xyz.imageInfo(imageId) return { data, url: "https://api.panoramax.xyz/" } } - /** * Reads a geovisio-somewhat-looking-like-geojson object and converts it to a provided image * @param meta * @private */ - private featureToImage(info: { data: ImageData, url: string }) { + private featureToImage(info: { data: ImageData; url: string }) { const meta = info?.data if (!meta) { return undefined @@ -82,8 +89,9 @@ export default class PanoramaxImageProvider extends ImageProvider { id: meta.id, url: makeAbsolute(meta.assets.sd.href), url_hd: makeAbsolute(meta.assets.hd.href), - host: meta["links"].find(l => l.rel === "root")?.href, - lon, lat, + host: meta["links"].find((l) => l.rel === "root")?.href, + lon, + lat, key: "panoramax", provider: this, status: meta.properties["geovisio:status"], @@ -92,14 +100,13 @@ export default class PanoramaxImageProvider extends ImageProvider { } } - private async getInfoFor(id: string): Promise<{ data: ImageData, url: string }> { + private async getInfoFor(id: string): Promise<{ data: ImageData; url: string }> { if (!id.match(/^[a-zA-Z0-9-]+$/)) { return undefined } const cached = PanoramaxImageProvider.knownMeta[id] if (cached) { if (new Date().getTime() - cached.time.getTime() < 1000) { - return { data: cached.data, url: undefined } } } @@ -120,10 +127,9 @@ export default class PanoramaxImageProvider extends ImageProvider { if (!Panoramax.isId(value)) { return undefined } - return [await this.getInfoFor(value).then(r => this.featureToImage(r))] + return [await this.getInfoFor(value).then((r) => this.featureToImage(r))] } - getRelevantUrls(tags: Record, prefixes: string[]): Store { const source = UIEventSource.FromPromise(super.getRelevantUrlsFor(tags, prefixes)) @@ -131,14 +137,15 @@ export default class PanoramaxImageProvider extends ImageProvider { if (data === undefined) { return true } - return data?.some(img => img?.status !== undefined && img?.status !== "ready" && img?.status !== "broken") + return data?.some( + (img) => + img?.status !== undefined && img?.status !== "ready" && img?.status !== "broken" + ) } - Stores.Chronic(1500, () => - hasLoading(source.data), - ).addCallback(_ => { + Stores.Chronic(1500, () => hasLoading(source.data)).addCallback((_) => { console.log("UPdating... ") - super.getRelevantUrlsFor(tags, prefixes).then(data => { + super.getRelevantUrlsFor(tags, prefixes).then((data) => { console.log("New panoramax data is", data, hasLoading(data)) source.set(data) return !hasLoading(data) @@ -148,7 +155,10 @@ export default class PanoramaxImageProvider extends ImageProvider { return source } - public async DownloadAttribution(providedImage: { url: string; id: string; }): Promise { + public async DownloadAttribution(providedImage: { + url: string + id: string + }): Promise { const meta = await this.getInfoFor(providedImage.id) return { @@ -171,9 +181,14 @@ export class PanoramaxUploader implements ImageUploader { this._panoramax = new AuthorizedPanoramax(url, token) } - async uploadImage(blob: File, currentGps: [number, number], author: string, noblur: boolean = false): Promise<{ - key: string; - value: string; + async uploadImage( + blob: File, + currentGps: [number, number], + author: string, + noblur: boolean = false + ): Promise<{ + key: string + value: string absoluteUrl: string }> { // https://panoramax.openstreetmap.fr/api/docs/swagger#/ @@ -183,7 +198,7 @@ export class PanoramaxUploader implements ImageUploader { let hasGPS = false try { const tags = await ExifReader.load(blob) - hasDate = tags?.DateTime !== undefined + hasDate = tags?.DateTime !== undefined hasGPS = tags?.GPSLatitude !== undefined && tags?.GPSLongitude !== undefined } catch (e) { console.error("Could not read EXIF-tags") @@ -203,7 +218,6 @@ export class PanoramaxUploader implements ImageUploader { exifOverride: { Artist: author, }, - }) PanoramaxImageProvider.singleton.addKnownMeta(img) return { @@ -212,5 +226,4 @@ export class PanoramaxUploader implements ImageUploader { absoluteUrl: img.assets.hd.href, } } - } diff --git a/src/Logic/ImageProviders/WikidataImageProvider.ts b/src/Logic/ImageProviders/WikidataImageProvider.ts index c64896559..3bee532a8 100644 --- a/src/Logic/ImageProviders/WikidataImageProvider.ts +++ b/src/Logic/ImageProviders/WikidataImageProvider.ts @@ -11,8 +11,10 @@ export class WikidataImageProvider extends ImageProvider { public static readonly singleton = new WikidataImageProvider() public readonly defaultKeyPrefixes = ["wikidata"] public readonly name = "Wikidata" - private static readonly keyBlacklist: ReadonlySet = new Set( - ["mapillary", ...Utils.Times(i => "mapillary:" + i, 10)]) + private static readonly keyBlacklist: ReadonlySet = new Set([ + "mapillary", + ...Utils.Times((i) => "mapillary:" + i, 10), + ]) private constructor() { super() @@ -26,7 +28,7 @@ export class WikidataImageProvider extends ImageProvider { return new SvelteUIElement(Wikidata_icon) } - public async ExtractUrls(key: string, value: string): Promise { + public async ExtractUrls(key: string, value: string): Promise { if (WikidataImageProvider.keyBlacklist.has(key)) { return undefined } diff --git a/src/Logic/ImageProviders/WikimediaImageProvider.ts b/src/Logic/ImageProviders/WikimediaImageProvider.ts index 631ea44f9..bf1e7a367 100644 --- a/src/Logic/ImageProviders/WikimediaImageProvider.ts +++ b/src/Logic/ImageProviders/WikimediaImageProvider.ts @@ -37,7 +37,7 @@ export class WikimediaImageProvider extends ImageProvider { return value } const baseUrl = `https://commons.wikimedia.org/wiki/Special:FilePath/${encodeURIComponent( - value, + value )}` if (useHd) { return baseUrl @@ -106,7 +106,8 @@ export class WikimediaImageProvider extends ImageProvider { value = WikimediaImageProvider.removeCommonsPrefix(value) if (value.startsWith("Category:")) { const urls = await Wikimedia.GetCategoryContents(value) - return urls.filter((url) => url.startsWith("File:")) + return urls + .filter((url) => url.startsWith("File:")) .map((image) => this.UrlForImage(image)) } if (value.startsWith("File:")) { @@ -117,7 +118,7 @@ export class WikimediaImageProvider extends ImageProvider { return undefined } // We do a last effort and assume this is a file - return [(this.UrlForImage("File:" + value))] + return [this.UrlForImage("File:" + value)] } public async DownloadAttribution(img: { url: string }): Promise { @@ -147,7 +148,7 @@ export class WikimediaImageProvider extends ImageProvider { console.warn( "The file", filename, - "has no usable metedata or license attached... Please fix the license info file yourself!", + "has no usable metedata or license attached... Please fix the license info file yourself!" ) return undefined } diff --git a/src/Logic/Osm/Actions/ChangeLocationAction.ts b/src/Logic/Osm/Actions/ChangeLocationAction.ts index 85b6a34b1..1f26469b1 100644 --- a/src/Logic/Osm/Actions/ChangeLocationAction.ts +++ b/src/Logic/Osm/Actions/ChangeLocationAction.ts @@ -33,7 +33,7 @@ export default class ChangeLocationAction extends OsmChangeAction { meta: { theme: string reason: string - }, + } ) { super(id, true) this.state = state @@ -66,12 +66,10 @@ export default class ChangeLocationAction extends OsmChangeAction { return [d] } - const insertIntoWay = new InsertPointIntoWayAction( - lat, lon, this._id, snapToWay, { - allowReuseOfPreviouslyCreatedPoints: false, - reusePointWithinMeters: 0.25, - }, - ).prepareChangeDescription() + const insertIntoWay = new InsertPointIntoWayAction(lat, lon, this._id, snapToWay, { + allowReuseOfPreviouslyCreatedPoints: false, + reusePointWithinMeters: 0.25, + }).prepareChangeDescription() return [d, { ...insertIntoWay, meta: d.meta }] } diff --git a/src/Logic/Osm/Actions/CreateNewNodeAction.ts b/src/Logic/Osm/Actions/CreateNewNodeAction.ts index a935575d2..eadabcd2f 100644 --- a/src/Logic/Osm/Actions/CreateNewNodeAction.ts +++ b/src/Logic/Osm/Actions/CreateNewNodeAction.ts @@ -38,7 +38,7 @@ export default class CreateNewNodeAction extends OsmCreateAction { theme: string changeType: "create" | "import" | null specialMotivation?: string - }, + } ) { super(null, basicTags !== undefined && basicTags.length > 0) this._basicTags = basicTags @@ -102,21 +102,12 @@ export default class CreateNewNodeAction extends OsmCreateAction { return [newPointChange] } - const change = new InsertPointIntoWayAction( - this._lat, - this._lon, - id, - this._snapOnto, - { - reusePointWithinMeters: this._reusePointDistance, - allowReuseOfPreviouslyCreatedPoints: this._reusePreviouslyCreatedPoint, - }, - ).prepareChangeDescription() + const change = new InsertPointIntoWayAction(this._lat, this._lon, id, this._snapOnto, { + reusePointWithinMeters: this._reusePointDistance, + allowReuseOfPreviouslyCreatedPoints: this._reusePreviouslyCreatedPoint, + }).prepareChangeDescription() - return [ - newPointChange, - { ...change, meta: this.meta }, - ] + return [newPointChange, { ...change, meta: this.meta }] } private setElementId(id: number) { diff --git a/src/Logic/Osm/Actions/InsertPointIntoWayAction.ts b/src/Logic/Osm/Actions/InsertPointIntoWayAction.ts index fb013e56e..f50e9f9a6 100644 --- a/src/Logic/Osm/Actions/InsertPointIntoWayAction.ts +++ b/src/Logic/Osm/Actions/InsertPointIntoWayAction.ts @@ -2,7 +2,7 @@ import { ChangeDescription } from "./ChangeDescription" import { GeoOperations } from "../../GeoOperations" import { OsmWay } from "../OsmObject" -export default class InsertPointIntoWayAction { +export default class InsertPointIntoWayAction { private readonly _lat: number private readonly _lon: number private readonly _idToInsert: number @@ -21,22 +21,19 @@ export default class InsertPointIntoWayAction { allowReuseOfPreviouslyCreatedPoints?: boolean reusePointWithinMeters?: number } - ){ + ) { this._lat = lat this._lon = lon this._idToInsert = idToInsert this._snapOnto = snapOnto this._options = options - } /** * Tries to create the changedescription of the way where the point is inserted * Returns `undefined` if inserting failed */ - public prepareChangeDescription(): Omit | undefined { - - + public prepareChangeDescription(): Omit | undefined { // Project the point onto the way console.log("Snapping a node onto an existing way...") const geojson = this._snapOnto.asGeoJson() @@ -59,13 +56,19 @@ export default class InsertPointIntoWayAction { } const prev = outerring[index] - if (GeoOperations.distanceBetween(prev, projectedCoor) < this._options.reusePointWithinMeters) { + if ( + GeoOperations.distanceBetween(prev, projectedCoor) < + this._options.reusePointWithinMeters + ) { // We reuse this point instead! reusedPointId = this._snapOnto.nodes[index] reusedPointCoordinates = this._snapOnto.coordinates[index] } const next = outerring[index + 1] - if (GeoOperations.distanceBetween(next, projectedCoor) < this._options.reusePointWithinMeters) { + if ( + GeoOperations.distanceBetween(next, projectedCoor) < + this._options.reusePointWithinMeters + ) { // We reuse this point instead! reusedPointId = this._snapOnto.nodes[index + 1] reusedPointCoordinates = this._snapOnto.coordinates[index + 1] @@ -82,15 +85,13 @@ export default class InsertPointIntoWayAction { locations.splice(index + 1, 0, [this._lon, this._lat]) ids.splice(index + 1, 0, this._idToInsert) - return { + return { type: "way", id: this._snapOnto.id, changes: { coordinates: locations, nodes: ids, - } + }, } - } - } diff --git a/src/Logic/Osm/Actions/ReplaceGeometryAction.ts b/src/Logic/Osm/Actions/ReplaceGeometryAction.ts index 9ea6f820b..07904c955 100644 --- a/src/Logic/Osm/Actions/ReplaceGeometryAction.ts +++ b/src/Logic/Osm/Actions/ReplaceGeometryAction.ts @@ -217,7 +217,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr const url = `${ this.state.osmConnection?._oauth_config?.url ?? "https://api.openstreetmap.org" }/api/0.6/${this.wayToReplaceId}/full` - const rawData = await Utils.downloadJsonCached<{elements: any[]}>(url, 1000) + const rawData = await Utils.downloadJsonCached<{ elements: any[] }>(url, 1000) parsed = OsmObject.ParseObjects(rawData.elements) } const allNodes = parsed.filter((o) => o.type === "node") diff --git a/src/Logic/Osm/Changes.ts b/src/Logic/Osm/Changes.ts index 113fc27a4..e2cf65a05 100644 --- a/src/Logic/Osm/Changes.ts +++ b/src/Logic/Osm/Changes.ts @@ -49,11 +49,11 @@ export class Changes { featureSwitches: { featureSwitchMorePrivacy?: Store featureSwitchIsTesting?: Store - }, - osmConnection: OsmConnection, - reportError?: (error: string) => void, - featureProperties?: FeaturePropertiesStore, - historicalUserLocations?: FeatureSource, + } + osmConnection: OsmConnection + reportError?: (error: string) => void + featureProperties?: FeaturePropertiesStore + historicalUserLocations?: FeatureSource allElements?: IndexedFeatureSource }, leftRightSensitive: boolean = false, @@ -64,8 +64,11 @@ export class Changes { this.allChanges.setData([...this.pendingChanges.data]) // If a pending change contains a negative ID, we save that this._nextId = Math.min(-1, ...(this.pendingChanges.data?.map((pch) => pch.id ?? 0) ?? [])) - if(isNaN(this._nextId) && state.reportError !== undefined){ - state.reportError("Got a NaN as nextID. Pending changes IDs are:" +this.pendingChanges.data?.map(pch => pch?.id).join(".")) + if (isNaN(this._nextId) && state.reportError !== undefined) { + state.reportError( + "Got a NaN as nextID. Pending changes IDs are:" + + this.pendingChanges.data?.map((pch) => pch?.id).join(".") + ) this._nextId = -100 } this.state = state @@ -84,12 +87,12 @@ export class Changes { // This doesn't matter however, as the '-1' is per piecewise upload, not global per changeset } - public static createTestObject(): Changes{ + public static createTestObject(): Changes { return new Changes({ osmConnection: new OsmConnection(), - featureSwitches:{ - featureSwitchIsTesting: new ImmutableStore(true) - } + featureSwitches: { + featureSwitchIsTesting: new ImmutableStore(true), + }, }) } @@ -837,12 +840,16 @@ export class Changes { ) // We keep all the refused changes to try them again - this.pendingChanges.setData(refusedChanges.flatMap((c) => c).filter(c => { - if(c.id === null || c.id === undefined){ - return false - } - return true - })) + this.pendingChanges.setData( + refusedChanges + .flatMap((c) => c) + .filter((c) => { + if (c.id === null || c.id === undefined) { + return false + } + return true + }) + ) } catch (e) { console.error( "Could not handle changes - probably an old, pending changeset in localstorage with an invalid format; erasing those", diff --git a/src/Logic/Osm/ChangesetHandler.ts b/src/Logic/Osm/ChangesetHandler.ts index 33d336765..01af622a5 100644 --- a/src/Logic/Osm/ChangesetHandler.ts +++ b/src/Logic/Osm/ChangesetHandler.ts @@ -205,12 +205,12 @@ export class ChangesetHandler { try { return await this.UploadWithNew(generateChangeXML, openChangeset, extraMetaTags) } catch (e) { - const req = (e) + const req = e if (req.status === 403) { // Someone got the banhammer // This is the message that OSM returned, will be something like "you have an important message, go to osm.org" - const msg = req.responseText - alert(msg+"\n\nWe'll take you to openstreetmap.org now") + const msg = req.responseText + alert(msg + "\n\nWe'll take you to openstreetmap.org now") window.location.replace(this.osmConnection.Backend()) return } diff --git a/src/Logic/Osm/OsmConnection.ts b/src/Logic/Osm/OsmConnection.ts index 8e8c4d904..80e07277b 100644 --- a/src/Logic/Osm/OsmConnection.ts +++ b/src/Logic/Osm/OsmConnection.ts @@ -45,14 +45,14 @@ export class OsmConnection { public userDetails: UIEventSource public isLoggedIn: Store public gpxServiceIsOnline: UIEventSource = new UIEventSource( - "unknown", + "unknown" ) public apiIsOnline: UIEventSource = new UIEventSource( - "unknown", + "unknown" ) public loadingStatus = new UIEventSource<"not-attempted" | "loading" | "error" | "logged-in">( - "not-attempted", + "not-attempted" ) public preferencesHandler: OsmPreferences public readonly _oauth_config: AuthConfig @@ -96,7 +96,7 @@ export class OsmConnection { this.userDetails = new UIEventSource( new UserDetails(this._oauth_config.url), - "userDetails", + "userDetails" ) if (options.fakeUser) { const ud = this.userDetails.data @@ -117,7 +117,7 @@ export class OsmConnection { (user) => user.loggedIn && (this.apiIsOnline.data === "unknown" || this.apiIsOnline.data === "online"), - [this.apiIsOnline], + [this.apiIsOnline] ) this.isLoggedIn.addCallback((isLoggedIn) => { if (this.userDetails.data.loggedIn == false && isLoggedIn == true) { @@ -160,17 +160,16 @@ export class OsmConnection { defaultValue: string = undefined, options?: { prefix?: string - }, + } ): UIEventSource { const prefix = options?.prefix ?? "mapcomplete-" return >this.preferencesHandler.getPreference(key, defaultValue, prefix) - } public getPreference( key: string, defaultValue: string = undefined, - prefix: string = "mapcomplete-", + prefix: string = "mapcomplete-" ): UIEventSource { return >this.preferencesHandler.getPreference(key, defaultValue, prefix) } @@ -214,7 +213,7 @@ export class OsmConnection { this.updateAuthObject() LocalStorageSource.get("location_before_login").setData( - Utils.runningFromConsole ? undefined : window.location.href, + Utils.runningFromConsole ? undefined : window.location.href ) this.auth.xhr( { @@ -252,13 +251,13 @@ export class OsmConnection { data.account_created = userInfo.getAttribute("account_created") data.uid = Number(userInfo.getAttribute("id")) data.languages = Array.from( - userInfo.getElementsByTagName("languages")[0].getElementsByTagName("lang"), + userInfo.getElementsByTagName("languages")[0].getElementsByTagName("lang") ).map((l) => l.textContent) data.csCount = Number.parseInt( - userInfo.getElementsByTagName("changesets")[0].getAttribute("count") ?? "0", + userInfo.getElementsByTagName("changesets")[0].getAttribute("count") ?? "0" ) data.tracesCount = Number.parseInt( - userInfo.getElementsByTagName("traces")[0].getAttribute("count") ?? "0", + userInfo.getElementsByTagName("traces")[0].getAttribute("count") ?? "0" ) data.img = undefined @@ -290,7 +289,7 @@ export class OsmConnection { action(this.userDetails.data) } this._onLoggedIn = [] - }, + } ) } @@ -308,7 +307,7 @@ export class OsmConnection { method: "GET" | "POST" | "PUT" | "DELETE", header?: Record, content?: string, - allowAnonymous: boolean = false, + allowAnonymous: boolean = false ): Promise { const connection: osmAuth = this.auth if (allowAnonymous && !this.auth.authenticated()) { @@ -316,7 +315,7 @@ export class OsmConnection { `${this.Backend()}/api/0.6/${path}`, header, method, - content, + content ) if (possibleResult["content"]) { return possibleResult["content"] @@ -333,13 +332,13 @@ export class OsmConnection { content, path: `/api/0.6/${path}`, }, - function(err, response) { + function (err, response) { if (err !== null) { error(err) } else { ok(response) } - }, + } ) }) } @@ -348,7 +347,7 @@ export class OsmConnection { path: string, content?: string, header?: Record, - allowAnonymous: boolean = false, + allowAnonymous: boolean = false ): Promise { return await this.interact(path, "POST", header, content, allowAnonymous) } @@ -356,7 +355,7 @@ export class OsmConnection { public async put( path: string, content?: string, - header?: Record, + header?: Record ): Promise { return await this.interact(path, "PUT", header, content) } @@ -364,7 +363,7 @@ export class OsmConnection { public async get( path: string, header?: Record, - allowAnonymous: boolean = false, + allowAnonymous: boolean = false ): Promise { return await this.interact(path, "GET", header, undefined, allowAnonymous) } @@ -403,7 +402,7 @@ export class OsmConnection { return new Promise<{ id: number }>((ok) => { window.setTimeout( () => ok({ id: Math.floor(Math.random() * 1000) }), - Math.random() * 5000, + Math.random() * 5000 ) }) } @@ -415,7 +414,7 @@ export class OsmConnection { { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", }, - true, + true ) const parsed = JSON.parse(response) console.log("Got result:", parsed) @@ -438,14 +437,14 @@ export class OsmConnection { * Note: these are called 'tags' on the wiki, but I opted to name them 'labels' instead as they aren't "key=value" tags, but just words. */ labels: string[] - }, + } ): Promise<{ id: number }> { if (this._dryRun.data) { console.warn("Dryrun enabled - not actually uploading GPX ", gpx) return new Promise<{ id: number }>((ok) => { window.setTimeout( () => ok({ id: Math.floor(Math.random() * 1000) }), - Math.random() * 5000, + Math.random() * 5000 ) }) } @@ -462,9 +461,9 @@ export class OsmConnection { } const extras = { file: - "; filename=\"" + + '; filename="' + (options.filename ?? "gpx_track_mapcomplete_" + new Date().toISOString()) + - "\"\r\nContent-Type: application/gpx+xml", + '"\r\nContent-Type: application/gpx+xml', } const boundary = "987654" @@ -472,7 +471,7 @@ export class OsmConnection { let body = "" for (const key in contents) { body += "--" + boundary + "\r\n" - body += "Content-Disposition: form-data; name=\"" + key + "\"" + body += 'Content-Disposition: form-data; name="' + key + '"' if (extras[key] !== undefined) { body += extras[key] } @@ -506,13 +505,13 @@ export class OsmConnection { path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}`, }, - function(err) { + function (err) { if (err !== null) { error(err) } else { ok() } - }, + } ) }) } @@ -521,7 +520,7 @@ export class OsmConnection { * To be called by land.html */ public finishLogin(callback: (previousURL: string) => void) { - this.auth.authenticate(function() { + this.auth.authenticate(function () { // Fully authed at this point console.log("Authentication successful!") const previousLocation = LocalStorageSource.get("location_before_login") @@ -538,8 +537,8 @@ export class OsmConnection { ? "https://mapcomplete.org/land.html" : window.location.protocol + "//" + window.location.host + "/land.html", /* We use 'singlePage' as much as possible, it is the most stable - including in PWA. - * However, this breaks in iframes so we open a popup in that case - */ + * However, this breaks in iframes so we open a popup in that case + */ singlepage: !this._iframeMode, auto: true, apiUrl: this._oauth_config.api_url ?? this._oauth_config.url, diff --git a/src/Logic/Osm/OsmPreferences.ts b/src/Logic/Osm/OsmPreferences.ts index 3686f743c..4b30c4212 100644 --- a/src/Logic/Osm/OsmPreferences.ts +++ b/src/Logic/Osm/OsmPreferences.ts @@ -5,7 +5,6 @@ import OSMAuthInstance = OSMAuth.osmAuth import { Utils } from "../../Utils" export class OsmPreferences { - /** * A 'cache' of all the preference stores * @private @@ -39,7 +38,6 @@ export class OsmPreferences { }) } - private setPreferencesAll(key: string, value: string) { if (this._allPreferences.data[key] !== value) { this._allPreferences.data[key] = value @@ -54,11 +52,11 @@ export class OsmPreferences { } return this.preferences[key] } - const pref = this.preferences[key] = new UIEventSource(value, "preference: " + key) + const pref = (this.preferences[key] = new UIEventSource(value, "preference: " + key)) if (value) { this.setPreferencesAll(key, value) } - pref.addCallback(v => { + pref.addCallback((v) => { console.log("Got an update:", key, "--->", v) this.uploadKvSplit(key, v) this.setPreferencesAll(key, v) @@ -71,7 +69,7 @@ export class OsmPreferences { this.seenKeys = Object.keys(prefs) const legacy = OsmPreferences.getLegacyCombinedItems(prefs) const merged = OsmPreferences.mergeDict(prefs) - if(Object.keys(legacy).length > 0){ + if (Object.keys(legacy).length > 0) { await this.removeLegacy(legacy) } for (const key in merged) { @@ -82,12 +80,7 @@ export class OsmPreferences { } } - - public getPreference( - key: string, - defaultValue: string = undefined, - prefix?: string, - ) { + public getPreference(key: string, defaultValue: string = undefined, prefix?: string) { return this.getPreferenceSeedFromlocal(key, defaultValue, { prefix }) } @@ -100,27 +93,29 @@ export class OsmPreferences { key: string, defaultValue: string = undefined, options?: { - prefix?: string, + prefix?: string saveToLocalStorage?: true | boolean - }, + } ): UIEventSource { if (options?.prefix) { key = options.prefix + key } key = key.replace(/[:/"' {}.%\\]/g, "") - const localStorage = LocalStorageSource.get(key) // cached if (localStorage.data === "null" || localStorage.data === "undefined") { localStorage.set(undefined) } - const pref: UIEventSource = this.initPreference(key, localStorage.data ?? defaultValue) // cached + const pref: UIEventSource = this.initPreference( + key, + localStorage.data ?? defaultValue + ) // cached if (this.localStorageInited.has(key)) { return pref } if (options?.saveToLocalStorage ?? true) { - pref.addCallback(v => localStorage.set(v)) // Keep a local copy + pref.addCallback((v) => localStorage.set(v)) // Keep a local copy } this.localStorageInited.add(key) return pref @@ -134,7 +129,7 @@ export class OsmPreferences { public async removeLegacy(legacyDict: Record) { for (const k in legacyDict) { const v = legacyDict[k] - console.log("Upgrading legacy preference",k ) + console.log("Upgrading legacy preference", k) await this.removeAllWithPrefix(k) this.osmConnection.getPreference(k).set(v) } @@ -148,20 +143,19 @@ export class OsmPreferences { const newDict = {} const allKeys: string[] = Object.keys(dict) - const normalKeys = allKeys.filter(k => !k.match(/[a-z-_0-9A-Z]*:[0-9]+/)) + const normalKeys = allKeys.filter((k) => !k.match(/[a-z-_0-9A-Z]*:[0-9]+/)) for (const normalKey of normalKeys) { if (normalKey.match(/-combined-[0-9]*$/) || normalKey.match(/-combined-length$/)) { // Ignore legacy keys continue } const partKeys = OsmPreferences.keysStartingWith(allKeys, normalKey) - const parts = partKeys.map(k => dict[k]) + const parts = partKeys.map((k) => dict[k]) newDict[normalKey] = parts.join("") } return newDict } - /** * Gets all items which have a 'combined'-string, the legacy long preferences * @@ -180,7 +174,9 @@ export class OsmPreferences { public static getLegacyCombinedItems(dict: Record): Record { const merged: Record = {} const keys = Object.keys(dict) - const toCheck = Utils.NoNullInplace(Utils.Dedup(keys.map(k => k.match(/(.*)-combined-[0-9]+$/)?.[1]))) + const toCheck = Utils.NoNullInplace( + Utils.Dedup(keys.map((k) => k.match(/(.*)-combined-[0-9]+$/)?.[1])) + ) for (const key of toCheck) { let i = 0 let str = "" @@ -195,7 +191,6 @@ export class OsmPreferences { return merged } - /** * Bulk-downloads all preferences * @private @@ -221,10 +216,9 @@ export class OsmPreferences { dict[k] = pref.getAttribute("v") } resolve(dict) - }, + } ) }) - } /** @@ -238,7 +232,7 @@ export class OsmPreferences { * */ private static keysStartingWith(allKeys: string[], key: string): string[] { - const keys = allKeys.filter(k => k === key || k.match(new RegExp(key + ":[0-9]+"))) + const keys = allKeys.filter((k) => k === key || k.match(new RegExp(key + ":[0-9]+"))) keys.sort() return keys } @@ -249,14 +243,12 @@ export class OsmPreferences { * */ private async uploadKvSplit(k: string, v: string) { - if (v === null || v === undefined || v === "" || v === "undefined" || v === "null") { const keysToDelete = OsmPreferences.keysStartingWith(this.seenKeys, k) - await Promise.all(keysToDelete.map(k => this.deleteKeyDirectly(k))) + await Promise.all(keysToDelete.map((k) => this.deleteKeyDirectly(k))) return } - await this.uploadKeyDirectly(k, v.slice(0, 255)) v = v.slice(255) let i = 0 @@ -265,7 +257,6 @@ export class OsmPreferences { v = v.slice(255) i++ } - } /** @@ -283,25 +274,23 @@ export class OsmPreferences { return } return new Promise((resolve, reject) => { - - this.auth.xhr( - { - method: "DELETE", - path: "/api/0.6/user/preferences/" + encodeURIComponent(k), - headers: { "Content-Type": "text/plain" }, - }, - (error) => { - if (error) { - console.warn("Could not remove preference", error) - reject(error) - return - } - console.debug("Preference ", k, "removed!") - resolve() - }, - ) - }, - ) + this.auth.xhr( + { + method: "DELETE", + path: "/api/0.6/user/preferences/" + encodeURIComponent(k), + headers: { "Content-Type": "text/plain" }, + }, + (error) => { + if (error) { + console.warn("Could not remove preference", error) + reject(error) + return + } + console.debug("Preference ", k, "removed!") + resolve() + } + ) + }) } /** @@ -328,7 +317,6 @@ export class OsmPreferences { } return new Promise((resolve, reject) => { - this.auth.xhr( { method: "PUT", @@ -343,7 +331,7 @@ export class OsmPreferences { return } resolve() - }, + } ) }) } @@ -351,12 +339,10 @@ export class OsmPreferences { async removeAllWithPrefix(prefix: string) { const keys = this.seenKeys for (const key of keys) { - if(!key.startsWith(prefix)){ + if (!key.startsWith(prefix)) { continue } await this.deleteKeyDirectly(key) } } - - } diff --git a/src/Logic/Search/CombinedSearcher.ts b/src/Logic/Search/CombinedSearcher.ts index 2ab9a1843..ad5dc65b9 100644 --- a/src/Logic/Search/CombinedSearcher.ts +++ b/src/Logic/Search/CombinedSearcher.ts @@ -1,4 +1,8 @@ -import GeocodingProvider, { SearchResult, GeocodingOptions, GeocodeResult } from "./GeocodingProvider" +import GeocodingProvider, { + SearchResult, + GeocodingOptions, + GeocodeResult, +} from "./GeocodingProvider" import { Utils } from "../../Utils" import { Store, Stores } from "../UIEventSource" @@ -8,7 +12,7 @@ export default class CombinedSearcher implements GeocodingProvider { constructor(...providers: ReadonlyArray) { this._providers = Utils.NoNull(providers) - this._providersWithSuggest = this._providers.filter(pr => pr.suggest !== undefined) + this._providersWithSuggest = this._providers.filter((pr) => pr.suggest !== undefined) } /** @@ -21,12 +25,10 @@ export default class CombinedSearcher implements GeocodingProvider { const results: GeocodeResult[] = [] const seenIds = new Set() for (const geocodedElement of geocoded) { - if(geocodedElement === undefined){ + if (geocodedElement === undefined) { continue } for (const entry of geocodedElement) { - - if (entry.osm_id === undefined) { throw "Invalid search result: a search result always must have an osm_id to be able to merge results from different sources" } @@ -42,14 +44,13 @@ export default class CombinedSearcher implements GeocodingProvider { } async search(query: string, options?: GeocodingOptions): Promise { - const results = (await Promise.all(this._providers.map(pr => pr.search(query, options)))) + const results = await Promise.all(this._providers.map((pr) => pr.search(query, options))) return CombinedSearcher.merge(results) } suggest(query: string, options?: GeocodingOptions): Store { return Stores.concat( - this._providersWithSuggest.map(pr => pr.suggest(query, options))) - .map(gcrss => CombinedSearcher.merge(gcrss)) - + this._providersWithSuggest.map((pr) => pr.suggest(query, options)) + ).map((gcrss) => CombinedSearcher.merge(gcrss)) } } diff --git a/src/Logic/Search/CoordinateSearch.ts b/src/Logic/Search/CoordinateSearch.ts index d531da759..9260ad0ad 100644 --- a/src/Logic/Search/CoordinateSearch.ts +++ b/src/Logic/Search/CoordinateSearch.ts @@ -19,7 +19,6 @@ export default class CoordinateSearch implements GeocodingProvider { /^(-?[0-9]+\.[0-9]+)[ ,;/\\]+(-?[0-9]+\.[0-9]+)/, /lon[:=]? *['"]?(-?[0-9]+\.[0-9]+)['"]?[ ,;&]+lat[:=]? *['"]?(-?[0-9]+\.[0-9]+)['"]?/, /lng[:=]? *['"]?(-?[0-9]+\.[0-9]+)['"]?[ ,;&]+lat[:=]? *['"]?(-?[0-9]+\.[0-9]+)['"]?/, - ] /** @@ -60,16 +59,18 @@ export default class CoordinateSearch implements GeocodingProvider { * results[0] // => {lat: -57.5802905, lon: -12.7202538, "display_name": "lon: -12.720254, lat: -57.58029", "category": "coordinate","osm_id": "-12.720254/-57.58029", "source": "coordinate:latlon"} */ private directSearch(query: string): GeocodeResult[] { - const matches = Utils.NoNull(CoordinateSearch.latLonRegexes.map(r => query.match(r))) - .map(m => CoordinateSearch.asResult(m[2], m[1], "latlon") ) + const matches = Utils.NoNull(CoordinateSearch.latLonRegexes.map((r) => query.match(r))).map( + (m) => CoordinateSearch.asResult(m[2], m[1], "latlon") + ) - const matchesLonLat = Utils.NoNull(CoordinateSearch.lonLatRegexes.map(r => query.match(r))) - .map(m => CoordinateSearch.asResult(m[1], m[2], "lonlat")) + const matchesLonLat = Utils.NoNull( + CoordinateSearch.lonLatRegexes.map((r) => query.match(r)) + ).map((m) => CoordinateSearch.asResult(m[1], m[2], "lonlat")) return matches.concat(matchesLonLat) } private static round6(n: number): string { - return "" + (Math.round(n * 1000000) / 1000000) + return "" + Math.round(n * 1000000) / 1000000 } private static asResult(lonIn: string, latIn: string, source: string): GeocodeResult { @@ -82,7 +83,7 @@ export default class CoordinateSearch implements GeocodingProvider { lon, display_name: "lon: " + lonStr + ", lat: " + latStr, category: "coordinate", - source: "coordinate:"+source, + source: "coordinate:" + source, osm_id: lonStr + "/" + latStr, } } @@ -94,5 +95,4 @@ export default class CoordinateSearch implements GeocodingProvider { async search(query: string): Promise { return this.directSearch(query) } - } diff --git a/src/Logic/Search/FilterSearch.ts b/src/Logic/Search/FilterSearch.ts index 35711f3cc..d82a14160 100644 --- a/src/Logic/Search/FilterSearch.ts +++ b/src/Logic/Search/FilterSearch.ts @@ -6,16 +6,20 @@ import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import LayerState from "../State/LayerState" import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig" -export type FilterSearchResult = { option: FilterConfigOption, filter: FilterConfig, layer: LayerConfig, index: number } - +export type FilterSearchResult = { + option: FilterConfigOption + filter: FilterConfig + layer: LayerConfig + index: number +} /** * Searches matching filters */ export default class FilterSearch { - private readonly _state: {layerState: LayerState, theme: ThemeConfig} + private readonly _state: { layerState: LayerState; theme: ThemeConfig } - constructor(state: {layerState: LayerState, theme: ThemeConfig}) { + constructor(state: { layerState: LayerState; theme: ThemeConfig }) { this._state = state } @@ -23,12 +27,15 @@ export default class FilterSearch { if (query.length === 0) { return [] } - const queries = query.split(" ").map(query => { - if (!Utils.isEmoji(query)) { - return Utils.simplifyStringForSearch(query) - } - return query - }).filter(q => q.length > 0) + const queries = query + .split(" ") + .map((query) => { + if (!Utils.isEmoji(query)) { + return Utils.simplifyStringForSearch(query) + } + return query + }) + .filter((q) => q.length > 0) const possibleFilters: FilterSearchResult[] = [] for (const layer of this._state.theme.layers) { if (!Array.isArray(layer.filters)) { @@ -46,29 +53,36 @@ export default class FilterSearch { if (!option.osmTags) { continue } - if(option.fields.length > 0){ + if (option.fields.length > 0) { // Filters with a search field are not supported as of now, see #2141 continue } - let terms = ([option.question.txt, - ...(option.searchTerms?.[Locale.language.data] ?? option.searchTerms?.["en"] ?? [])] - .flatMap(term => [term, ...(term?.split(" ") ?? [])])) - terms = terms.map(t => Utils.simplifyStringForSearch(t)) + let terms = [ + option.question.txt, + ...(option.searchTerms?.[Locale.language.data] ?? + option.searchTerms?.["en"] ?? + []), + ].flatMap((term) => [term, ...(term?.split(" ") ?? [])]) + terms = terms.map((t) => Utils.simplifyStringForSearch(t)) terms.push(option.emoji) Utils.NoNullInplace(terms) - const distances = queries.flatMap(query => terms.map(entry => { - const d = Utils.levenshteinDistance(query, entry.slice(0, query.length)) - const dRelative = d / query.length - return dRelative - })) + const distances = queries.flatMap((query) => + terms.map((entry) => { + const d = Utils.levenshteinDistance(query, entry.slice(0, query.length)) + const dRelative = d / query.length + return dRelative + }) + ) const levehnsteinD = Math.min(...distances) if (levehnsteinD > 0.25) { continue } possibleFilters.push({ - option, layer, filter, index: - i, + option, + layer, + filter, + index: i, }) } } @@ -85,7 +99,7 @@ export default class FilterSearch { if (!Array.isArray(filteredLayer.layerDef.filters)) { continue } - if (Constants.priviliged_layers.indexOf( id) >= 0) { + if (Constants.priviliged_layers.indexOf(id) >= 0) { continue } for (const filter of filteredLayer.layerDef.filters) { @@ -116,13 +130,16 @@ export default class FilterSearch { * Note that this depends on the language and the displayed text. For example, two filters {"en": "A", "nl": "B"} and {"en": "X", "nl": "B"} will be joined for dutch but not for English * */ - static mergeSemiIdenticalLayers(filters: ReadonlyArray, language: string):T[][] { - const results : Record = {} + static mergeSemiIdenticalLayers( + filters: ReadonlyArray, + language: string + ): T[][] { + const results: Record = {} for (const filter of filters) { const txt = filter.option.question.textFor(language) - if(results[txt]){ + if (results[txt]) { results[txt].push(filter) - }else{ + } else { results[txt] = [filter] } } diff --git a/src/Logic/Search/GeocodingFeatureSource.ts b/src/Logic/Search/GeocodingFeatureSource.ts index 65a449638..f4f3960f6 100644 --- a/src/Logic/Search/GeocodingFeatureSource.ts +++ b/src/Logic/Search/GeocodingFeatureSource.ts @@ -7,8 +7,8 @@ export default class GeocodingFeatureSource implements FeatureSource { public features: Store>[]> constructor(provider: Store) { - this.features = provider.map(geocoded => { - if(geocoded === undefined){ + this.features = provider.map((geocoded) => { + if (geocoded === undefined) { return [] } const features: Feature[] = [] @@ -28,18 +28,16 @@ export default class GeocodingFeatureSource implements FeatureSource { osm_id: gc.osm_type + "/" + gc.osm_id, osm_key: gc.feature?.properties?.osm_key, osm_value: gc.feature?.properties?.osm_value, - source: gc.source + source: gc.source, }, geometry: { type: "Point", - coordinates: [gc.lon, gc.lat] - } + coordinates: [gc.lon, gc.lat], + }, }) - } return features }) } - } diff --git a/src/Logic/Search/GeocodingProvider.ts b/src/Logic/Search/GeocodingProvider.ts index c0ad756ca..ea6187e0d 100644 --- a/src/Logic/Search/GeocodingProvider.ts +++ b/src/Logic/Search/GeocodingProvider.ts @@ -8,7 +8,7 @@ import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson" import { GeoOperations } from "../GeoOperations" export type GeocodingCategory = - "coordinate" + | "coordinate" | "city" | "house" | "street" @@ -19,7 +19,7 @@ export type GeocodingCategory = | "airport" | "shop" -export type GeocodeResult = { +export type GeocodeResult = { /** * The name of the feature being displayed */ @@ -27,8 +27,8 @@ export type GeocodeResult = { /** * Some optional, extra information */ - description?: string | Promise, - feature?: Feature, + description?: string | Promise + feature?: Feature lat: number lon: number /** @@ -37,22 +37,18 @@ export type GeocodeResult = { */ boundingbox?: number[] osm_type?: "node" | "way" | "relation" - osm_id: string, - category?: GeocodingCategory, - payload?: object, + osm_id: string + category?: GeocodingCategory + payload?: object source?: string } -export type SearchResult = - | GeocodeResult +export type SearchResult = GeocodeResult export interface GeocodingOptions { bbox?: BBox } - export default interface GeocodingProvider { - - search(query: string, options?: GeocodingOptions): Promise /** @@ -62,26 +58,28 @@ export default interface GeocodingProvider { suggest?(query: string, options?: GeocodingOptions): Store } -export type ReverseGeocodingResult = Feature +export type ReverseGeocodingResult = Feature< + Geometry, + { + osm_id: number + osm_type: "node" | "way" | "relation" + country: string + city: string + countrycode: string + type: GeocodingCategory + street: string + } +> export interface ReverseGeocodingProvider { reverseSearch( coordinate: { lon: number; lat: number }, zoom: number, - language?: string, - ): Promise; + language?: string + ): Promise } export class GeocodingUtils { - public static searchLayer = GeocodingUtils.initSearchLayer() private static initSearchLayer(): LayerConfig { @@ -103,16 +101,14 @@ export class GeocodingUtils { train_station: 14, airport: 13, shop: 16, - } - public static mergeSimilarResults(results: GeocodeResult[]){ + public static mergeSimilarResults(results: GeocodeResult[]) { const byName: Record = {} - for (const result of results) { const nm = result.display_name - if(!byName[nm]) { + if (!byName[nm]) { byName[nm] = [] } byName[nm].push(result) @@ -123,11 +119,13 @@ export class GeocodingUtils { const options = byName[nm] const added = options[0] merged.push(added) - const centers: [number,number][] = [[added.lon, added.lat]] + const centers: [number, number][] = [[added.lon, added.lat]] for (const other of options) { - const otherCenter:[number,number] = [other.lon, other.lat] - const nearbyFound= centers.some(center => GeoOperations.distanceBetween(center, otherCenter) < 500) - if(!nearbyFound){ + const otherCenter: [number, number] = [other.lon, other.lat] + const nearbyFound = centers.some( + (center) => GeoOperations.distanceBetween(center, otherCenter) < 500 + ) + if (!nearbyFound) { merged.push(other) centers.push(otherCenter) } @@ -136,7 +134,6 @@ export class GeocodingUtils { return merged } - public static categoryToIcon: Record = { city: "building_office_2", coordinate: "globe_alt", @@ -148,8 +145,5 @@ export class GeocodingUtils { county: "building_office_2", airport: "airport", shop: "building_storefront", - } - } - diff --git a/src/Logic/Search/LayerSearch.ts b/src/Logic/Search/LayerSearch.ts index 9397c0ee4..3d23767c4 100644 --- a/src/Logic/Search/LayerSearch.ts +++ b/src/Logic/Search/LayerSearch.ts @@ -5,22 +5,26 @@ import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig" import { Utils } from "../../Utils" export default class LayerSearch { - private readonly _theme: ThemeConfig private readonly _layerWhitelist: Set constructor(theme: ThemeConfig) { this._theme = theme - this._layerWhitelist = new Set(theme.layers - .filter(l => l.isNormal()) - .map(l => l.id)) + this._layerWhitelist = new Set(theme.layers.filter((l) => l.isNormal()).map((l) => l.id)) } - static scoreLayers(query: string, options: { - whitelist?: Set, blacklist?: Set - }): Record { + static scoreLayers( + query: string, + options: { + whitelist?: Set + blacklist?: Set + } + ): Record { const result: Record = {} - const queryParts = query.trim().split(" ").map(q => Utils.simplifyStringForSearch(q)) + const queryParts = query + .trim() + .split(" ") + .map((q) => Utils.simplifyStringForSearch(q)) for (const id in ThemeSearch.officialThemes.layers) { if (options?.whitelist && !options?.whitelist.has(id)) { continue @@ -29,18 +33,17 @@ export default class LayerSearch { continue } const keywords = ThemeSearch.officialThemes.layers[id] - result[id] = Math.min(...queryParts.map(q => SearchUtils.scoreKeywords(q, keywords))) + result[id] = Math.min(...queryParts.map((q) => SearchUtils.scoreKeywords(q, keywords))) } return result } - public search(query: string, limit: number, scoreThreshold: number = 2): LayerConfig[] { if (query.length < 1) { return [] } const scores = LayerSearch.scoreLayers(query, { whitelist: this._layerWhitelist }) - const asList: ({ layer: LayerConfig, score: number })[] = [] + const asList: { layer: LayerConfig; score: number }[] = [] for (const layer in scores) { asList.push({ layer: this._theme.getLayer(layer), @@ -50,10 +53,8 @@ export default class LayerSearch { asList.sort((a, b) => a.score - b.score) return asList - .filter(sorted => sorted.score < scoreThreshold) + .filter((sorted) => sorted.score < scoreThreshold) .slice(0, limit) - .map(l => l.layer) + .map((l) => l.layer) } - - } diff --git a/src/Logic/Search/LocalElementSearch.ts b/src/Logic/Search/LocalElementSearch.ts index 988402d99..46f28b434 100644 --- a/src/Logic/Search/LocalElementSearch.ts +++ b/src/Logic/Search/LocalElementSearch.ts @@ -7,14 +7,14 @@ import { ImmutableStore, Store, Stores } from "../UIEventSource" import OpenStreetMapIdSearch from "./OpenStreetMapIdSearch" type IntermediateResult = { - feature: Feature, + feature: Feature /** * Lon, lat */ - center: [number, number], - levehnsteinD: number, - physicalDistance: number, - searchTerms: string[], + center: [number, number] + levehnsteinD: number + physicalDistance: number + searchTerms: string[] description: string } export default class LocalElementSearch implements GeocodingProvider { @@ -24,37 +24,50 @@ export default class LocalElementSearch implements GeocodingProvider { constructor(state: ThemeViewState, limit: number) { this._state = state this._limit = limit - } async search(query: string, options?: GeocodingOptions): Promise { return this.searchEntries(query, options, false).data } - private getPartialResult(query: string, candidateId: string | undefined, matchStart: boolean, centerpoint: [number, number], features: Feature[]): IntermediateResult[] { - const results: IntermediateResult [] = [] + private getPartialResult( + query: string, + candidateId: string | undefined, + matchStart: boolean, + centerpoint: [number, number], + features: Feature[] + ): IntermediateResult[] { + const results: IntermediateResult[] = [] for (const feature of features) { const props = feature.properties - const searchTerms: string[] = Utils.NoNull([props.name, props.alt_name, props.local_name, - (props["addr:street"] && props["addr:number"]) ? - props["addr:street"] + props["addr:number"] : undefined]) + const searchTerms: string[] = Utils.NoNull([ + props.name, + props.alt_name, + props.local_name, + props["addr:street"] && props["addr:number"] + ? props["addr:street"] + props["addr:number"] + : undefined, + ]) let levehnsteinD: number if (candidateId === props.id) { levehnsteinD = 0 } else { - levehnsteinD = Math.min(...searchTerms.flatMap(entry => entry.split(/ /)).map(entry => { - let simplified = Utils.simplifyStringForSearch(entry) - if (matchStart) { - simplified = simplified.slice(0, query.length) - } - return Utils.levenshteinDistance(query, simplified) - })) + levehnsteinD = Math.min( + ...searchTerms + .flatMap((entry) => entry.split(/ /)) + .map((entry) => { + let simplified = Utils.simplifyStringForSearch(entry) + if (matchStart) { + simplified = simplified.slice(0, query.length) + } + return Utils.levenshteinDistance(query, simplified) + }) + ) } const center = GeoOperations.centerpointCoordinates(feature) - if ((levehnsteinD / query.length) <= 0.3) { - + if (levehnsteinD / query.length <= 0.3) { let description = "" if (feature.properties["addr:street"]) { description += "" + feature.properties["addr:street"] @@ -75,7 +88,11 @@ export default class LocalElementSearch implements GeocodingProvider { return results } - searchEntries(query: string, options?: GeocodingOptions, matchStart?: boolean): Store { + searchEntries( + query: string, + options?: GeocodingOptions, + matchStart?: boolean + ): Store { if (query.length < 3) { return new ImmutableStore([]) } @@ -88,17 +105,26 @@ export default class LocalElementSearch implements GeocodingProvider { const partials: Store[] = [] for (const [_, geoIndexedStore] of properties) { - const partialResult = geoIndexedStore.features.map(features => this.getPartialResult(query, candidateId, matchStart, centerPoint, features)) + const partialResult = geoIndexedStore.features.map((features) => + this.getPartialResult(query, candidateId, matchStart, centerPoint, features) + ) partials.push(partialResult) } - const listed: Store = Stores.concat(partials).map(l => l.flatMap(x => x)) - return listed.mapD(results => { - results.sort((a, b) => (a.physicalDistance + a.levehnsteinD * 25) - (b.physicalDistance + b.levehnsteinD * 25)) + const listed: Store = Stores.concat(partials).map((l) => + l.flatMap((x) => x) + ) + return listed.mapD((results) => { + results.sort( + (a, b) => + a.physicalDistance + + a.levehnsteinD * 25 - + (b.physicalDistance + b.levehnsteinD * 25) + ) if (this._limit) { results = results.slice(0, this._limit) } - return results.map(entry => { + return results.map((entry) => { const [osm_type, osm_id] = entry.feature.properties.id.split("/") return { lon: entry.center[0], @@ -113,12 +139,9 @@ export default class LocalElementSearch implements GeocodingProvider { } }) }) - - } suggest(query: string, options?: GeocodingOptions): Store { return this.searchEntries(query, options, true) } - } diff --git a/src/Logic/Search/NominatimGeocoding.ts b/src/Logic/Search/NominatimGeocoding.ts index 750be4fac..ece8c9071 100644 --- a/src/Logic/Search/NominatimGeocoding.ts +++ b/src/Logic/Search/NominatimGeocoding.ts @@ -6,26 +6,24 @@ import Locale from "../../UI/i18n/Locale" import GeocodingProvider, { GeocodingOptions, SearchResult } from "./GeocodingProvider" export class NominatimGeocoding implements GeocodingProvider { - - private readonly _host ; + private readonly _host private readonly limit: number - constructor(limit: number = 3, host: string = Constants.nominatimEndpoint) { + constructor(limit: number = 3, host: string = Constants.nominatimEndpoint) { this.limit = limit this._host = host } - public search(query: string, options?:GeocodingOptions): Promise { + public search(query: string, options?: GeocodingOptions): Promise { const b = options?.bbox ?? BBox.global - const url = `${ - this._host - }search?format=json&limit=${this.limit}&viewbox=${b.getEast()},${b.getNorth()},${b.getWest()},${b.getSouth()}&accept-language=${ + const url = `${this._host}search?format=json&limit=${ + this.limit + }&viewbox=${b.getEast()},${b.getNorth()},${b.getWest()},${b.getSouth()}&accept-language=${ Locale.language.data }&q=${query}` return Utils.downloadJson(url) } - async reverseSearch( coordinate: { lon: number; lat: number }, zoom: number = 17, diff --git a/src/Logic/Search/OpenStreetMapIdSearch.ts b/src/Logic/Search/OpenStreetMapIdSearch.ts index 48e9d9b43..c48fbfd72 100644 --- a/src/Logic/Search/OpenStreetMapIdSearch.ts +++ b/src/Logic/Search/OpenStreetMapIdSearch.ts @@ -5,12 +5,13 @@ import { SpecialVisualizationState } from "../../UI/SpecialVisualization" import { Utils } from "../../Utils" export default class OpenStreetMapIdSearch implements GeocodingProvider { - private static readonly regex = /((https?:\/\/)?(www.)?(osm|openstreetmap).org\/)?(n|node|w|way|r|relation)[/ ]?([0-9]+)/ + private static readonly regex = + /((https?:\/\/)?(www.)?(osm|openstreetmap).org\/)?(n|node|w|way|r|relation)[/ ]?([0-9]+)/ private static readonly types: Readonly> = { - "n": "node", - "w": "way", - "r": "relation", + n: "node", + w: "way", + r: "relation", } private readonly _state: SpecialVisualizationState @@ -55,15 +56,17 @@ export default class OpenStreetMapIdSearch implements GeocodingProvider { category: "coordinate", osm_type: <"node" | "way" | "relation">osm_type, osm_id, - lat: 0, lon: 0, + lat: 0, + lon: 0, source: "osmid", - } } const [lat, lon] = obj.centerpoint() return { - lat, lon, - display_name: obj.tags.name ?? obj.tags.alt_name ?? obj.tags.local_name ?? obj.tags.ref ?? id, + lat, + lon, + display_name: + obj.tags.name ?? obj.tags.alt_name ?? obj.tags.local_name ?? obj.tags.ref ?? id, description: osm_type, osm_type: <"node" | "way" | "relation">osm_type, osm_id, @@ -72,14 +75,15 @@ export default class OpenStreetMapIdSearch implements GeocodingProvider { } async search(query: string, options?: GeocodingOptions): Promise { - if (!isNaN(Number(query))) { const n = Number(query) - return Utils.NoNullInplace(await Promise.all([ - this.getInfoAbout(`node/${n}`).catch(x => undefined), - this.getInfoAbout(`way/${n}`).catch(x => undefined), - this.getInfoAbout(`relation/${n}`).catch(() => undefined), - ])) + return Utils.NoNullInplace( + await Promise.all([ + this.getInfoAbout(`node/${n}`).catch((x) => undefined), + this.getInfoAbout(`way/${n}`).catch((x) => undefined), + this.getInfoAbout(`relation/${n}`).catch(() => undefined), + ]) + ) } const id = OpenStreetMapIdSearch.extractId(query) @@ -92,5 +96,4 @@ export default class OpenStreetMapIdSearch implements GeocodingProvider { suggest?(query: string, options?: GeocodingOptions): Store { return UIEventSource.FromPromise(this.search(query, options)) } - } diff --git a/src/Logic/Search/PhotonSearch.ts b/src/Logic/Search/PhotonSearch.ts index 3bb2b66b9..8cb434d8d 100644 --- a/src/Logic/Search/PhotonSearch.ts +++ b/src/Logic/Search/PhotonSearch.ts @@ -2,7 +2,8 @@ import Constants from "../../Models/Constants" import GeocodingProvider, { GeocodeResult, GeocodingCategory, - GeocodingOptions, GeocodingUtils, + GeocodingOptions, + GeocodingUtils, ReverseGeocodingProvider, ReverseGeocodingResult, } from "./GeocodingProvider" @@ -16,31 +17,35 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding private _endpoint: string private supportedLanguages = ["en", "de", "fr"] private static readonly types = { - "R": "relation", - "W": "way", - "N": "node", + R: "relation", + W: "way", + N: "node", } private readonly suggestionLimit: number = 5 private readonly searchLimit: number = 1 - - constructor(suggestionLimit:number = 5, searchLimit:number = 1, endpoint?: string) { + constructor(suggestionLimit: number = 5, searchLimit: number = 1, endpoint?: string) { this.suggestionLimit = suggestionLimit this.searchLimit = searchLimit this._endpoint = endpoint ?? Constants.photonEndpoint ?? "https://photon.komoot.io/" } - async reverseSearch(coordinate: { - lon: number; - lat: number - }, zoom: number, language?: string): Promise { - const url = `${this._endpoint}/reverse?lon=${coordinate.lon}&lat=${coordinate.lat}&${this.getLanguage(language)}` + async reverseSearch( + coordinate: { + lon: number + lat: number + }, + zoom: number, + language?: string + ): Promise { + const url = `${this._endpoint}/reverse?lon=${coordinate.lon}&lat=${ + coordinate.lat + }&${this.getLanguage(language)}` const result = await Utils.downloadJsonCached(url, 1000 * 60 * 60) for (const f of result.features) { f.properties.osm_type = PhotonSearch.types[f.properties.osm_type] } return result.features - } /** @@ -49,13 +54,11 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding * @private */ private getLanguage(language?: string): string { - language ??= Locale.language.data if (this.supportedLanguages.indexOf(language) < 0) { return "" } return `&lang=${language}` - } suggest(query: string, options?: GeocodingOptions): Store { @@ -75,7 +78,6 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding switch (type) { case "house": { - const addr = ifdef("", p.street) + ifdef(" ", p.housenumber) if (!addr) { return p.city @@ -94,7 +96,6 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding case "country": return undefined } - } private getCategory(entry: Feature) { @@ -111,7 +112,11 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding return p.type } - async search(query: string, options?: GeocodingOptions, limit?: number): Promise { + async search( + query: string, + options?: GeocodingOptions, + limit?: number + ): Promise { if (query.length < 3) { return [] } @@ -121,9 +126,11 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding const [lon, lat] = options.bbox.center() bbox = `&lon=${lon}&lat=${lat}` } - const url = `${this._endpoint}/api/?q=${encodeURIComponent(query)}&limit=${limit}${this.getLanguage()}${bbox}` + const url = `${this._endpoint}/api/?q=${encodeURIComponent( + query + )}&limit=${limit}${this.getLanguage()}${bbox}` const results = await Utils.downloadJsonCached(url, 1000 * 60 * 60) - const encoded= results.features.map(f => { + const encoded = results.features.map((f) => { const [lon, lat] = GeoOperations.centerpointCoordinates(f) let boundingbox: number[] = undefined if (f.properties.extent) { @@ -138,11 +145,11 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding osm_type: PhotonSearch.types[f.properties.osm_type], category: this.getCategory(f), boundingbox, - lon, lat, + lon, + lat, source: this._endpoint, } }) return GeocodingUtils.mergeSimilarResults(encoded) } - } diff --git a/src/Logic/Search/SearchUtils.ts b/src/Logic/Search/SearchUtils.ts index 7b0f5a42e..033fcfb83 100644 --- a/src/Logic/Search/SearchUtils.ts +++ b/src/Logic/Search/SearchUtils.ts @@ -3,13 +3,11 @@ import { Utils } from "../../Utils" import ThemeSearch from "./ThemeSearch" export default class SearchUtils { - - /** Applies special search terms, such as 'studio', 'osmcha', ... * Returns 'false' if nothing is matched. * Doesn't return control flow if a match is found (navigates to another page in this case) */ - public static applySpecialSearch(searchTerm: string, ) { + public static applySpecialSearch(searchTerm: string) { searchTerm = searchTerm.toLowerCase() if (!searchTerm) { return false @@ -33,10 +31,8 @@ export default class SearchUtils { window.location.href = "./studio.html" } return false - } - /** * Searches for the smallest distance in words; will split both the query and the terms * @@ -44,19 +40,26 @@ export default class SearchUtils { * SearchUtils.scoreKeywords("waste", {"en": ["A layer with drinking water points"]}, "en") // => 2 * */ - public static scoreKeywords(query: string, keywords: Record | string[], language?: string): number { - if(!keywords){ + public static scoreKeywords( + query: string, + keywords: Record | string[], + language?: string + ): number { + if (!keywords) { return Infinity } language ??= Locale.language.data - const queryParts = query.trim().split(" ").map(q => Utils.simplifyStringForSearch(q)) + const queryParts = query + .trim() + .split(" ") + .map((q) => Utils.simplifyStringForSearch(q)) let terms: string[] if (Array.isArray(keywords)) { terms = keywords } else { terms = (keywords[language] ?? []).concat(keywords["*"]) } - const termsAll = Utils.NoNullInplace(terms).flatMap(t => t.split(" ")) + const termsAll = Utils.NoNullInplace(terms).flatMap((t) => t.split(" ")) let distanceSummed = 0 for (let i = 0; i < queryParts.length; i++) { diff --git a/src/Logic/Search/ThemeSearch.ts b/src/Logic/Search/ThemeSearch.ts index 3c4ef6be4..e44f02790 100644 --- a/src/Logic/Search/ThemeSearch.ts +++ b/src/Logic/Search/ThemeSearch.ts @@ -8,57 +8,59 @@ import LayerSearch from "./LayerSearch" import SearchUtils from "./SearchUtils" import { OsmConnection } from "../Osm/OsmConnection" - type ThemeSearchScore = { - theme: MinimalThemeInformation, - lowest: number, - perLayer?: Record, - other: number, + theme: MinimalThemeInformation + lowest: number + perLayer?: Record + other: number } - export default class ThemeSearch { - public static readonly officialThemes: { - themes: MinimalThemeInformation[], + themes: MinimalThemeInformation[] layers: Record> - } = themeOverview - public static readonly officialThemesById: Map = new Map() + } = themeOverview + public static readonly officialThemesById: Map = new Map< + string, + MinimalThemeInformation + >() static { for (const th of ThemeSearch.officialThemes.themes ?? []) { ThemeSearch.officialThemesById.set(th.id, th) } } - private readonly _knownHiddenThemes: Store> private readonly _layersToIgnore: string[] private readonly _otherThemes: MinimalThemeInformation[] - constructor(state: {osmConnection: OsmConnection, theme: ThemeConfig}) { - this._layersToIgnore = state.theme.layers.filter(l => l.isNormal()).map(l => l.id) - this._knownHiddenThemes = UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection).map(list => new Set(list)) - this._otherThemes = ThemeSearch.officialThemes.themes - .filter(th => th.id !== state.theme.id) + constructor(state: { osmConnection: OsmConnection; theme: ThemeConfig }) { + this._layersToIgnore = state.theme.layers.filter((l) => l.isNormal()).map((l) => l.id) + this._knownHiddenThemes = UserRelatedState.initDiscoveredHiddenThemes( + state.osmConnection + ).map((list) => new Set(list)) + this._otherThemes = ThemeSearch.officialThemes.themes.filter( + (th) => th.id !== state.theme.id + ) } - public search(query: string, limit: number, threshold: number = 3): MinimalThemeInformation[] { if (query.length < 1) { return [] } - const sorted = ThemeSearch.sortedByLowestScores(query, this._otherThemes, this._layersToIgnore) + const sorted = ThemeSearch.sortedByLowestScores( + query, + this._otherThemes, + this._layersToIgnore + ) return sorted - .filter(sorted => sorted.lowest < threshold) - .map(th => th.theme) - .filter(th => !th.hideFromOverview || this._knownHiddenThemes.data.has(th.id)) + .filter((sorted) => sorted.lowest < threshold) + .map((th) => th.theme) + .filter((th) => !th.hideFromOverview || this._knownHiddenThemes.data.has(th.id)) .slice(0, limit) } - public static createUrlFor( - layout: { id: string }, - state?: { layoutToUse?: { id } }, - ): string { + public static createUrlFor(layout: { id: string }, state?: { layoutToUse?: { id } }): string { if (layout === undefined) { return undefined } @@ -88,7 +90,6 @@ export default class ThemeSearch { linkPrefix = `${path}/theme.html?userlayout=${layout.id}&` } - return `${linkPrefix}` } @@ -101,17 +102,21 @@ export default class ThemeSearch { * @param ignoreLayers * @private */ - private static scoreThemes(query: string, themes: MinimalThemeInformation[], ignoreLayers: string[] = undefined): Record { + private static scoreThemes( + query: string, + themes: MinimalThemeInformation[], + ignoreLayers: string[] = undefined + ): Record { if (query?.length < 1) { return undefined } themes = Utils.NoNullInplace(themes) - let options : {blacklist: Set} = undefined - if(ignoreLayers?.length > 0){ + let options: { blacklist: Set } = undefined + if (ignoreLayers?.length > 0) { options = { blacklist: new Set(ignoreLayers) } } - const layerScores = query.length < 3 ? {} : LayerSearch.scoreLayers(query, options) + const layerScores = query.length < 3 ? {} : LayerSearch.scoreLayers(query, options) const results: Record = {} for (const layoutInfo of themes) { const theme = layoutInfo.id @@ -122,20 +127,22 @@ export default class ThemeSearch { results[theme] = { theme: layoutInfo, lowest: -1, - other: 0 + other: 0, } continue } - const perLayer = Utils.asRecord( - layoutInfo.layers ?? [], layer => layerScores[layer], - ) + const perLayer = Utils.asRecord(layoutInfo.layers ?? [], (layer) => layerScores[layer]) const language = Locale.language.data - const keywords = Utils.NoNullInplace([layoutInfo.shortDescription, layoutInfo.title]) - .map(item => typeof item === "string" ? item : (item[language] ?? item["*"])) + const keywords = Utils.NoNullInplace([ + layoutInfo.shortDescription, + layoutInfo.title, + ]).map((item) => (typeof item === "string" ? item : item[language] ?? item["*"])) - - const other = Math.min(SearchUtils.scoreKeywords(query, keywords), SearchUtils.scoreKeywords(query, layoutInfo.keywords)) + const other = Math.min( + SearchUtils.scoreKeywords(query, keywords), + SearchUtils.scoreKeywords(query, layoutInfo.keywords) + ) const lowest = Math.min(other, ...Object.values(perLayer)) results[theme] = { theme: layoutInfo, @@ -147,15 +154,21 @@ export default class ThemeSearch { return results } - public static sortedByLowestScores(search: string, themes: MinimalThemeInformation[], ignoreLayers: string[] = []): ThemeSearchScore[] { + public static sortedByLowestScores( + search: string, + themes: MinimalThemeInformation[], + ignoreLayers: string[] = [] + ): ThemeSearchScore[] { const scored = Object.values(this.scoreThemes(search, themes, ignoreLayers)) scored.sort((a, b) => a.lowest - b.lowest) return scored } - public static sortedByLowest(search: string, themes: MinimalThemeInformation[], ignoreLayers: string[] = []): MinimalThemeInformation[] { - return this.sortedByLowestScores(search, themes, ignoreLayers) - .map(th => th.theme) + public static sortedByLowest( + search: string, + themes: MinimalThemeInformation[], + ignoreLayers: string[] = [] + ): MinimalThemeInformation[] { + return this.sortedByLowestScores(search, themes, ignoreLayers).map((th) => th.theme) } - } diff --git a/src/Logic/State/FeatureSwitchState.ts b/src/Logic/State/FeatureSwitchState.ts index e7d39702e..657afb72b 100644 --- a/src/Logic/State/FeatureSwitchState.ts +++ b/src/Logic/State/FeatureSwitchState.ts @@ -45,7 +45,6 @@ export class OsmConnectionFeatureSwitches { } export default class FeatureSwitchState extends OsmConnectionFeatureSwitches { - public readonly featureSwitchEnableLogin: UIEventSource public readonly featureSwitchSearch: UIEventSource public readonly featureSwitchBackgroundSelection: UIEventSource diff --git a/src/Logic/State/LayerState.ts b/src/Logic/State/LayerState.ts index 9df3a9b1f..d611f9132 100644 --- a/src/Logic/State/LayerState.ts +++ b/src/Logic/State/LayerState.ts @@ -11,8 +11,8 @@ import FilterConfig from "../../Models/ThemeConfig/FilterConfig" import Constants from "../../Models/Constants" export type ActiveFilter = { - layer: LayerConfig, - filter: FilterConfig, + layer: LayerConfig + filter: FilterConfig control: UIEventSource } /** @@ -36,9 +36,13 @@ export default class LayerState { private readonly _activeFilters: UIEventSource = new UIEventSource([]) public readonly activeFilters: Store = this._activeFilters - private readonly _activeLayers: UIEventSource = new UIEventSource(undefined) + private readonly _activeLayers: UIEventSource = new UIEventSource< + FilteredLayer[] + >(undefined) public readonly activeLayers: Store = this._activeLayers - private readonly _nonactiveLayers: UIEventSource = new UIEventSource(undefined) + private readonly _nonactiveLayers: UIEventSource = new UIEventSource< + FilteredLayer[] + >(undefined) public readonly nonactiveLayers: Store = this._nonactiveLayers private readonly osmConnection: OsmConnection @@ -71,7 +75,7 @@ export default class LayerState { this.filteredLayers = filteredLayers layers.forEach((l) => LayerState.linkFilterStates(l, filteredLayers)) - this.filteredLayers.forEach(fl => { + this.filteredLayers.forEach((fl) => { fl.isDisplayed.addCallback(() => this.updateActiveFilters()) for (const [_, appliedFilter] of fl.appliedFilters) { appliedFilter.addCallback(() => this.updateActiveFilters()) @@ -80,27 +84,27 @@ export default class LayerState { this.updateActiveFilters() } - private updateActiveFilters(){ + private updateActiveFilters() { const filters: ActiveFilter[] = [] const activeLayers: FilteredLayer[] = [] - const nonactiveLayers: FilteredLayer[] = [] - this.filteredLayers.forEach(fl => { - if(!fl.isDisplayed.data){ + const nonactiveLayers: FilteredLayer[] = [] + this.filteredLayers.forEach((fl) => { + if (!fl.isDisplayed.data) { nonactiveLayers.push(fl) return } activeLayers.push(fl) - if(fl.layerDef.filterIsSameAs){ + if (fl.layerDef.filterIsSameAs) { return } for (const [filtername, appliedFilter] of fl.appliedFilters) { if (appliedFilter.data === undefined) { continue } - const filter = fl.layerDef.filters.find(f => f.id === filtername) - if(typeof appliedFilter.data === "number"){ - if(filter.options[appliedFilter.data].osmTags === undefined){ + const filter = fl.layerDef.filters.find((f) => f.id === filtername) + if (typeof appliedFilter.data === "number") { + if (filter.options[appliedFilter.data].osmTags === undefined) { // This is probably the first, generic option which doesn't _actually_ filter continue } diff --git a/src/Logic/State/SearchState.ts b/src/Logic/State/SearchState.ts index 5e2310cf3..6f9af0431 100644 --- a/src/Logic/State/SearchState.ts +++ b/src/Logic/State/SearchState.ts @@ -17,7 +17,6 @@ import { FeatureSource } from "../FeatureSource/FeatureSource" import { Feature } from "geojson" export default class SearchState { - public readonly feedback: UIEventSource = new UIEventSource(undefined) public readonly searchTerm: UIEventSource = new UIEventSource("") public readonly searchIsFocused = new UIEventSource(false) @@ -39,57 +38,66 @@ export default class SearchState { new LocalElementSearch(state, 5), new CoordinateSearch(), new OpenStreetMapIdSearch(state), - new PhotonSearch() // new NominatimGeocoding(), + new PhotonSearch(), // new NominatimGeocoding(), ] const bounds = state.mapProperties.bounds - const suggestionsList = this.searchTerm.stabilized(250).mapD(search => { + const suggestionsList = this.searchTerm.stabilized(250).mapD( + (search) => { if (search.length === 0) { return undefined } - return this.locationSearchers.map(ls => ls.suggest(search, { bbox: bounds.data })) - - }, [bounds] + return this.locationSearchers.map((ls) => ls.suggest(search, { bbox: bounds.data })) + }, + [bounds] ) - this.suggestionsSearchRunning = suggestionsList.bind(suggestions => { + this.suggestionsSearchRunning = suggestionsList.bind((suggestions) => { if (suggestions === undefined) { return new ImmutableStore(true) } - return Stores.concat(suggestions).map(suggestions => suggestions.some(list => list === undefined)) + return Stores.concat(suggestions).map((suggestions) => + suggestions.some((list) => list === undefined) + ) }) - this.suggestions = suggestionsList.bindD(suggestions => - Stores.concat(suggestions).map(suggestions => CombinedSearcher.merge(suggestions)) + this.suggestions = suggestionsList.bindD((suggestions) => + Stores.concat(suggestions).map((suggestions) => CombinedSearcher.merge(suggestions)) ) const themeSearch = new ThemeSearch(state) - this.themeSuggestions = this.searchTerm.mapD(query => themeSearch.search(query, 3)) + this.themeSuggestions = this.searchTerm.mapD((query) => themeSearch.search(query, 3)) const layerSearch = new LayerSearch(state.theme) - this.layerSuggestions = this.searchTerm.mapD(query => layerSearch.search(query, 5)) + this.layerSuggestions = this.searchTerm.mapD((query) => layerSearch.search(query, 5)) const filterSearch = new FilterSearch(state) - this.filterSuggestions = this.searchTerm.stabilized(50) - .mapD(query => filterSearch.search(query)) - .mapD(filterResult => { - const active = state.layerState.activeFilters.data - return filterResult.filter(({ filter, index, layer }) => { - const foundMatch = active.some(active => - active.filter.id === filter.id && layer.id === active.layer.id && active.control.data === index) + this.filterSuggestions = this.searchTerm + .stabilized(50) + .mapD((query) => filterSearch.search(query)) + .mapD( + (filterResult) => { + const active = state.layerState.activeFilters.data + return filterResult.filter(({ filter, index, layer }) => { + const foundMatch = active.some( + (active) => + active.filter.id === filter.id && + layer.id === active.layer.id && + active.control.data === index + ) - return !foundMatch - }) - }, [state.layerState.activeFilters]) + return !foundMatch + }) + }, + [state.layerState.activeFilters] + ) this.locationResults = new GeocodingFeatureSource(this.suggestions.stabilized(250)) this.showSearchDrawer = new UIEventSource(false) - this.searchIsFocused.addCallbackAndRunD(sugg => { + this.searchIsFocused.addCallbackAndRunD((sugg) => { if (sugg) { this.showSearchDrawer.set(true) } }) - - } public async apply(result: FilterSearchResult[] | LayerConfig) { @@ -108,7 +116,7 @@ export default class SearchState { private async applyFilter(payload: FilterSearchResult[]) { const state = this.state - const layersToShow = payload.map(fsr => fsr.layer.id) + const layersToShow = payload.map((fsr) => fsr.layer.id) console.log("Layers to show are", layersToShow) for (const [name, otherLayer] of state.layerState.filteredLayers) { const layer = otherLayer.layerDef @@ -144,7 +152,7 @@ export default class SearchState { } // This feature might not be loaded because we zoomed out const object = await this.state.osmObjectDownloader.DownloadObjectAsync(osmid) - if(object === "deleted"){ + if (object === "deleted") { return } const f = object.asGeoJson() diff --git a/src/Logic/State/UserRelatedState.ts b/src/Logic/State/UserRelatedState.ts index 0d3d4eae0..6700f2664 100644 --- a/src/Logic/State/UserRelatedState.ts +++ b/src/Logic/State/UserRelatedState.ts @@ -22,9 +22,7 @@ import Showdown from "showdown" import { LocalStorageSource } from "../Web/LocalStorageSource" import { GeocodeResult } from "../Search/GeocodingProvider" - export class OptionallySyncedHistory { - public readonly syncPreference: UIEventSource<"sync" | "local" | "no"> public readonly value: Store private readonly synced: UIEventSource @@ -34,18 +32,26 @@ export class OptionallySyncedHistory { private readonly _isSame: (a: T, b: T) => boolean private osmconnection: OsmConnection - constructor(key: string, osmconnection: OsmConnection, maxHistory: number = 20, isSame?: (a: T, b: T) => boolean) { + constructor( + key: string, + osmconnection: OsmConnection, + maxHistory: number = 20, + isSame?: (a: T, b: T) => boolean + ) { this.osmconnection = osmconnection this._maxHistory = maxHistory this._isSame = isSame - this.syncPreference = osmconnection.getPreference( - "preference-" + key + "-history", - "sync", - ) - const synced = this.synced = UIEventSource.asObject(osmconnection.getPreference(key + "-history"), []) - const local = this.local = LocalStorageSource.getParsed(key + "-history", []) - const thisSession = this.thisSession = new UIEventSource([], "optionally-synced:" + key + "(session only)") - this.syncPreference.addCallback(syncmode => { + this.syncPreference = osmconnection.getPreference("preference-" + key + "-history", "sync") + const synced = (this.synced = UIEventSource.asObject( + osmconnection.getPreference(key + "-history"), + [] + )) + const local = (this.local = LocalStorageSource.getParsed(key + "-history", [])) + const thisSession = (this.thisSession = new UIEventSource( + [], + "optionally-synced:" + key + "(session only)" + )) + this.syncPreference.addCallback((syncmode) => { if (syncmode === "sync") { let list = [...thisSession.data, ...synced.data].slice(0, maxHistory) if (this._isSame) { @@ -67,9 +73,7 @@ export class OptionallySyncedHistory { } }) - this.value = this.syncPreference.bind(syncPref => this.getAppropriateStore(syncPref)) - - + this.value = this.syncPreference.bind((syncPref) => this.getAppropriateStore(syncPref)) } private getAppropriateStore(syncPref?: string) { @@ -87,7 +91,7 @@ export class OptionallySyncedHistory { const store = this.getAppropriateStore() let oldList = store.data ?? [] if (this._isSame) { - oldList = oldList.filter(x => !this._isSame(t, x)) + oldList = oldList.filter((x) => !this._isSame(t, x)) } store.set([t, ...oldList].slice(0, this._maxHistory)) } @@ -100,14 +104,13 @@ export class OptionallySyncedHistory { if (t === undefined) { return } - this.osmconnection.isLoggedIn.addCallbackAndRun(loggedIn => { + this.osmconnection.isLoggedIn.addCallbackAndRun((loggedIn) => { if (!loggedIn) { return } this.add(t) return true }) - } clear() { @@ -157,7 +160,7 @@ export default class UserRelatedState { */ public readonly gpsLocationHistoryRetentionTime = new UIEventSource( 7 * 24 * 60 * 60, - "gps_location_retention", + "gps_location_retention" ) public readonly addNewFeatureMode = new UIEventSource< @@ -180,18 +183,17 @@ export default class UserRelatedState { public readonly recentlyVisitedThemes: OptionallySyncedHistory public readonly recentlyVisitedSearch: OptionallySyncedHistory - constructor( osmConnection: OsmConnection, layout?: ThemeConfig, featureSwitches?: FeatureSwitchState, - mapProperties?: MapProperties, + mapProperties?: MapProperties ) { this.osmConnection = osmConnection this._mapProperties = mapProperties this.showAllQuestionsAtOnce = UIEventSource.asBoolean( - this.osmConnection.getPreference("show-all-questions", "false"), + this.osmConnection.getPreference("show-all-questions", "false") ) this.language = this.osmConnection.getPreference("language") this.showTags = this.osmConnection.getPreference("show_tags") @@ -203,15 +205,19 @@ export default class UserRelatedState { this.mangroveIdentity = new MangroveIdentity( this.osmConnection.getPreference("identity", undefined, "mangrove"), - this.osmConnection.getPreference("identity-creation-date", undefined, "mangrove"), + this.osmConnection.getPreference("identity-creation-date", undefined, "mangrove") + ) + this.preferredBackgroundLayer = this.osmConnection.getPreference( + "preferred-background-layer" ) - this.preferredBackgroundLayer = this.osmConnection.getPreference("preferred-background-layer") this.addNewFeatureMode = this.osmConnection.getPreference( "preferences-add-new-mode", - "button_click_right", + "button_click_right" + ) + this.showScale = UIEventSource.asBoolean( + this.osmConnection.GetPreference("preference-show-scale", "false") ) - this.showScale = UIEventSource.asBoolean(this.osmConnection.GetPreference("preference-show-scale", "false")) this.imageLicense = this.osmConnection.getPreference("pictures-license", "CC0") this.installedUserThemes = UserRelatedState.initInstalledUserThemes(osmConnection) @@ -224,12 +230,13 @@ export default class UserRelatedState { "theme", this.osmConnection, 10, - (a, b) => a === b, + (a, b) => a === b ) - this.recentlyVisitedSearch = new OptionallySyncedHistory("places", + this.recentlyVisitedSearch = new OptionallySyncedHistory( + "places", this.osmConnection, 15, - (a, b) => a.osm_id === b.osm_id && a.osm_type === b.osm_type, + (a, b) => a.osm_id === b.osm_id && a.osm_type === b.osm_type ) this.syncLanguage() this.recentlyVisitedThemes.addDefferred(layout?.id) @@ -279,9 +286,7 @@ export default class UserRelatedState { */ public addUnofficialTheme(themeInfo: MinimalThemeInformation) { const pref = this.osmConnection.getPreference("unofficial-theme-" + themeInfo.id) - this.osmConnection.isLoggedIn.when( - () => pref.set(JSON.stringify(themeInfo)) - ) + this.osmConnection.isLoggedIn.when(() => pref.set(JSON.stringify(themeInfo))) } public getUnofficialTheme(id: string): MinimalThemeInformation | undefined { @@ -298,9 +303,9 @@ export default class UserRelatedState { } catch (e) { console.warn( "Removing theme " + - id + - " as it could not be parsed from the preferences; the content is:", - str, + id + + " as it could not be parsed from the preferences; the content is:", + str ) pref.setData(null) return undefined @@ -330,7 +335,7 @@ export default class UserRelatedState { title: layout.title.translations, shortDescription: layout.shortDescription.translations, definition: layout["definition"], - }), + }) ) } } @@ -340,7 +345,7 @@ export default class UserRelatedState { return osmConnection.preferencesHandler.allPreferences.map((prefs) => Object.keys(prefs) .filter((k) => k.startsWith(prefix)) - .map((k) => k.substring(prefix.length)), + .map((k) => k.substring(prefix.length)) ) } @@ -354,7 +359,7 @@ export default class UserRelatedState { return userPreferences.map((preferences) => Object.keys(preferences) .filter((key) => key.startsWith(prefix)) - .map((key) => key.substring(prefix.length, key.length - "-enabled".length)), + .map((key) => key.substring(prefix.length, key.length - "-enabled".length)) ) } @@ -370,7 +375,7 @@ export default class UserRelatedState { return undefined } return [home.lon, home.lat] - }), + }) ).map((homeLonLat) => { if (homeLonLat === undefined) { return empty @@ -400,7 +405,7 @@ export default class UserRelatedState { * */ private initAmendedPrefs( layout?: ThemeConfig, - featureSwitches?: FeatureSwitchState, + featureSwitches?: FeatureSwitchState ): UIEventSource> { const amendedPrefs = new UIEventSource>({ _theme: layout?.id, @@ -446,19 +451,19 @@ export default class UserRelatedState { const missingLayers = Utils.Dedup( untranslated .filter((k) => k.startsWith("layers:")) - .map((k) => k.slice("layers:".length).split(".")[0]), + .map((k) => k.slice("layers:".length).split(".")[0]) ) const zenLinks: { link: string; id: string }[] = Utils.NoNull([ hasMissingTheme ? { - id: "theme:" + layout.id, - link: LinkToWeblate.hrefToWeblateZen( - language, - "themes", - layout.id, - ), - } + id: "theme:" + layout.id, + link: LinkToWeblate.hrefToWeblateZen( + language, + "themes", + layout.id + ), + } : undefined, ...missingLayers.map((id) => ({ id: "layer:" + id, @@ -475,7 +480,7 @@ export default class UserRelatedState { } amendedPrefs.ping() }, - [this.translationMode], + [this.translationMode] ) this.mangroveIdentity.getKeyId().addCallbackAndRun((kid) => { @@ -494,7 +499,7 @@ export default class UserRelatedState { .makeHtml(userDetails.description) ?.replace(/>/g, ">") ?.replace(/</g, "<") - ?.replace(/\n/g, ""), + ?.replace(/\n/g, "") ) } @@ -505,7 +510,7 @@ export default class UserRelatedState { (c: { contributor: string; commits: number }) => { const replaced = c.contributor.toLowerCase().replace(/\s+/g, "") return replaced === simplifiedName - }, + } ) if (isTranslator) { amendedPrefs.data["_translation_contributions"] = "" + isTranslator.commits @@ -514,7 +519,7 @@ export default class UserRelatedState { (c: { contributor: string; commits: number }) => { const replaced = c.contributor.toLowerCase().replace(/\s+/g, "") return replaced === simplifiedName - }, + } ) if (isCodeContributor) { amendedPrefs.data["_code_contributions"] = "" + isCodeContributor.commits diff --git a/src/Logic/State/UserSettingsMetaTagging.ts b/src/Logic/State/UserSettingsMetaTagging.ts index 33a5ae85b..6e568c5c3 100644 --- a/src/Logic/State/UserSettingsMetaTagging.ts +++ b/src/Logic/State/UserSettingsMetaTagging.ts @@ -1,14 +1,42 @@ import { Utils } from "../../Utils" /** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */ export class ThemeMetaTagging { - public static readonly themeName = "usersettings" + public static readonly themeName = "usersettings" - public metaTaggging_for_usersettings(feat: {properties: Record}) { - Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) ) - Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' ) - Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) ) - Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) ) - Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a ) - feat.properties['__current_backgroun'] = 'initial_value' - } -} \ No newline at end of file + public metaTaggging_for_usersettings(feat: { properties: Record }) { + Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () => + feat.properties._description + .match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/) + ?.at(1) + ) + Utils.AddLazyProperty( + feat.properties, + "_d", + () => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? "" + ) + Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () => + ((feat) => { + const e = document.createElement("div") + e.innerHTML = feat.properties._d + return Array.from(e.getElementsByTagName("a")).filter( + (a) => a.href.match(/mastodon|en.osm.town/) !== null + )[0]?.href + })(feat) + ) + Utils.AddLazyProperty(feat.properties, "_mastodon_link", () => + ((feat) => { + const e = document.createElement("div") + e.innerHTML = feat.properties._d + return Array.from(e.getElementsByTagName("a")).filter( + (a) => a.getAttribute("rel")?.indexOf("me") >= 0 + )[0]?.href + })(feat) + ) + Utils.AddLazyProperty( + feat.properties, + "_mastodon_candidate", + () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a + ) + feat.properties["__current_backgroun"] = "initial_value" + } +} diff --git a/src/Logic/Tags/Tag.ts b/src/Logic/Tags/Tag.ts index 40bb10c1c..b4de29291 100644 --- a/src/Logic/Tags/Tag.ts +++ b/src/Logic/Tags/Tag.ts @@ -19,7 +19,7 @@ export class Tag extends TagsFilter { if (value === undefined) { throw `Invalid value while constructing a Tag with key '${key}': value is undefined` } - if(value.length > 255 || key.length > 255){ + if (value.length > 255 || key.length > 255) { throw "Invalid tag: length is over 255" } if (value === "*") { diff --git a/src/Logic/UIEventSource.ts b/src/Logic/UIEventSource.ts index 022cbe869..bf421b509 100644 --- a/src/Logic/UIEventSource.ts +++ b/src/Logic/UIEventSource.ts @@ -23,7 +23,7 @@ export class Stores { } public static FromPromiseWithErr( - promise: Promise, + promise: Promise ): Store<{ success: T } | { error: any }> { return UIEventSource.FromPromiseWithErr(promise) } @@ -99,7 +99,7 @@ export class Stores { */ static holdDefined(store: Store): Store { const newStore = new UIEventSource(store.data) - store.addCallbackD(t => { + store.addCallbackD((t) => { newStore.setData(t) }) return newStore @@ -133,23 +133,27 @@ export abstract class Store implements Readable { abstract map( f: (t: T) => J, extraStoresToWatch: Store[], - callbackDestroyFunction: (f: () => void) => void, + callbackDestroyFunction: (f: () => void) => void ): Store public mapD( f: (t: Exclude) => J, extraStoresToWatch?: Store[], - callbackDestroyFunction?: (f: () => void) => void, + callbackDestroyFunction?: (f: () => void) => void ): Store { - return this.map((t) => { - if (t === undefined) { - return undefined - } - if (t === null) { - return null - } - return f(>t) - }, extraStoresToWatch, callbackDestroyFunction) + return this.map( + (t) => { + if (t === undefined) { + return undefined + } + if (t === null) { + return null + } + return f(>t) + }, + extraStoresToWatch, + callbackDestroyFunction + ) } /** @@ -176,7 +180,7 @@ export abstract class Store implements Readable { abstract addCallbackAndRun(callback: (data: T) => void): () => void public withEqualityStabilized( - comparator: (t: T | undefined, t1: T | undefined) => boolean, + comparator: (t: T | undefined, t1: T | undefined) => boolean ): Store { let oldValue = undefined return this.map((v) => { @@ -269,7 +273,10 @@ export abstract class Store implements Readable { return sink } - public bindD(f: (t: Exclude) => Store, extraSources: UIEventSource[] = []): Store { + public bindD( + f: (t: Exclude) => Store, + extraSources: UIEventSource[] = [] + ): Store { return this.bind((t) => { if (t === null) { return null @@ -343,10 +350,10 @@ export abstract class Store implements Readable { public abstract destroy() - when(callback: () => void, condition?: (v:T) => boolean) { - condition ??= v => v === true - this.addCallbackAndRunD(v => { - if ( condition(v)) { + when(callback: () => void, condition?: (v: T) => boolean) { + condition ??= (v) => v === true + this.addCallbackAndRunD((v) => { + if (condition(v)) { callback() return true } @@ -364,8 +371,7 @@ export class ImmutableStore extends Store { this.data = data } - private static readonly pass: () => void = () => { - } + private static readonly pass: () => void = () => {} addCallback(_: (data: T) => void): () => void { // pass: data will never change @@ -394,7 +400,7 @@ export class ImmutableStore extends Store { map( f: (t: T) => J, extraStores: Store[] = undefined, - ondestroyCallback?: (f: () => void) => void, + ondestroyCallback?: (f: () => void) => void ): ImmutableStore { if (extraStores?.length > 0) { return new MappedStore(this, f, extraStores, undefined, f(this.data), ondestroyCallback) @@ -464,7 +470,7 @@ class ListenerTracker { let endTime = new Date().getTime() / 1000 if (endTime - startTime > 500) { console.trace( - "Warning: a ping took more then 500ms; this is probably a performance issue", + "Warning: a ping took more then 500ms; this is probably a performance issue" ) } if (toDelete !== undefined) { @@ -506,7 +512,7 @@ class MappedStore extends Store { extraStores: Store[], upstreamListenerHandler: ListenerTracker | undefined, initialState: T, - onDestroy?: (f: () => void) => void, + onDestroy?: (f: () => void) => void ) { super() this._upstream = upstream @@ -546,7 +552,7 @@ class MappedStore extends Store { map( f: (t: T) => J, extraStores: Store[] = undefined, - ondestroyCallback?: (f: () => void) => void, + ondestroyCallback?: (f: () => void) => void ): Store { let stores: Store[] = undefined if (extraStores?.length > 0 || this._extraStores?.length > 0) { @@ -568,7 +574,7 @@ class MappedStore extends Store { stores, this._callbacks, f(this.data), - ondestroyCallback, + ondestroyCallback ) } @@ -624,7 +630,7 @@ class MappedStore extends Store { this._unregisterFromUpstream = this._upstream.addCallback((_) => self.update()) this._unregisterFromExtraStores = this._extraStores?.map((store) => - store?.addCallback((_) => self.update()), + store?.addCallback((_) => self.update()) ) this._callbacksAreRegistered = true } @@ -645,8 +651,7 @@ class MappedStore extends Store { } export class UIEventSource extends Store implements Writable { - private static readonly pass: (() => void) = () => { - } + private static readonly pass: () => void = () => {} public data: T _callbacks: ListenerTracker = new ListenerTracker() @@ -661,7 +666,7 @@ export class UIEventSource extends Store implements Writable { public static flatten( source: Store>, - possibleSources?: Store[], + possibleSources?: Store[] ): UIEventSource { const sink = new UIEventSource(source.data?.data) @@ -690,7 +695,7 @@ export class UIEventSource extends Store implements Writable { */ public static FromPromise( promise: Promise, - onError: (e) => void = undefined, + onError: (e) => void = undefined ): UIEventSource { const src = new UIEventSource(undefined) promise?.then((d) => src.setData(d)) @@ -711,7 +716,7 @@ export class UIEventSource extends Store implements Writable { * @constructor */ public static FromPromiseWithErr( - promise: Promise, + promise: Promise ): UIEventSource<{ success: T } | { error: any } | undefined> { const src = new UIEventSource<{ success: T } | { error: any }>(undefined) promise @@ -743,7 +748,7 @@ export class UIEventSource extends Store implements Writable { return undefined } return "" + fl - }, + } ) } @@ -774,7 +779,7 @@ export class UIEventSource extends Store implements Writable { return undefined } return "" + fl - }, + } ) } @@ -782,11 +787,14 @@ export class UIEventSource extends Store implements Writable { return stringUIEventSource.sync( (str) => str === "true", [], - (b) => "" + b, + (b) => "" + b ) } - static asObject(stringUIEventSource: UIEventSource, defaultV: T): UIEventSource { + static asObject( + stringUIEventSource: UIEventSource, + defaultV: T + ): UIEventSource { return stringUIEventSource.sync( (str) => { if (str === undefined || str === null || str === "") { @@ -800,7 +808,7 @@ export class UIEventSource extends Store implements Writable { } }, [], - (b) => JSON.stringify(b) ?? "", + (b) => JSON.stringify(b) ?? "" ) } @@ -890,7 +898,7 @@ export class UIEventSource extends Store implements Writable { public map( f: (t: T) => J, extraSources: Store[] = [], - onDestroy?: (f: () => void) => void, + onDestroy?: (f: () => void) => void ): Store { return new MappedStore(this, f, extraSources, this._callbacks, f(this.data), onDestroy) } @@ -902,7 +910,7 @@ export class UIEventSource extends Store implements Writable { public mapD( f: (t: Exclude) => J, extraSources: Store[] = [], - callbackDestroyFunction?: (f: () => void) => void, + callbackDestroyFunction?: (f: () => void) => void ): Store { return new MappedStore( this, @@ -920,7 +928,7 @@ export class UIEventSource extends Store implements Writable { this.data === undefined || this.data === null ? this.data : f(this.data), - callbackDestroyFunction, + callbackDestroyFunction ) } @@ -940,7 +948,7 @@ export class UIEventSource extends Store implements Writable { f: (t: T) => J, extraSources: Store[], g: (j: J, t: T) => T, - allowUnregister = false, + allowUnregister = false ): UIEventSource { const self = this @@ -949,7 +957,7 @@ export class UIEventSource extends Store implements Writable { const newSource = new UIEventSource(f(this.data), "map(" + this.tag + ")@" + callee) - const update = function() { + const update = function () { newSource.setData(f(self.data)) return allowUnregister && newSource._callbacks.length() === 0 } diff --git a/src/Logic/Web/LocalStorageSource.ts b/src/Logic/Web/LocalStorageSource.ts index 310830a00..872dcb371 100644 --- a/src/Logic/Web/LocalStorageSource.ts +++ b/src/Logic/Web/LocalStorageSource.ts @@ -5,7 +5,6 @@ import { Utils } from "../../Utils" * UIEventsource-wrapper around localStorage */ export class LocalStorageSource { - private static readonly _cache: Record> = {} static getParsed(key: string, defaultValue: T): UIEventSource { @@ -21,7 +20,7 @@ export class LocalStorageSource { } }, [], - (value) => JSON.stringify(value), + (value) => JSON.stringify(value) ) } @@ -32,7 +31,6 @@ export class LocalStorageSource { } let saved = defaultValue if (!Utils.runningFromConsole) { - try { saved = localStorage.getItem(key) if (saved === "undefined") { diff --git a/src/Logic/Web/MangroveReviews.ts b/src/Logic/Web/MangroveReviews.ts index 6765d8204..b52f48c49 100644 --- a/src/Logic/Web/MangroveReviews.ts +++ b/src/Logic/Web/MangroveReviews.ts @@ -22,7 +22,7 @@ export class MangroveIdentity { this.mangroveIdentity = mangroveIdentity this._mangroveIdentityCreationDate = mangroveIdentityCreationDate mangroveIdentity.addCallbackAndRunD(async (data) => { - if(data === ""){ + if (data === "") { return } await this.setKeypair(data) diff --git a/src/Logic/Web/NameSuggestionIndex.ts b/src/Logic/Web/NameSuggestionIndex.ts index 04fab73aa..7133c1247 100644 --- a/src/Logic/Web/NameSuggestionIndex.ts +++ b/src/Logic/Web/NameSuggestionIndex.ts @@ -297,15 +297,13 @@ export default class NameSuggestionIndex { return true } - if ( - i.locationSet.include.some((c) => countries.indexOf(c) >= 0) - ) { + if (i.locationSet.include.some((c) => countries.indexOf(c) >= 0)) { // We prefer the countries provided by lonlat2country, they are more precise and are loaded already anyway (cheap) // Country might contain multiple countries, separated by ';' return true } - if (i.locationSet.exclude?.some(c => countries.indexOf(c) >= 0)) { + if (i.locationSet.exclude?.some((c) => countries.indexOf(c) >= 0)) { return false } @@ -313,18 +311,20 @@ export default class NameSuggestionIndex { return true } - const hasSpecial = i.locationSet.include?.some(i => i.endsWith(".geojson") || Array.isArray(i)) || i.locationSet.exclude?.some(i => i.endsWith(".geojson") || Array.isArray(i)) + const hasSpecial = + i.locationSet.include?.some((i) => i.endsWith(".geojson") || Array.isArray(i)) || + i.locationSet.exclude?.some((i) => i.endsWith(".geojson") || Array.isArray(i)) if (!hasSpecial) { return false } const key = i.locationSet.include?.join(";") + "-" + i.locationSet.exclude?.join(";") const fromCache = NameSuggestionIndex.resolvedSets[key] - const resolvedSet = fromCache ?? NameSuggestionIndex.loco.resolveLocationSet(i.locationSet) + const resolvedSet = + fromCache ?? NameSuggestionIndex.loco.resolveLocationSet(i.locationSet) if (!fromCache) { NameSuggestionIndex.resolvedSets[key] = resolvedSet } - if (resolvedSet) { // We actually have a location set, so we can check if the feature is in it, by determining if our point is inside the MultiPolygon using @turf/boolean-point-in-polygon // This might occur for some extra boundaries, such as counties, ... diff --git a/src/Logic/Web/NearbyImagesSearch.ts b/src/Logic/Web/NearbyImagesSearch.ts index bf9f571f4..0b4c07f70 100644 --- a/src/Logic/Web/NearbyImagesSearch.ts +++ b/src/Logic/Web/NearbyImagesSearch.ts @@ -58,7 +58,7 @@ export interface P4CPicture { author? license? detailsUrl?: string - direction?: number, + direction?: number osmTags?: object /*To copy straight into OSM!*/ thumbUrl: string details: { @@ -103,7 +103,7 @@ class P4CImageFetcher implements ImageFetcher { { mindate: new Date().getTime() - maxAgeSeconds, towardscenter: false, - }, + } ) } catch (e) { console.log("P4C image fetcher failed with", e) @@ -172,16 +172,13 @@ class ImagesFromPanoramaxFetcher implements ImageFetcher { constructor(url?: string, radius: number = 100) { this._radius = radius if (url) { - this._panoramax = new Panoramax(url) } else { this._panoramax = new PanoramaxXYZ() } } - public async fetchImages(lat: number, lon: number): Promise { - const bboxObj = new BBox([ GeoOperations.destination([lon, lat], this._radius * Math.sqrt(2), -45), GeoOperations.destination([lon, lat], this._radius * Math.sqrt(2), 135), @@ -189,16 +186,16 @@ class ImagesFromPanoramaxFetcher implements ImageFetcher { const bbox: [number, number, number, number] = bboxObj.toLngLatFlat() const images = await this._panoramax.search({ bbox, limit: 1000 }) - return images.map(i => { + return images.map((i) => { const [lng, lat] = i.geometry.coordinates - return ({ + return { pictureUrl: i.assets.sd.href, coordinates: { lng, lat }, provider: "panoramax", direction: i.properties["view:azimuth"], osmTags: { - "panoramax": i.id, + panoramax: i.id, }, thumbUrl: i.assets.thumb.href, date: new Date(i.properties.datetime).getTime(), @@ -206,9 +203,10 @@ class ImagesFromPanoramaxFetcher implements ImageFetcher { author: i.providers.at(-1).name, detailsUrl: i.id, details: { - isSpherical: i.properties["exif"]["Xmp.GPano.ProjectionType"] === "equirectangular", + isSpherical: + i.properties["exif"]["Xmp.GPano.ProjectionType"] === "equirectangular", }, - }) + } }) } } @@ -236,7 +234,7 @@ class ImagesFromCacheServerFetcher implements ImageFetcher { async fetchImagesForType( targetlat: number, targetlon: number, - type: "lines" | "pois" | "polygons", + type: "lines" | "pois" | "polygons" ): Promise { const { x, y, z } = Tiles.embedded_tile(targetlat, targetlon, 14) @@ -253,7 +251,7 @@ class ImagesFromCacheServerFetcher implements ImageFetcher { }), x, y, - z, + z ) await src.updateAsync() return src.features.data @@ -427,7 +425,7 @@ export class CombinedFetcher { lat: number, lon: number, state: UIEventSource>, - sink: UIEventSource, + sink: UIEventSource ): Promise { try { const pics = await source.fetchImages(lat, lon) @@ -460,7 +458,7 @@ export class CombinedFetcher { public getImagesAround( lon: number, - lat: number, + lat: number ): { images: Store state: Store> diff --git a/src/Logic/Web/Wikidata.ts b/src/Logic/Web/Wikidata.ts index e4418515a..56a35fda6 100644 --- a/src/Logic/Web/Wikidata.ts +++ b/src/Logic/Web/Wikidata.ts @@ -1,6 +1,6 @@ import { Utils } from "../../Utils" import { Store, UIEventSource } from "../UIEventSource" -import { WBK} from "wikibase-sdk" +import { WBK } from "wikibase-sdk" export class WikidataResponse { public readonly id: string @@ -128,10 +128,9 @@ interface SparqlResult { * Utility functions around wikidata */ export default class Wikidata { - public static wds = WBK({ instance: "https://wikidata.org", - sparqlEndpoint: "https://query.wikidata.org/bigdata/namespace/wdq/sparql" + sparqlEndpoint: "https://query.wikidata.org/bigdata/namespace/wdq/sparql", }) public static readonly neededUrls = [ @@ -211,7 +210,7 @@ export default class Wikidata { ${instanceOf} ${minusPhrases.join("\n ")} } ORDER BY ASC(?num) LIMIT ${options?.maxCount ?? 20}` - const url = Wikidata. wds.sparqlQuery(sparql) + const url = Wikidata.wds.sparqlQuery(sparql) const result = await Utils.downloadJson(url) /*The full uri of the wikidata-item*/ @@ -252,7 +251,7 @@ export default class Wikidata { lang + "&type=item&origin=*" + "&props=" // props= removes some unused values in the result - const response = await Utils.downloadJsonCached<{search: any[]}>(url, 10000) + const response = await Utils.downloadJsonCached<{ search: any[] }>(url, 10000) const result = response.search @@ -401,7 +400,7 @@ export default class Wikidata { "}" const url = Wikidata.wds.sparqlQuery(query) const result = await Utils.downloadJsonCached(url, 24 * 60 * 60 * 1000) - return result.results.bindings + return result.results.bindings } private static _cache = new Map>() diff --git a/src/Models/Constants.ts b/src/Models/Constants.ts index ddc05165c..bbb102401 100644 --- a/src/Models/Constants.ts +++ b/src/Models/Constants.ts @@ -27,7 +27,7 @@ export default class Constants { "favourite", "summary", "search", - "geocoded_image" + "geocoded_image", ] as const /** * Special layers which are not included in a theme by default @@ -50,7 +50,7 @@ export default class Constants { ...Constants.no_include, ] as const - public static panoramax: { url: string, token: string } = packagefile.config.panoramax + public static panoramax: { url: string; token: string } = packagefile.config.panoramax // The user journey states thresholds when a new feature gets unlocked public static userJourney = { diff --git a/src/Models/FilteredLayer.ts b/src/Models/FilteredLayer.ts index 79c59c03c..420935051 100644 --- a/src/Models/FilteredLayer.ts +++ b/src/Models/FilteredLayer.ts @@ -36,7 +36,7 @@ export default class FilteredLayer { constructor( layer: LayerConfig, appliedFilters?: ReadonlyMap>, - isDisplayed?: UIEventSource, + isDisplayed?: UIEventSource ) { this.layerDef = layer this.isDisplayed = isDisplayed ?? new UIEventSource(true) @@ -82,25 +82,25 @@ export default class FilteredLayer { layer: LayerConfig, context: string, osmConnection: OsmConnection, - enabledByDefault?: Store, + enabledByDefault?: Store ) { let isDisplayed: UIEventSource if (layer.syncSelection === "local") { isDisplayed = LocalStorageSource.getParsed( context + "-layer-" + layer.id + "-enabled", - layer.shownByDefault, + layer.shownByDefault ) } else if (layer.syncSelection === "theme-only") { isDisplayed = FilteredLayer.getPref( osmConnection, context + "-layer-" + layer.id + "-enabled", - layer, + layer ) } else if (layer.syncSelection === "global") { isDisplayed = FilteredLayer.getPref( osmConnection, "layer-" + layer.id + "-enabled", - layer, + layer ) } else { let isShown = layer.shownByDefault @@ -110,7 +110,7 @@ export default class FilteredLayer { isDisplayed = QueryParameters.GetBooleanQueryParameter( FilteredLayer.queryParameterKey(layer), isShown, - "Whether or not layer " + layer.id + " is shown", + "Whether or not layer " + layer.id + " is shown" ) } @@ -145,7 +145,7 @@ export default class FilteredLayer { */ private static fieldsToTags( option: FilterConfigOption, - fieldstate: string | Record, + fieldstate: string | Record ): TagsFilter | undefined { let properties: Record if (typeof fieldstate === "string") { @@ -181,7 +181,7 @@ export default class FilteredLayer { private static getPref( osmConnection: OsmConnection, key: string, - layer: LayerConfig, + layer: LayerConfig ): UIEventSource { return osmConnection.GetPreference(key, layer.shownByDefault + "").sync( (v) => { @@ -196,7 +196,7 @@ export default class FilteredLayer { return undefined } return "" + b - }, + } ) } diff --git a/src/Models/MapProperties.ts b/src/Models/MapProperties.ts index 336b3686c..69d016f5e 100644 --- a/src/Models/MapProperties.ts +++ b/src/Models/MapProperties.ts @@ -32,7 +32,6 @@ export interface MapProperties { onKeyNavigationEvent(f: (event: KeyNavigationEvent) => void | boolean): () => void flyTo(lon: number, lat: number, zoom: number): void - } export interface ExportableMap { diff --git a/src/Models/RasterLayers.ts b/src/Models/RasterLayers.ts index c05d34bb8..0e0cb0e79 100644 --- a/src/Models/RasterLayers.ts +++ b/src/Models/RasterLayers.ts @@ -23,25 +23,25 @@ export class AvailableRasterLayers { } console.debug("Downloading ELI") const eli = await Utils.downloadJson<{ features: EditorLayerIndex }>( - "./assets/data/editor-layer-index.json", + "./assets/data/editor-layer-index.json" ) this._editorLayerIndex = eli.features?.filter((l) => l.properties.id !== "Bing") ?? [] this._editorLayerIndexStore.set(this._editorLayerIndex) return this._editorLayerIndex } - public static readonly globalLayers: ReadonlyArray = AvailableRasterLayers.initGlobalLayers() + public static readonly globalLayers: ReadonlyArray = + AvailableRasterLayers.initGlobalLayers() private static initGlobalLayers(): RasterLayerPolygon[] { - const gl: RasterLayerProperties[] = (globallayers["default"] ?? globallayers ).layers - .filter( - (properties) => - properties.id !== "osm.carto" && properties.id !== "Bing", /*Added separately*/ - ) + const gl: RasterLayerProperties[] = (globallayers["default"] ?? globallayers).layers.filter( + (properties) => + properties.id !== "osm.carto" && properties.id !== "Bing" /*Added separately*/ + ) const glEli: RasterLayerProperties[] = globallayersEli["default"] ?? globallayersEli const joined = gl.concat(glEli) - if (joined.some(j => !j.id)) { - console.log("Invalid layers:", JSON.stringify(joined .filter(l => !l.id))) + if (joined.some((j) => !j.id)) { + console.log("Invalid layers:", JSON.stringify(joined.filter((l) => !l.id))) throw "Detected invalid global layer with invalid id" } return joined.map( @@ -50,7 +50,7 @@ export class AvailableRasterLayers { type: "Feature", properties, geometry: BBox.global.asGeometry(), - }, + } ) } @@ -85,18 +85,18 @@ export class AvailableRasterLayers { public static layersAvailableAt( location: Store<{ lon: number; lat: number }>, - enableBing?: Store, + enableBing?: Store ): { store: Store } { const store = { store: undefined } Utils.AddLazyProperty(store, "store", () => - AvailableRasterLayers._layersAvailableAt(location, enableBing), + AvailableRasterLayers._layersAvailableAt(location, enableBing) ) return store } private static _layersAvailableAt( location: Store<{ lon: number; lat: number }>, - enableBing?: Store, + enableBing?: Store ): Store { this.editorLayerIndex() // start the download const availableLayersBboxes = Stores.ListStabilized( @@ -109,8 +109,8 @@ export class AvailableRasterLayers { const lonlat: [number, number] = [loc.lon, loc.lat] return eli.filter((eliPolygon) => BBox.get(eliPolygon).contains(lonlat)) }, - [AvailableRasterLayers._editorLayerIndexStore], - ), + [AvailableRasterLayers._editorLayerIndexStore] + ) ) return Stores.ListStabilized( availableLayersBboxes.map( @@ -132,15 +132,15 @@ export class AvailableRasterLayers { if ( !matching.some( (l) => - l.id === AvailableRasterLayers.defaultBackgroundLayer.properties.id, + l.id === AvailableRasterLayers.defaultBackgroundLayer.properties.id ) ) { matching.push(AvailableRasterLayers.defaultBackgroundLayer) } return matching }, - [enableBing], - ), + [enableBing] + ) ) } } @@ -159,7 +159,7 @@ export class RasterLayerUtils { available: RasterLayerPolygon[], preferredCategory: string, ignoreLayer?: RasterLayerPolygon, - skipLayers: number = 0, + skipLayers: number = 0 ): RasterLayerPolygon { const inCategory = available.filter((l) => l.properties.category === preferredCategory) const best: RasterLayerPolygon[] = inCategory.filter((l) => l.properties.best) @@ -167,7 +167,7 @@ export class RasterLayerUtils { let all = best.concat(others) console.log( "Selected layers are:", - all.map((l) => l.properties.id), + all.map((l) => l.properties.id) ) if (others.length > skipLayers) { all = all.slice(skipLayers) diff --git a/src/Models/ThemeConfig/Conversion/PrepareLayer.ts b/src/Models/ThemeConfig/Conversion/PrepareLayer.ts index 470bbdd93..edd779a62 100644 --- a/src/Models/ThemeConfig/Conversion/PrepareLayer.ts +++ b/src/Models/ThemeConfig/Conversion/PrepareLayer.ts @@ -10,7 +10,10 @@ import { SetDefault, } from "./Conversion" import { LayerConfigJson } from "../Json/LayerConfigJson" -import { MinimalTagRenderingConfigJson, TagRenderingConfigJson } from "../Json/TagRenderingConfigJson" +import { + MinimalTagRenderingConfigJson, + TagRenderingConfigJson, +} from "../Json/TagRenderingConfigJson" import { Utils } from "../../../Utils" import RewritableConfigJson from "../Json/RewritableConfigJson" import SpecialVisualizations from "../../../UI/SpecialVisualizations" @@ -31,23 +34,26 @@ import { ConversionContext } from "./ConversionContext" import { ExpandRewrite } from "./ExpandRewrite" import { TagUtils } from "../../../Logic/Tags/TagUtils" - class AddFiltersFromTagRenderings extends DesugaringStep { constructor() { - super("Inspects all the tagRenderings. If some tagRenderings have the `filter` attribute set, introduce those filters. This step might introduce shorthand filter names, thus 'ExpandFilter' should be run afterwards. Can be disabled with \"#filter\":\"no-auto\"", ["filter"], "AddFiltersFromTagRenderings") + super( + 'Inspects all the tagRenderings. If some tagRenderings have the `filter` attribute set, introduce those filters. This step might introduce shorthand filter names, thus \'ExpandFilter\' should be run afterwards. Can be disabled with "#filter":"no-auto"', + ["filter"], + "AddFiltersFromTagRenderings" + ) } convert(json: LayerConfigJson, context: ConversionContext): LayerConfigJson { const noAutoFilters = json["#filter"] === "no-auto" - if(noAutoFilters){ + if (noAutoFilters) { return json } - if(json.filter?.["sameAs"]){ + if (json.filter?.["sameAs"]) { return json } - const filters: (FilterConfigJson | string)[] = [...json.filter ?? []] + const filters: (FilterConfigJson | string)[] = [...(json.filter ?? [])] function filterExists(filterName: string): boolean { return filters.some((existing) => { @@ -59,8 +65,6 @@ class AddFiltersFromTagRenderings extends DesugaringStep { }) } - - for (let i = 0; i < json.tagRenderings?.length; i++) { const tagRendering = json.tagRenderings[i] if (!tagRendering?.filter) { @@ -70,7 +74,12 @@ class AddFiltersFromTagRenderings extends DesugaringStep { if (filterExists(tagRendering["id"])) { continue } - filters.push(ExpandFilter.buildFilterFromTagRendering(tagRendering, context.enters("tagRenderings", i, "filter"))) + filters.push( + ExpandFilter.buildFilterFromTagRendering( + tagRendering, + context.enters("tagRenderings", i, "filter") + ) + ) continue } for (const filterName of tagRendering.filter ?? []) { @@ -89,7 +98,7 @@ class AddFiltersFromTagRenderings extends DesugaringStep { } } - if(filters.length === 0){ + if (filters.length === 0) { return json } @@ -102,10 +111,12 @@ class ExpandFilter extends DesugaringStep { constructor(state: DesugaringContext) { super( - ["Expands filters: replaces a shorthand by the value found in 'filters.json'.", - "If the string is formatted 'layername.filtername, it will be looked up into that layer instead."].join(" "), + [ + "Expands filters: replaces a shorthand by the value found in 'filters.json'.", + "If the string is formatted 'layername.filtername, it will be looked up into that layer instead.", + ].join(" "), ["filter"], - "ExpandFilter", + "ExpandFilter" ) this._state = state } @@ -118,10 +129,13 @@ class ExpandFilter extends DesugaringStep { return filters } - public static buildFilterFromTagRendering(tr: TagRenderingConfigJson, context: ConversionContext): FilterConfigJson { + public static buildFilterFromTagRendering( + tr: TagRenderingConfigJson, + context: ConversionContext + ): FilterConfigJson { if (!(tr.mappings?.length >= 1)) { context.err( - "Found a matching tagRendering to base a filter on, but this tagRendering does not contain any mappings", + "Found a matching tagRendering to base a filter on, but this tagRendering does not contain any mappings" ) } const options = (tr).mappings.map((mapping) => { @@ -131,12 +145,13 @@ class ExpandFilter extends DesugaringStep { emoji = icon icon = undefined } - return ({ + return { question: mapping.then, osmTags: mapping.if, searchTerms: mapping.searchTerms, - icon, emoji, - }) + icon, + emoji, + } }) // Add default option options.unshift({ @@ -144,10 +159,10 @@ class ExpandFilter extends DesugaringStep { osmTags: undefined, searchTerms: undefined, }) - return ({ + return { id: tr["id"], options, - }) + } } convert(json: LayerConfigJson, context: ConversionContext): LayerConfigJson { @@ -159,11 +174,9 @@ class ExpandFilter extends DesugaringStep { return json // Nothing to change here } - const newFilters: FilterConfigJson[] = [] const filters = <(FilterConfigJson | string)[]>json.filter - /** * Create filters based on builtin filters or create them based on the tagRendering */ @@ -181,7 +194,10 @@ class ExpandFilter extends DesugaringStep { json.tagRenderings.find((tr) => !!tr && tr["id"] === filter) ) if (matchingTr) { - const filter = ExpandFilter.buildFilterFromTagRendering(matchingTr, context.enters("filter", i)) + const filter = ExpandFilter.buildFilterFromTagRendering( + matchingTr, + context.enters("filter", i) + ) newFilters.push(filter) continue } @@ -194,7 +210,7 @@ class ExpandFilter extends DesugaringStep { const split = filter.split(".") if (split.length > 2) { context.err( - "invalid filter name: " + filter + ", expected `layername.filterid`", + "invalid filter name: " + filter + ", expected `layername.filterid`" ) } const layer = this._state.sharedLayers.get(split[0]) @@ -203,7 +219,7 @@ class ExpandFilter extends DesugaringStep { } const expectedId = split[1] const expandedFilter = (<(FilterConfigJson | string)[]>layer.filter).find( - (f) => typeof f !== "string" && f.id === expectedId, + (f) => typeof f !== "string" && f.id === expectedId ) if (expandedFilter === undefined) { context.err("Did not find filter with name " + filter) @@ -218,15 +234,15 @@ class ExpandFilter extends DesugaringStep { const suggestions = Utils.sortedByLevenshteinDistance( filter, Array.from(ExpandFilter.predefinedFilters.keys()), - (t) => t, + (t) => t ) context .enter(filter) .err( "While searching for predefined filter " + - filter + - ": this filter is not found. Perhaps you meant one of: " + - suggestions, + filter + + ": this filter is not found. Perhaps you meant one of: " + + suggestions ) } newFilters.push(found) @@ -239,9 +255,9 @@ class ExpandTagRendering extends Conversion< | string | TagRenderingConfigJson | { - builtin: string | string[] - override: any -}, + builtin: string | string[] + override: any + }, TagRenderingConfigJson[] > { private readonly _state: DesugaringContext @@ -263,12 +279,12 @@ class ExpandTagRendering extends Conversion< noHardcodedStrings?: false | boolean // If set, a question will be added to the 'sharedTagRenderings'. Should only be used for 'questions.json' addToContext?: false | boolean - }, + } ) { super( "Converts a tagRenderingSpec into the full tagRendering, e.g. by substituting the tagRendering by the shared-question and reusing the builtins", [], - "ExpandTagRendering", + "ExpandTagRendering" ) this._state = state this._self = self @@ -288,7 +304,7 @@ class ExpandTagRendering extends Conversion< public convert( spec: string | any, - ctx: ConversionContext, + ctx: ConversionContext ): QuestionableTagRenderingConfigJson[] { const trs = this.convertOnce(spec, ctx) @@ -401,8 +417,8 @@ class ExpandTagRendering extends Conversion< found, ConversionContext.construct( [layer.id, "tagRenderings", found["id"]], - ["AddContextToTranslations"], - ), + ["AddContextToTranslations"] + ) ) matchingTrs[i] = found } @@ -430,17 +446,17 @@ class ExpandTagRendering extends Conversion< ctx.warn( `A literal rendering was detected: ${tr} Did you perhaps forgot to add a layer name as 'layername.${tr}'? ` + - Array.from(state.sharedLayers.keys()).join(", "), + Array.from(state.sharedLayers.keys()).join(", ") ) } if (this._options?.noHardcodedStrings && this._state?.sharedLayers?.size > 0) { ctx.err( "Detected an invocation to a builtin tagRendering, but this tagrendering was not found: " + - tr + - " \n Did you perhaps forget to add the layer as prefix, such as `icons." + - tr + - "`? ", + tr + + " \n Did you perhaps forget to add the layer as prefix, such as `icons." + + tr + + "`? " ) } @@ -475,9 +491,9 @@ class ExpandTagRendering extends Conversion< } ctx.err( "An object calling a builtin can only have keys `builtin` or `override`, but a key with name `" + - key + - "` was found. This won't be picked up! The full object is: " + - JSON.stringify(tr), + key + + "` was found. This won't be picked up! The full object is: " + + JSON.stringify(tr) ) } @@ -496,39 +512,39 @@ class ExpandTagRendering extends Conversion< const candidates = Utils.sortedByLevenshteinDistance( layerName, Array.from(state.sharedLayers.keys()), - (s) => s, + (s) => s ) if (state.sharedLayers.size === 0) { ctx.warn( "BOOTSTRAPPING. Rerun generate layeroverview. While reusing tagrendering: " + - name + - ": layer " + - layerName + - " not found for now, but ignoring as this is a bootstrapping run. ", + name + + ": layer " + + layerName + + " not found for now, but ignoring as this is a bootstrapping run. " ) } else { ctx.err( ": While reusing tagrendering: " + - name + - ": layer " + - layerName + - " not found. Maybe you meant one of " + - candidates.slice(0, 3).join(", "), + name + + ": layer " + + layerName + + " not found. Maybe you meant one of " + + candidates.slice(0, 3).join(", ") ) } continue } candidates = Utils.NoNull(layer.tagRenderings.map((tr) => tr["id"])).map( - (id) => layerName + "." + id, + (id) => layerName + "." + id ) } candidates = Utils.sortedByLevenshteinDistance(name, candidates, (i) => i) ctx.err( "The tagRendering with identifier " + - name + - " was not found.\n\tDid you mean one of " + - candidates.join(", ") + - "?\n(Hint: did you add a new label and are you trying to use this label at the same time? Run 'reset:layeroverview' first", + name + + " was not found.\n\tDid you mean one of " + + candidates.join(", ") + + "?\n(Hint: did you add a new label and are you trying to use this label at the same time? Run 'reset:layeroverview' first" ) continue } @@ -553,13 +569,13 @@ class DetectInline extends DesugaringStep { super( "If no 'inline' is set on the freeform key, it will be automatically added. If no special renderings are used, it'll be set to true", ["freeform.inline"], - "DetectInline", + "DetectInline" ) } convert( json: QuestionableTagRenderingConfigJson, - context: ConversionContext, + context: ConversionContext ): QuestionableTagRenderingConfigJson { if (json.freeform === undefined) { return json @@ -582,7 +598,7 @@ class DetectInline extends DesugaringStep { if (json.freeform.inline === true) { context.err( "'inline' is set, but the rendering contains a special visualisation...\n " + - spec[key], + spec[key] ) } json = JSON.parse(JSON.stringify(json)) @@ -596,17 +612,18 @@ class DetectInline extends DesugaringStep { return json } - if(json.render === undefined){ + if (json.render === undefined) { context.err("No 'render' defined") return json } - if(!Object.values(json?.render)?.some(render => render !== "{"+json.freeform.key+"}")){ + if ( + !Object.values(json?.render)?.some((render) => render !== "{" + json.freeform.key + "}") + ) { // We only render the current value, without anything more. Not worth inlining return json } - json.freeform.inline ??= true return json } @@ -617,7 +634,7 @@ export class AddQuestionBox extends DesugaringStep { super( "Adds a 'questions'-object if no question element is added yet", ["tagRenderings"], - "AddQuestionBox", + "AddQuestionBox" ) } @@ -641,18 +658,18 @@ export class AddQuestionBox extends DesugaringStep { json.tagRenderings = [...json.tagRenderings] const allSpecials: Exclude[] = ( ValidationUtils.getAllSpecialVisualisations( - json.tagRenderings, + json.tagRenderings ).filter((spec) => typeof spec !== "string") ) const questionSpecials = allSpecials.filter((sp) => sp.func.funcName === "questions") const noLabels = questionSpecials.filter( - (sp) => sp.args.length === 0 || sp.args[0].trim() === "", + (sp) => sp.args.length === 0 || sp.args[0].trim() === "" ) if (noLabels.length > 1) { context.err( - "Multiple 'questions'-visualisations found which would show _all_ questions. Don't do this", + "Multiple 'questions'-visualisations found which would show _all_ questions. Don't do this" ) } @@ -660,9 +677,9 @@ export class AddQuestionBox extends DesugaringStep { const allLabels = new Set( [].concat( ...json.tagRenderings.map( - (tr) => (tr).labels ?? [], - ), - ), + (tr) => (tr).labels ?? [] + ) + ) ) const seen: Set = new Set() for (const questionSpecial of questionSpecials) { @@ -680,20 +697,20 @@ export class AddQuestionBox extends DesugaringStep { if (blacklisted?.length > 0 && used?.length > 0) { context.err( "The {questions()}-special rendering only supports either a blacklist OR a whitelist, but not both." + - "\n Whitelisted: " + - used.join(", ") + - "\n Blacklisted: " + - blacklisted.join(", "), + "\n Whitelisted: " + + used.join(", ") + + "\n Blacklisted: " + + blacklisted.join(", ") ) } for (const usedLabel of used) { if (!allLabels.has(usedLabel)) { context.err( "This layers specifies a special question element for label `" + - usedLabel + - "`, but this label doesn't exist.\n" + - " Available labels are " + - Array.from(allLabels).join(", "), + usedLabel + + "`, but this label doesn't exist.\n" + + " Available labels are " + + Array.from(allLabels).join(", ") ) } seen.add(usedLabel) @@ -726,7 +743,7 @@ export class AddEditingElements extends DesugaringStep { super( "Add some editing elements, such as the delete button or the move button if they are configured. These used to be handled by the feature info box, but this has been replaced by special visualisation elements", [], - "AddEditingElements", + "AddEditingElements" ) this._desugaring = desugaring this.builtinQuestions = Array.from(this._desugaring.tagRenderings?.values() ?? []) @@ -756,13 +773,13 @@ export class AddEditingElements extends DesugaringStep { json.tagRenderings = [...(json.tagRenderings ?? [])] const allIds = new Set(json.tagRenderings.map((tr) => tr["id"])) const specialVisualisations = ValidationUtils.getAllSpecialVisualisations( - json.tagRenderings, + json.tagRenderings ) const usedSpecialFunctions = new Set( specialVisualisations.map((sv) => - typeof sv === "string" ? undefined : sv.func.funcName, - ), + typeof sv === "string" ? undefined : sv.func.funcName + ) ) /***** ADD TO TOP ****/ @@ -830,7 +847,7 @@ export class RewriteSpecial extends DesugaringStep { super( "Converts a 'special' translation into a regular translation which uses parameters", ["special"], - "RewriteSpecial", + "RewriteSpecial" ) } @@ -921,12 +938,12 @@ export class RewriteSpecial extends DesugaringStep { private static convertIfNeeded( input: | (object & { - special: { - type: string - } - }) + special: { + type: string + } + }) | any, - context: ConversionContext, + context: ConversionContext ): any { const special = input["special"] if (special === undefined) { @@ -936,7 +953,7 @@ export class RewriteSpecial extends DesugaringStep { const type = special["type"] if (type === undefined) { context.err( - "A 'special'-block should define 'type' to indicate which visualisation should be used", + "A 'special'-block should define 'type' to indicate which visualisation should be used" ) return undefined } @@ -946,10 +963,10 @@ export class RewriteSpecial extends DesugaringStep { const options = Utils.sortedByLevenshteinDistance( type, SpecialVisualizations.specialVisualizations, - (sp) => sp.funcName, + (sp) => sp.funcName ) context.err( - `Special visualisation '${type}' not found. Did you perhaps mean ${options[0].funcName}, ${options[1].funcName} or ${options[2].funcName}?\n\tFor all known special visualisations, please see https://github.com/pietervdvn/MapComplete/blob/develop/Docs/SpecialRenderings.md`, + `Special visualisation '${type}' not found. Did you perhaps mean ${options[0].funcName}, ${options[1].funcName} or ${options[2].funcName}?\n\tFor all known special visualisations, please see https://github.com/pietervdvn/MapComplete/blob/develop/Docs/SpecialRenderings.md` ) return undefined } @@ -970,7 +987,7 @@ export class RewriteSpecial extends DesugaringStep { const byDistance = Utils.sortedByLevenshteinDistance( wrongArg, argNamesList, - (x) => x, + (x) => x ) return `Unexpected argument in special block at ${context} with name '${wrongArg}'. Did you mean ${ byDistance[0] @@ -989,8 +1006,8 @@ export class RewriteSpecial extends DesugaringStep { `Obligated parameter '${arg.name}' in special rendering of type ${ vis.funcName } not found.\n The full special rendering specification is: '${JSON.stringify( - input, - )}'\n ${arg.name}: ${arg.doc}`, + input + )}'\n ${arg.name}: ${arg.doc}` ) } } @@ -1092,7 +1109,7 @@ export class RewriteSpecial extends DesugaringStep { continue } Utils.WalkPath(path.path, json, (leaf, travelled) => - RewriteSpecial.convertIfNeeded(leaf, context.enter(travelled)), + RewriteSpecial.convertIfNeeded(leaf, context.enter(travelled)) ) } @@ -1126,7 +1143,7 @@ class ExpandIconBadges extends DesugaringStep { } = badgesJson[i] const expanded = this._expand.convert( iconBadge.then, - context.enters("iconBadges", i), + context.enters("iconBadges", i) ) if (expanded === undefined) { iconBadges.push(iconBadge) @@ -1137,7 +1154,7 @@ class ExpandIconBadges extends DesugaringStep { ...expanded.map((resolved) => ({ if: iconBadge.if, then: resolved, - })), + })) ) } @@ -1154,11 +1171,11 @@ class PreparePointRendering extends Fuse { new Each( new On( "icon", - new FirstOf(new ExpandTagRendering(state, layer, { applyCondition: false })), - ), - ), + new FirstOf(new ExpandTagRendering(state, layer, { applyCondition: false })) + ) + ) ), - new ExpandIconBadges(state, layer), + new ExpandIconBadges(state, layer) ) } } @@ -1168,7 +1185,7 @@ class SetFullNodeDatabase extends DesugaringStep { super( "sets the fullNodeDatabase-bit if needed", ["fullNodeDatabase"], - "SetFullNodeDatabase", + "SetFullNodeDatabase" ) } @@ -1197,7 +1214,7 @@ class ExpandMarkerRenderings extends DesugaringStep { super( "Expands tagRenderings in the icons, if needed", ["icon", "color"], - "ExpandMarkerRenderings", + "ExpandMarkerRenderings" ) this._layer = layer this._state = state @@ -1229,7 +1246,7 @@ class AddFavouriteBadges extends DesugaringStep { super( "Adds the favourite heart to the title and the rendering badges", [], - "AddFavouriteBadges", + "AddFavouriteBadges" ) } @@ -1254,7 +1271,7 @@ export class AddRatingBadge extends DesugaringStep { super( "Adds the 'rating'-element if a reviews-element is used in the tagRenderings", ["titleIcons"], - "AddRatingBadge", + "AddRatingBadge" ) } @@ -1273,8 +1290,8 @@ export class AddRatingBadge extends DesugaringStep { const specialVis: Exclude[] = < Exclude[] - >ValidationUtils.getAllSpecialVisualisations(json.tagRenderings).filter( - (rs) => typeof rs !== "string", + >ValidationUtils.getAllSpecialVisualisations(json.tagRenderings).filter( + (rs) => typeof rs !== "string" ) const funcs = new Set(specialVis.map((rs) => rs.func.funcName)) @@ -1290,12 +1307,12 @@ export class AutoTitleIcon extends DesugaringStep { super( "The auto-icon creates a (non-clickable) title icon based on a tagRendering which has icons", ["titleIcons"], - "AutoTitleIcon", + "AutoTitleIcon" ) } private createTitleIconsBasedOn( - tr: QuestionableTagRenderingConfigJson, + tr: QuestionableTagRenderingConfigJson ): TagRenderingConfigJson | undefined { const mappings: { if: TagConfigJson; then: string }[] = tr.mappings ?.filter((m) => m.icon !== undefined) @@ -1325,7 +1342,7 @@ export class AutoTitleIcon extends DesugaringStep { return undefined } return this.createTitleIconsBasedOn(tr) - }), + }) ) json.titleIcons.splice(allAutoIndex, 1, ...generated) return json @@ -1354,8 +1371,8 @@ export class AutoTitleIcon extends DesugaringStep { .enters("titleIcons", i) .warn( "TagRendering with id " + - trId + - " does not have any icons, not generating an icon for this", + trId + + " does not have any icons, not generating an icon for this" ) continue } @@ -1370,7 +1387,7 @@ class DeriveSource extends DesugaringStep { super( "If no source is given, automatically derives the osmTags by 'or'-ing all the preset tags", ["source"], - "DeriveSource", + "DeriveSource" ) } @@ -1380,7 +1397,7 @@ class DeriveSource extends DesugaringStep { } if (!json.presets) { context.err( - "No source tags given. Trying to derive the source-tags based on the presets, but no presets are given", + "No source tags given. Trying to derive the source-tags based on the presets, but no presets are given" ) return json } @@ -1406,7 +1423,7 @@ class DeriveSource extends DesugaringStep { export class PrepareLayer extends Fuse { constructor( state: DesugaringContext, - options?: { addTagRenderingsToContext?: false | boolean }, + options?: { addTagRenderingsToContext?: false | boolean } ) { super( "Fully prepares and expands a layer for the LayerConfig.", @@ -1419,8 +1436,8 @@ export class PrepareLayer extends Fuse { new Concat( new ExpandTagRendering(state, layer, { addToContext: options?.addTagRenderingsToContext ?? false, - }), - ), + }) + ) ), new On("tagRenderings", new Each(new DetectInline())), new AddQuestionBox(), @@ -1433,11 +1450,11 @@ export class PrepareLayer extends Fuse { new On( "pointRendering", (layer) => - new Each(new On("marker", new Each(new ExpandMarkerRenderings(state, layer)))), + new Each(new On("marker", new Each(new ExpandMarkerRenderings(state, layer)))) ), new On( "pointRendering", - (layer) => new Each(new PreparePointRendering(state, layer)), + (layer) => new Each(new PreparePointRendering(state, layer)) ), new SetDefault("titleIcons", ["icons.defaults"]), new AddRatingBadge(), @@ -1446,10 +1463,10 @@ export class PrepareLayer extends Fuse { new On( "titleIcons", (layer) => - new Concat(new ExpandTagRendering(state, layer, { noHardcodedStrings: true })), + new Concat(new ExpandTagRendering(state, layer, { noHardcodedStrings: true })) ), new AddFiltersFromTagRenderings(), - new ExpandFilter(state), + new ExpandFilter(state) ) } } diff --git a/src/Models/ThemeConfig/Conversion/PrepareTheme.ts b/src/Models/ThemeConfig/Conversion/PrepareTheme.ts index 21518a730..f1081cbd6 100644 --- a/src/Models/ThemeConfig/Conversion/PrepareTheme.ts +++ b/src/Models/ThemeConfig/Conversion/PrepareTheme.ts @@ -1,4 +1,14 @@ -import { Concat, Conversion, DesugaringContext, DesugaringStep, Each, Fuse, On, Pass, SetDefault } from "./Conversion" +import { + Concat, + Conversion, + DesugaringContext, + DesugaringStep, + Each, + Fuse, + On, + Pass, + SetDefault, +} from "./Conversion" import { ThemeConfigJson } from "../Json/ThemeConfigJson" import { PrepareLayer } from "./PrepareLayer" import { LayerConfigJson } from "../Json/LayerConfigJson" @@ -18,7 +28,7 @@ class SubstituteLayer extends Conversion [ + const withDistance: [string, number][] = knownLayers.map((lname) => [ lname, Utils.levenshteinDistance(name, lname), ]) @@ -74,14 +84,14 @@ class SubstituteLayer extends Conversion 0 ) { context.err( - `When overriding a layer, an override is not allowed to override into tagRenderings. Use "+tagRenderings" or "tagRenderings+" instead to prepend or append some questions.`, + `When overriding a layer, an override is not allowed to override into tagRenderings. Use "+tagRenderings" or "tagRenderings+" instead to prepend or append some questions.` ) } try { const trPlus = json["override"]["tagRenderings+"] if (trPlus) { let index = found.tagRenderings.findIndex( - (tr) => tr["id"] === "leftover-questions", + (tr) => tr["id"] === "leftover-questions" ) if (index < 0) { index = found.tagRenderings.length @@ -95,8 +105,8 @@ class SubstituteLayer extends Conversion 0) { context.err( "This theme specifies that certain tagrenderings have to be removed based on forbidden layers. One or more of these layers did not match any tagRenderings and caused no deletions: " + - unused.join(", ") + - "\n This means that this label can be removed or that the original tagRendering that should be deleted does not have this label anymore", + unused.join(", ") + + "\n This means that this label can be removed or that the original tagRendering that should be deleted does not have this label anymore" ) } found.tagRenderings = filtered @@ -172,7 +182,7 @@ class AddDefaultLayers extends DesugaringStep { super( "Adds the default layers, namely: " + Constants.added_by_default.join(", "), ["layers"], - "AddDefaultLayers", + "AddDefaultLayers" ) this._state = state } @@ -195,10 +205,10 @@ class AddDefaultLayers extends DesugaringStep { if (alreadyLoaded.has(v.id)) { context.warn( "Layout " + - context + - " already has a layer with name " + - v.id + - "; skipping inclusion of this builtin layer", + context + + " already has a layer with name " + + v.id + + "; skipping inclusion of this builtin layer" ) continue } @@ -214,7 +224,7 @@ class AddContextToTranslationsInLayout extends DesugaringStep { super( "Adds context to translations, including the prefix 'themes:json.id'; this is to make sure terms in an 'overrides' or inline layer are linkable too", ["_context"], - "AddContextToTranlationsInLayout", + "AddContextToTranlationsInLayout" ) } @@ -223,7 +233,7 @@ class AddContextToTranslationsInLayout extends DesugaringStep { // The context is used to generate the 'context' in the translation .It _must_ be `json.id` to correctly link into weblate return conversion.convert( json, - ConversionContext.construct([json.id], ["AddContextToTranslation"]), + ConversionContext.construct([json.id], ["AddContextToTranslation"]) ) } } @@ -233,7 +243,7 @@ class ApplyOverrideAll extends DesugaringStep { super( "Applies 'overrideAll' onto every 'layer'. The 'overrideAll'-field is removed afterwards", ["overrideAll", "layers"], - "ApplyOverrideAll", + "ApplyOverrideAll" ) } @@ -262,7 +272,7 @@ class ApplyOverrideAll extends DesugaringStep { layer.tagRenderings = tagRenderingsPlus } else { let index = layer.tagRenderings.findIndex( - (tr) => tr["id"] === "leftover-questions", + (tr) => tr["id"] === "leftover-questions" ) if (index < 0) { index = layer.tagRenderings.length - 1 @@ -285,7 +295,7 @@ class AddDependencyLayersToTheme extends DesugaringStep { super( `If a layer has a dependency on another layer, these layers are added automatically on the theme. (For example: defibrillator depends on 'walls_and_buildings' to snap onto. This layer is added automatically)`, ["layers"], - "AddDependencyLayersToTheme", + "AddDependencyLayersToTheme" ) this._state = state } @@ -294,7 +304,7 @@ class AddDependencyLayersToTheme extends DesugaringStep { alreadyLoaded: LayerConfigJson[], allKnownLayers: Map, themeId: string, - context: ConversionContext, + context: ConversionContext ): { config: LayerConfigJson; reason: string }[] { const dependenciesToAdd: { config: LayerConfigJson; reason: string }[] = [] const loadedLayerIds: Set = new Set(alreadyLoaded.map((l) => l?.id)) @@ -318,7 +328,7 @@ class AddDependencyLayersToTheme extends DesugaringStep { for (const layerConfig of alreadyLoaded) { try { const layerDeps = DependencyCalculator.getLayerDependencies( - new LayerConfig(layerConfig, themeId + "(dependencies)"), + new LayerConfig(layerConfig, themeId + "(dependencies)") ) dependencies.push(...layerDeps) } catch (e) { @@ -337,8 +347,16 @@ class AddDependencyLayersToTheme extends DesugaringStep { // We mark the needed layer as 'mustLoad' const loadedLayer = alreadyLoaded.find((l) => l.id === dependency.neededLayer) loadedLayer.forceLoad = true - if(dependency.checkHasSnapName && !loadedLayer.snapName){ - context.enters("layer dependency").err("Layer "+dependency.neededLayer+" is loaded because "+dependency.reason+"; so it must specify a `snapName`. This is used in the sentence `move this point to snap it to {snapName}`") + if (dependency.checkHasSnapName && !loadedLayer.snapName) { + context + .enters("layer dependency") + .err( + "Layer " + + dependency.neededLayer + + " is loaded because " + + dependency.reason + + "; so it must specify a `snapName`. This is used in the sentence `move this point to snap it to {snapName}`" + ) } } } @@ -362,10 +380,10 @@ class AddDependencyLayersToTheme extends DesugaringStep { if (dep === undefined) { const message = [ "Loading a dependency failed: layer " + - unmetDependency.neededLayer + - " is not found, neither as layer of " + - themeId + - " nor as builtin layer.", + unmetDependency.neededLayer + + " is not found, neither as layer of " + + themeId + + " nor as builtin layer.", reason, "Loaded layers are: " + alreadyLoaded.map((l) => l.id).join(","), ] @@ -381,12 +399,11 @@ class AddDependencyLayersToTheme extends DesugaringStep { }) loadedLayerIds.add(dep.id) unmetDependencies = unmetDependencies.filter( - (d) => d.neededLayer !== unmetDependency.neededLayer, + (d) => d.neededLayer !== unmetDependency.neededLayer ) } } while (unmetDependencies.length > 0) - return dependenciesToAdd } @@ -404,12 +421,12 @@ class AddDependencyLayersToTheme extends DesugaringStep { layers, allKnownLayers, theme.id, - context, + context ) if (dependencies.length > 0) { for (const dependency of dependencies) { context.info( - "Added " + dependency.config.id + " to the theme. " + dependency.reason, + "Added " + dependency.config.id + " to the theme. " + dependency.reason ) } } @@ -457,7 +474,7 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep super( "Generates a warning if a theme uses an unsubstituted layer", ["layers"], - "WarnForUnsubstitutedLayersInTheme", + "WarnForUnsubstitutedLayersInTheme" ) } @@ -469,7 +486,7 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep context .enter("layers") .err( - "No layers are defined. You must define at least one layer to have a valid theme", + "No layers are defined. You must define at least one layer to have a valid theme" ) return json } @@ -493,10 +510,10 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep context.warn( "The theme " + - json.id + - " has an inline layer: " + - layer["id"] + - ". This is discouraged.", + json.id + + " has an inline layer: " + + layer["id"] + + ". This is discouraged." ) } return json @@ -523,13 +540,13 @@ class PostvalidateTheme extends DesugaringStep { } const sameBasedOn = ( json.layers.filter( - (l) => l["_basedOn"] === layer["_basedOn"] && l["id"] !== layer.id, + (l) => l["_basedOn"] === layer["_basedOn"] && l["id"] !== layer.id ) ) const minZoomAll = Math.min(...sameBasedOn.map((sbo) => sbo.minzoom)) const sameNameDetected = sameBasedOn.some( - (same) => JSON.stringify(layer["name"]) === JSON.stringify(same["name"]), + (same) => JSON.stringify(layer["name"]) === JSON.stringify(same["name"]) ) if (!sameNameDetected) { // The name is unique, so it'll won't be confusing @@ -538,12 +555,12 @@ class PostvalidateTheme extends DesugaringStep { if (minZoomAll < layer.minzoom) { context.err( "There are multiple layers based on " + - basedOn + - ". The layer with id " + - layer.id + - " has a minzoom of " + - layer.minzoom + - ", and has a name set. Another similar layer has a lower minzoom. As such, the layer selection might show 'zoom in to see features' even though some of the features are already visible. Set `\"name\": null` for this layer and eventually remove the 'name':null for the other layer.", + basedOn + + ". The layer with id " + + layer.id + + " has a minzoom of " + + layer.minzoom + + ", and has a name set. Another similar layer has a lower minzoom. As such, the layer selection might show 'zoom in to see features' even though some of the features are already visible. Set `\"name\": null` for this layer and eventually remove the 'name':null for the other layer." ) } } @@ -563,17 +580,17 @@ class PostvalidateTheme extends DesugaringStep { const closeLayers = Utils.sortedByLevenshteinDistance( sameAs, json.layers, - (l) => l["id"], + (l) => l["id"] ).map((l) => l["id"]) context .enters("layers", config.id, "filter", "sameAs") .err( "The layer " + - config.id + - " follows the filter state of layer " + - sameAs + - ", but no layer with this name was found.\n\tDid you perhaps mean one of: " + - closeLayers.slice(0, 3).join(", "), + config.id + + " follows the filter state of layer " + + sameAs + + ", but no layer with this name was found.\n\tDid you perhaps mean one of: " + + closeLayers.slice(0, 3).join(", ") ) } } @@ -589,7 +606,7 @@ export class PrepareTheme extends Fuse { state: DesugaringContext, options?: { skipDefaultLayers: false | boolean - }, + } ) { super( "Fully prepares and expands a theme", @@ -610,8 +627,8 @@ export class PrepareTheme extends Fuse { ? new Pass("AddDefaultLayers is disabled due to the set flag") : new AddDefaultLayers(state), new AddDependencyLayersToTheme(state), - // new AddImportLayers(), - new PostvalidateTheme(state), + // new AddImportLayers(), + new PostvalidateTheme(state) ) this.state = state } @@ -626,13 +643,13 @@ export class PrepareTheme extends Fuse { const needsNodeDatabase = result.layers?.some((l: LayerConfigJson) => l.tagRenderings?.some((tr) => ValidationUtils.getSpecialVisualisations(tr)?.some( - (special) => special.needsNodeDatabase, - ), - ), + (special) => special.needsNodeDatabase + ) + ) ) if (needsNodeDatabase) { context.info( - "Setting 'enableNodeDatabase' as this theme uses a special visualisation which needs to keep track of _all_ nodes", + "Setting 'enableNodeDatabase' as this theme uses a special visualisation which needs to keep track of _all_ nodes" ) result.enableNodeDatabase = true } diff --git a/src/Models/ThemeConfig/Conversion/PrevalidateLayer.ts b/src/Models/ThemeConfig/Conversion/PrevalidateLayer.ts index 76b17fdf9..ed1989778 100644 --- a/src/Models/ThemeConfig/Conversion/PrevalidateLayer.ts +++ b/src/Models/ThemeConfig/Conversion/PrevalidateLayer.ts @@ -88,7 +88,7 @@ export class PrevalidateLayer extends DesugaringStep { } } - if(json["doCount"] !== undefined){ + if (json["doCount"] !== undefined) { context.err("Detected 'doCount'. did you mean: isCounted ?") } @@ -145,7 +145,11 @@ export class PrevalidateLayer extends DesugaringStep { } } - if(this._isBuiltin && json.allowMove === undefined && json.source["geoJson"] === undefined) { + if ( + this._isBuiltin && + json.allowMove === undefined && + json.source["geoJson"] === undefined + ) { if (!Constants.priviliged_layers.find((x) => x == json.id)) { context.err("Layer " + json.id + " does not have an explicit 'allowMove'") } diff --git a/src/Models/ThemeConfig/Conversion/Validation.ts b/src/Models/ThemeConfig/Conversion/Validation.ts index dc0b55718..7aa1a5e06 100644 --- a/src/Models/ThemeConfig/Conversion/Validation.ts +++ b/src/Models/ThemeConfig/Conversion/Validation.ts @@ -30,7 +30,7 @@ export class ValidateLanguageCompleteness extends DesugaringStep { super( "Checks that the given object is fully translated in the specified languages", [], - "ValidateLanguageCompleteness", + "ValidateLanguageCompleteness" ) this._languages = languages ?? ["en"] } @@ -44,18 +44,18 @@ export class ValidateLanguageCompleteness extends DesugaringStep { .filter( (t) => t.tr.translations[neededLanguage] === undefined && - t.tr.translations["*"] === undefined, + t.tr.translations["*"] === undefined ) .forEach((missing) => { context .enter(missing.context.split(".")) .err( `The theme ${obj.id} should be translation-complete for ` + - neededLanguage + - ", but it lacks a translation for " + - missing.context + - ".\n\tThe known translation is " + - missing.tr.textFor("en"), + neededLanguage + + ", but it lacks a translation for " + + missing.context + + ".\n\tThe known translation is " + + missing.tr.textFor("en") ) }) } @@ -72,7 +72,7 @@ export class DoesImageExist extends DesugaringStep { constructor( knownImagePaths: Set, checkExistsSync: (path: string) => boolean = undefined, - ignore?: Set, + ignore?: Set ) { super("Checks if an image exists", [], "DoesImageExist") this._ignore = ignore @@ -112,15 +112,15 @@ export class DoesImageExist extends DesugaringStep { if (!this._knownImagePaths.has(image)) { if (this.doesPathExist === undefined) { context.err( - `Image with path ${image} not found or not attributed; it is used in ${context}`, + `Image with path ${image} not found or not attributed; it is used in ${context}` ) } else if (!this.doesPathExist(image)) { context.err( - `Image with path ${image} does not exist.\n Check for typo's and missing directories in the path.`, + `Image with path ${image} does not exist.\n Check for typo's and missing directories in the path.` ) } else { context.err( - `Image with path ${image} is not attributed (but it exists); execute 'npm run query:licenses' to add the license information and/or run 'npm run generate:licenses' to compile all the license info`, + `Image with path ${image} is not attributed (but it exists); execute 'npm run query:licenses' to add the license information and/or run 'npm run generate:licenses' to compile all the license info` ) } } @@ -133,7 +133,7 @@ class OverrideShadowingCheck extends DesugaringStep { super( "Checks that an 'overrideAll' does not override a single override", [], - "OverrideShadowingCheck", + "OverrideShadowingCheck" ) } @@ -183,7 +183,7 @@ class MiscThemeChecks extends DesugaringStep { context .enter("layers") .err( - "The 'layers'-field should be an array, but it is not. Did you pase a layer identifier and forget to add the '[' and ']'?", + "The 'layers'-field should be an array, but it is not. Did you pase a layer identifier and forget to add the '[' and ']'?" ) } if (json.socialImage === "") { @@ -217,23 +217,37 @@ class MiscThemeChecks extends DesugaringStep { context .enter("overideAll") .err( - "'overrideAll' is spelled with _two_ `r`s. You only wrote a single one of them.", + "'overrideAll' is spelled with _two_ `r`s. You only wrote a single one of them." ) } - if (json.defaultBackgroundId - && ![AvailableRasterLayers.osmCartoProperties.id, ...eliCategory ] - .find(l => l === json.defaultBackgroundId) ) { + if ( + json.defaultBackgroundId && + ![AvailableRasterLayers.osmCartoProperties.id, ...eliCategory].find( + (l) => l === json.defaultBackgroundId + ) + ) { const background = json.defaultBackgroundId - const match = AvailableRasterLayers.globalLayers.find(l => l.properties.id === background) + const match = AvailableRasterLayers.globalLayers.find( + (l) => l.properties.id === background + ) if (!match) { - const suggestions = Utils.sortedByLevenshteinDistance(background, - AvailableRasterLayers.globalLayers, l => l.properties.id) - context.enter("defaultBackgroundId") - .warn("The default background layer with id", background, "does not exist or is not a global layer. Perhaps you meant one of:", - suggestions.slice(0, 5).map(l => l.properties.id).join(", "), - "If you want to use a certain category of background image, use", AvailableRasterLayers.globalLayers.join(", ") - ) + const suggestions = Utils.sortedByLevenshteinDistance( + background, + AvailableRasterLayers.globalLayers, + (l) => l.properties.id + ) + context.enter("defaultBackgroundId").warn( + "The default background layer with id", + background, + "does not exist or is not a global layer. Perhaps you meant one of:", + suggestions + .slice(0, 5) + .map((l) => l.properties.id) + .join(", "), + "If you want to use a certain category of background image, use", + AvailableRasterLayers.globalLayers.join(", ") + ) } } return json @@ -245,7 +259,7 @@ export class PrevalidateTheme extends Fuse { super( "Various consistency checks on the raw JSON", new MiscThemeChecks(), - new OverrideShadowingCheck(), + new OverrideShadowingCheck() ) } } @@ -255,7 +269,7 @@ export class DetectConflictingAddExtraTags extends DesugaringStep ["_abc"] */ private static extractCalculatedTagNames( - layerConfig?: LayerConfigJson | { calculatedTags: string[] }, + layerConfig?: LayerConfigJson | { calculatedTags: string[] } ) { return ( layerConfig?.calculatedTags?.map((ct) => { @@ -555,16 +569,16 @@ export class DetectShadowedMappings extends DesugaringStep does have `rel='noopener'` set", [], - "ValidatePossibleLinks", + "ValidatePossibleLinks" ) } @@ -619,21 +633,21 @@ export class ValidatePossibleLinks extends DesugaringStep, - context: ConversionContext, + context: ConversionContext ): string | Record { if (typeof json === "string") { if (this.isTabnabbingProne(json)) { context.err( "The string " + - json + - " has a link targeting `_blank`, but it doesn't have `rel='noopener'` set. This gives rise to reverse tabnapping", + json + + " has a link targeting `_blank`, but it doesn't have `rel='noopener'` set. This gives rise to reverse tabnapping" ) } } else { for (const k in json) { if (this.isTabnabbingProne(json[k])) { context.err( - `The translation for ${k} '${json[k]}' has a link targeting \`_blank\`, but it doesn't have \`rel='noopener'\` set. This gives rise to reverse tabnapping`, + `The translation for ${k} '${json[k]}' has a link targeting \`_blank\`, but it doesn't have \`rel='noopener'\` set. This gives rise to reverse tabnapping` ) } } @@ -651,7 +665,7 @@ export class CheckTranslation extends DesugaringStep { super( "Checks that a translation is valid and internally consistent", ["*"], - "CheckTranslation", + "CheckTranslation" ) this._allowUndefined = allowUndefined } @@ -698,7 +712,7 @@ export class ValidateLayerConfig extends DesugaringStep { isBuiltin: boolean, doesImageExist: DoesImageExist, studioValidations: boolean = false, - skipDefaultLayers: boolean = false, + skipDefaultLayers: boolean = false ) { super("Thin wrapper around 'ValidateLayer", [], "ValidateLayerConfig") this.validator = new ValidateLayer( @@ -706,7 +720,7 @@ export class ValidateLayerConfig extends DesugaringStep { isBuiltin, doesImageExist, studioValidations, - skipDefaultLayers, + skipDefaultLayers ) } @@ -734,7 +748,7 @@ export class ValidatePointRendering extends DesugaringStep { .enters("fields", i) .err( `Invalid filter: ${type} is not a valid textfield type.\n\tTry one of ${Array.from( - Validators.availableTypes, - ).join(",")}`, + Validators.availableTypes + ).join(",")}` ) } } @@ -911,13 +925,13 @@ export class DetectDuplicateFilters extends DesugaringStep<{ super( "Tries to detect layers where a shared filter can be used (or where similar filters occur)", [], - "DetectDuplicateFilters", + "DetectDuplicateFilters" ) } convert( json: { layers: LayerConfigJson[]; themes: ThemeConfigJson[] }, - context: ConversionContext, + context: ConversionContext ): { layers: LayerConfigJson[]; themes: ThemeConfigJson[] } { const { layers, themes } = json const perOsmTag = new Map< @@ -981,7 +995,7 @@ export class DetectDuplicateFilters extends DesugaringStep<{ filter: FilterConfigJson }[] >, - theme?: ThemeConfigJson | undefined, + theme?: ThemeConfigJson | undefined ): void { if (layer.filter === undefined || layer.filter === null) { return @@ -1021,7 +1035,7 @@ export class DetectDuplicatePresets extends DesugaringStep { super( "Detects mappings which have identical (english) names or identical mappings.", ["presets"], - "DetectDuplicatePresets", + "DetectDuplicatePresets" ) } @@ -1032,13 +1046,13 @@ export class DetectDuplicatePresets extends DesugaringStep { if (new Set(enNames).size != enNames.length) { const dups = Utils.Duplicates(enNames) const layersWithDup = json.layers.filter((l) => - l.presets.some((p) => dups.indexOf(p.title.textFor("en")) >= 0), + l.presets.some((p) => dups.indexOf(p.title.textFor("en")) >= 0) ) const layerIds = layersWithDup.map((l) => l.id) context.err( `This theme has multiple presets which are named:${dups}, namely layers ${layerIds.join( - ", ", - )} this is confusing for contributors and is probably the result of reusing the same layer multiple times. Use \`{"override": {"=presets": []}}\` to remove some presets`, + ", " + )} this is confusing for contributors and is probably the result of reusing the same layer multiple times. Use \`{"override": {"=presets": []}}\` to remove some presets` ) } @@ -1053,17 +1067,17 @@ export class DetectDuplicatePresets extends DesugaringStep { Utils.SameObject(presetATags, presetBTags) && Utils.sameList( presetA.preciseInput.snapToLayers, - presetB.preciseInput.snapToLayers, + presetB.preciseInput.snapToLayers ) ) { context.err( `This theme has multiple presets with the same tags: ${presetATags.asHumanString( false, false, - {}, + {} )}, namely the preset '${presets[i].title.textFor("en")}' and '${presets[ j - ].title.textFor("en")}'`, + ].title.textFor("en")}'` ) } } @@ -1088,13 +1102,13 @@ export class ValidateThemeEnsemble extends Conversion< super( "Validates that all themes together are logical, i.e. no duplicate ids exists within (overriden) themes", [], - "ValidateThemeEnsemble", + "ValidateThemeEnsemble" ) } convert( json: ThemeConfig[], - context: ConversionContext, + context: ConversionContext ): Map< string, { @@ -1145,11 +1159,11 @@ export class ValidateThemeEnsemble extends Conversion< context.err( [ "The layer with id '" + - id + - "' is found in multiple themes with different tag definitions:", + id + + "' is found in multiple themes with different tag definitions:", "\t In theme " + oldTheme + ":\t" + oldTags.asHumanString(false, false, {}), "\tIn theme " + theme.id + ":\t" + tags.asHumanString(false, false, {}), - ].join("\n"), + ].join("\n") ) } } diff --git a/src/Models/ThemeConfig/DependencyCalculator.ts b/src/Models/ThemeConfig/DependencyCalculator.ts index 66fa129f5..3f124cbcf 100644 --- a/src/Models/ThemeConfig/DependencyCalculator.ts +++ b/src/Models/ThemeConfig/DependencyCalculator.ts @@ -33,9 +33,20 @@ export default class DependencyCalculator { */ public static getLayerDependencies( layer: LayerConfig - ): { neededLayer: string; reason: string; context?: string; neededBy: string, checkHasSnapName: boolean }[] { - const deps: { neededLayer: string; reason: string; context?: string; neededBy: string, checkHasSnapName: boolean }[] = - [] + ): { + neededLayer: string + reason: string + context?: string + neededBy: string + checkHasSnapName: boolean + }[] { + const deps: { + neededLayer: string + reason: string + context?: string + neededBy: string + checkHasSnapName: boolean + }[] = [] for (let i = 0; layer.presets !== undefined && i < layer.presets.length; i++) { const preset = layer.presets[i] @@ -51,7 +62,7 @@ export default class DependencyCalculator { reason: `preset \`${preset.title.textFor("en")}\` snaps to this layer`, context: `${layer.id}.presets[${i}]`, neededBy: layer.id, - checkHasSnapName: true + checkHasSnapName: true, }) }) } @@ -63,7 +74,7 @@ export default class DependencyCalculator { reason: "a tagrendering needs this layer", context: tr.id, neededBy: layer.id, - checkHasSnapName: false + checkHasSnapName: false, }) } } @@ -99,7 +110,7 @@ export default class DependencyCalculator { "] which calculates the value for " + currentKey, neededBy: layer.id, - checkHasSnapName: false + checkHasSnapName: false, }) return [] diff --git a/src/Models/ThemeConfig/FilterConfig.ts b/src/Models/ThemeConfig/FilterConfig.ts index b44ff6558..62eaa9453 100644 --- a/src/Models/ThemeConfig/FilterConfig.ts +++ b/src/Models/ThemeConfig/FilterConfig.ts @@ -59,20 +59,30 @@ export default class FilterConfig { throw `Invalid filter: no question given at ${ctx}` } - const fields: { name: string; type: ValidatorType }[] = (option.fields ?? []).map((f, i) => { - const type = f.type ?? "regex" - if(Validators.availableTypes.indexOf(type) < 0){ - throw `Invalid filter: type is not a valid validator. Did you mean one of ${Utils.sortedByLevenshteinDistance(type, >Validators.availableTypes, x => x).slice(0, 3)}` + const fields: { name: string; type: ValidatorType }[] = (option.fields ?? []).map( + (f, i) => { + const type = f.type ?? "regex" + if (Validators.availableTypes.indexOf(type) < 0) { + throw `Invalid filter: type is not a valid validator. Did you mean one of ${Utils.sortedByLevenshteinDistance( + type, + >Validators.availableTypes, + (x) => x + ).slice(0, 3)}` + } + // Type is validated against 'ValidatedTextField' in Validation.ts, in ValidateFilterConfig + if ( + f.name === undefined || + f.name === "" || + f.name.match(/[a-z0-9_-]+/) == null + ) { + throw `Invalid filter: a variable name should match [a-z0-9_-]+ at ${ctx}.fields[${i}]` + } + return { + name: f.name, + type, + } } - // Type is validated against 'ValidatedTextField' in Validation.ts, in ValidateFilterConfig - if (f.name === undefined || f.name === "" || f.name.match(/[a-z0-9_-]+/) == null) { - throw `Invalid filter: a variable name should match [a-z0-9_-]+ at ${ctx}.fields[${i}]` - } - return { - name: f.name, - type - } - }) + ) for (const field of fields) { for (const ln in question.translations) { @@ -226,7 +236,7 @@ export default class FilterConfig { opt.osmTags?.asHumanString() ?? "", opt.fields?.length > 0 ? opt.fields.map((f) => f.name + " (" + f.type + ")").join(" ") - : undefined + : undefined, ]) ) }) diff --git a/src/Models/ThemeConfig/LayerConfig.ts b/src/Models/ThemeConfig/LayerConfig.ts index cb02b719d..2f7824cb2 100644 --- a/src/Models/ThemeConfig/LayerConfig.ts +++ b/src/Models/ThemeConfig/LayerConfig.ts @@ -85,12 +85,12 @@ export default class LayerConfig extends WithContextLoader { } this.syncSelection = json.syncSelection ?? "no" - if(!json.source) { - if(json.presets === undefined){ - throw "Error while parsing "+json.id+" in "+context+"; no source given" + if (!json.source) { + if (json.presets === undefined) { + throw "Error while parsing " + json.id + " in " + context + "; no source given" } this.source = new SourceConfig({ - osmTags: TagUtils.Tag({or: json.presets.map(pr => ({and:pr.tags}))}), + osmTags: TagUtils.Tag({ or: json.presets.map((pr) => ({ and: pr.tags })) }), }) } else if (typeof json.source !== "string") { this.maxAgeOfCache = json.source["maxCacheAge"] ?? 24 * 60 * 60 * 30 @@ -104,7 +104,7 @@ export default class LayerConfig extends WithContextLoader { mercatorCrs: json.source["mercatorCrs"], idKey: json.source["idKey"], }, - json.id, + json.id ) } @@ -124,7 +124,7 @@ export default class LayerConfig extends WithContextLoader { if (json.calculatedTags !== undefined) { if (!official) { console.warn( - `Unofficial theme ${this.id} with custom javascript! This is a security risk`, + `Unofficial theme ${this.id} with custom javascript! This is a security risk` ) } this.calculatedTags = [] @@ -194,7 +194,7 @@ export default class LayerConfig extends WithContextLoader { tags: pr.tags.map((t) => TagUtils.SimpleTag(t)), description: Translations.T( pr.description, - `${translationContext}.presets.${i}.description`, + `${translationContext}.presets.${i}.description` ), preciseInput: preciseInput, exampleImages: pr.exampleImages, @@ -208,7 +208,7 @@ export default class LayerConfig extends WithContextLoader { if (json.lineRendering) { this.lineRendering = Utils.NoNull(json.lineRendering).map( - (r, i) => new LineRenderingConfig(r, `${context}[${i}]`), + (r, i) => new LineRenderingConfig(r, `${context}[${i}]`) ) } else { this.lineRendering = [] @@ -216,7 +216,7 @@ export default class LayerConfig extends WithContextLoader { if (json.pointRendering) { this.mapRendering = Utils.NoNull(json.pointRendering).map( - (r, i) => new PointRenderingConfig(r, `${context}[${i}](${this.id})`), + (r, i) => new PointRenderingConfig(r, `${context}[${i}](${this.id})`) ) } else { this.mapRendering = [] @@ -228,7 +228,7 @@ export default class LayerConfig extends WithContextLoader { r.location.has("centroid") || r.location.has("projected_centerpoint") || r.location.has("start") || - r.location.has("end"), + r.location.has("end") ) if ( @@ -250,7 +250,7 @@ export default class LayerConfig extends WithContextLoader { Constants.priviliged_layers.indexOf(this.id) < 0 && this.source !== null /*library layer*/ && !this.source?.geojsonSource?.startsWith( - "https://api.openstreetmap.org/api/0.6/notes.json", + "https://api.openstreetmap.org/api/0.6/notes.json" ) ) { throw ( @@ -269,7 +269,7 @@ export default class LayerConfig extends WithContextLoader { typeof tr !== "string" && tr["builtin"] === undefined && tr["id"] === undefined && - tr["rewrite"] === undefined, + tr["rewrite"] === undefined ) ?? [] if (missingIds?.length > 0 && official) { console.error("Some tagRenderings of", this.id, "are missing an id:", missingIds) @@ -280,8 +280,8 @@ export default class LayerConfig extends WithContextLoader { (tr, i) => new TagRenderingConfig( tr, - this.id + ".tagRenderings[" + i + "]", - ), + this.id + ".tagRenderings[" + i + "]" + ) ) if (json.units !== undefined && !Array.isArray(json.units)) { throw ( @@ -291,7 +291,7 @@ export default class LayerConfig extends WithContextLoader { ) } this.units = (json.units ?? []).flatMap((unitJson, i) => - Unit.fromJson(unitJson, this.tagRenderings, `${context}.unit[${i}]`), + Unit.fromJson(unitJson, this.tagRenderings, `${context}.unit[${i}]`) ) if ( @@ -362,14 +362,18 @@ export default class LayerConfig extends WithContextLoader { if (mapRenderings.length === 0) { return undefined } - return new Combine(mapRenderings.map( - mr => mr.GetBaseIcon(properties ?? this.GetBaseTags()).SetClass("absolute left-0 top-0 w-full h-full")) + return new Combine( + mapRenderings.map((mr) => + mr + .GetBaseIcon(properties ?? this.GetBaseTags()) + .SetClass("absolute left-0 top-0 w-full h-full") + ) ).SetClass("relative block w-full h-full") } public GetBaseTags(): Record { return TagUtils.changeAsProperties( - this.source?.osmTags?.asChange({ id: "node/-1" }) ?? [{ k: "id", v: "node/-1" }], + this.source?.osmTags?.asChange({ id: "node/-1" }) ?? [{ k: "id", v: "node/-1" }] ) } @@ -382,7 +386,7 @@ export default class LayerConfig extends WithContextLoader { neededLayer: string }[] = [], addedByDefault = false, - canBeIncluded = true, + canBeIncluded = true ): string { const extraProps: string[] = [] extraProps.push("This layer is shown at zoomlevel **" + this.minzoom + "** and higher") @@ -390,32 +394,32 @@ export default class LayerConfig extends WithContextLoader { if (canBeIncluded) { if (addedByDefault) { extraProps.push( - "**This layer is included automatically in every theme. This layer might contain no points**", + "**This layer is included automatically in every theme. This layer might contain no points**" ) } if (this.shownByDefault === false) { extraProps.push( - "This layer is not visible by default and must be enabled in the filter by the user. ", + "This layer is not visible by default and must be enabled in the filter by the user. " ) } if (this.title === undefined) { extraProps.push( - "Elements don't have a title set and cannot be toggled nor will they show up in the dashboard. If you import this layer in your theme, override `title` to make this toggleable.", + "Elements don't have a title set and cannot be toggled nor will they show up in the dashboard. If you import this layer in your theme, override `title` to make this toggleable." ) } if (this.name === undefined && this.shownByDefault === false) { extraProps.push( - "This layer is not visible by default and the visibility cannot be toggled, effectively resulting in a fully hidden layer. This can be useful, e.g. to calculate some metatags. If you want to render this layer (e.g. for debugging), enable it by setting the URL-parameter layer-=true", + "This layer is not visible by default and the visibility cannot be toggled, effectively resulting in a fully hidden layer. This can be useful, e.g. to calculate some metatags. If you want to render this layer (e.g. for debugging), enable it by setting the URL-parameter layer-=true" ) } if (this.name === undefined) { extraProps.push( - "Not visible in the layer selection by default. If you want to make this layer toggable, override `name`", + "Not visible in the layer selection by default. If you want to make this layer toggable, override `name`" ) } if (this.mapRendering.length === 0) { extraProps.push( - "Not rendered on the map by default. If you want to rendering this on the map, override `mapRenderings`", + "Not rendered on the map by default. If you want to rendering this on the map, override `mapRenderings`" ) } @@ -425,12 +429,12 @@ export default class LayerConfig extends WithContextLoader { "", "This layer is loaded from an external source, namely ", "`" + this.source.geojsonSource + "`", - ].join("\n\n"), + ].join("\n\n") ) } } else { extraProps.push( - "This layer can **not** be included in a theme. It is solely used by [special renderings](SpecialRenderings.md) showing a minimap with custom data.", + "This layer can **not** be included in a theme. It is solely used by [special renderings](SpecialRenderings.md) showing a minimap with custom data." ) } @@ -440,7 +444,7 @@ export default class LayerConfig extends WithContextLoader { usingLayer = [ "## Themes using this layer", MarkdownUtils.list( - (usedInThemes ?? []).map((id) => `[${id}](https://mapcomplete.org/${id})`), + (usedInThemes ?? []).map((id) => `[${id}](https://mapcomplete.org/${id})`) ), ] } else if (this.source !== null) { @@ -456,31 +460,43 @@ export default class LayerConfig extends WithContextLoader { " into the layout as it depends on it: ", dep.reason, "(" + dep.context + ")", - ].join(" "), + ].join(" ") ) } let presets: string[] = [] if (this.presets.length > 0) { - presets = [ "## Presets", "The following options to create new points are included:", - MarkdownUtils.list(this.presets.map(preset => { - let snaps = "" - if (preset.preciseInput?.snapToLayers) { - snaps = " (snaps to layers " + preset.preciseInput.snapToLayers.map(id => `\`${id}\``).join(", ") + ")" - } - return "**" + preset.title.txt + "** which has the following tags:" + new And(preset.tags).asHumanString(true) + snaps - })), + MarkdownUtils.list( + this.presets.map((preset) => { + let snaps = "" + if (preset.preciseInput?.snapToLayers) { + snaps = + " (snaps to layers " + + preset.preciseInput.snapToLayers + .map((id) => `\`${id}\``) + .join(", ") + + ")" + } + return ( + "**" + + preset.title.txt + + "** which has the following tags:" + + new And(preset.tags).asHumanString(true) + + snaps + ) + }) + ), ] } for (const revDep of Utils.Dedup(layerIsNeededBy?.get(this.id) ?? [])) { extraProps.push( ["This layer is needed as dependency for layer", `[${revDep}](#${revDep})`].join( - " ", - ), + " " + ) ) } @@ -491,10 +507,10 @@ export default class LayerConfig extends WithContextLoader { .filter((values) => values.key !== "id") .map((values) => { const embedded: string[] = values.values?.map((v) => - Link.OsmWiki(values.key, v, true).SetClass("mr-2").AsMarkdown(), + Link.OsmWiki(values.key, v, true).SetClass("mr-2").AsMarkdown() ) ?? ["_no preset options defined, or no values in them_"] const statistics = `https://taghistory.raifer.tech/?#***/${encodeURIComponent( - values.key, + values.key )}/` const tagInfo = `https://taginfo.openstreetmap.org/keys/${values.key}#values` return [ @@ -509,7 +525,7 @@ export default class LayerConfig extends WithContextLoader { : `[${values.type}](../SpecialInputElements.md#${values.type})`, embedded.join(" "), ] - }), + }) ) let quickOverview: string[] = [] @@ -519,7 +535,7 @@ export default class LayerConfig extends WithContextLoader { "this quick overview is incomplete", MarkdownUtils.table( ["attribute", "type", "values which are supported by this layer"], - tableRows, + tableRows ), ] } @@ -553,19 +569,19 @@ export default class LayerConfig extends WithContextLoader { const parts = neededTags["and"] tagsDescription.push( "Elements must match **all** of the following expressions:", - parts.map((p, i) => i + ". " + p.asHumanString(true, false, {})).join("\n"), + parts.map((p, i) => i + ". " + p.asHumanString(true, false, {})).join("\n") ) } else if (neededTags["or"]) { const parts = neededTags["or"] tagsDescription.push( "Elements must match **any** of the following expressions:", - parts.map((p) => " - " + p.asHumanString(true, false, {})).join("\n"), + parts.map((p) => " - " + p.asHumanString(true, false, {})).join("\n") ) } else { tagsDescription.push( "Elements must match the expression **" + - neededTags.asHumanString(true, false, {}) + - "**", + neededTags.asHumanString(true, false, {}) + + "**" ) } @@ -616,8 +632,7 @@ export default class LayerConfig extends WithContextLoader { if (!presets) { return undefined } - const matchingPresets = presets - .filter((pr) => new And(pr.tags).matchesProperties(tags)) + const matchingPresets = presets.filter((pr) => new And(pr.tags).matchesProperties(tags)) let mostShadowed = matchingPresets[0] let mostShadowedTags = new And(mostShadowed.tags) for (let i = 1; i < matchingPresets.length; i++) { @@ -646,18 +661,18 @@ export default class LayerConfig extends WithContextLoader { * Indicates if this is a normal layer, meaning that it can be toggled by the user in normal circumstances * Thus: name is set, not a note import layer, not synced with another filter, ... */ - public isNormal(){ - if(this.id.startsWith("note_import")){ + public isNormal() { + if (this.id.startsWith("note_import")) { return false } - if(Constants.added_by_default.indexOf( this.id) >=0){ + if (Constants.added_by_default.indexOf(this.id) >= 0) { return false } - if(this.filterIsSameAs !== undefined){ + if (this.filterIsSameAs !== undefined) { return false } - if(!this.name ){ + if (!this.name) { return false } return true diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index 153abe36c..483cab3da 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -85,7 +85,7 @@ export default class TagRenderingConfig { | string | TagRenderingConfigJson | (QuestionableTagRenderingConfigJson & { questionHintIsMd?: boolean }), - context?: string, + context?: string ) { let json = config if (json === undefined) { @@ -144,7 +144,7 @@ export default class TagRenderingConfig { this.description = Translations.T(json.description, translationKey + ".description") this.editButtonAriaLabel = Translations.T( json.editButtonAriaLabel, - translationKey + ".editButtonAriaLabel", + translationKey + ".editButtonAriaLabel" ) this.condition = TagUtils.Tag(json.condition ?? { and: [] }, `${context}.condition`) @@ -160,7 +160,7 @@ export default class TagRenderingConfig { } this.metacondition = TagUtils.Tag( json.metacondition ?? { and: [] }, - `${context}.metacondition`, + `${context}.metacondition` ) if (json.freeform) { if ( @@ -178,7 +178,7 @@ export default class TagRenderingConfig { }, perhaps you meant ${Utils.sortedByLevenshteinDistance( json.freeform.key, Validators.availableTypes, - (s) => s, + (s) => s )}` } const type: ValidatorType = json.freeform.type ?? "string" @@ -200,7 +200,7 @@ export default class TagRenderingConfig { placeholder, addExtraTags: json.freeform.addExtraTags?.map((tg, i) => - TagUtils.ParseUploadableTag(tg, `${context}.extratag[${i}]`), + TagUtils.ParseUploadableTag(tg, `${context}.extratag[${i}]`) ) ?? [], inline: json.freeform.inline ?? false, default: json.freeform.default, @@ -266,8 +266,8 @@ export default class TagRenderingConfig { context, this.multiAnswer, this.question !== undefined, - commonIconSize, - ), + commonIconSize + ) ) } else { this.mappings = [] @@ -293,7 +293,7 @@ export default class TagRenderingConfig { for (const expectedKey of keys) { if (usedKeys.indexOf(expectedKey) < 0) { const msg = `${context}.mappings[${i}]: This mapping only defines values for ${usedKeys.join( - ", ", + ", " )}, but it should also give a value for ${expectedKey}` this.configuration_warnings.push(msg) } @@ -340,7 +340,7 @@ export default class TagRenderingConfig { context: string, multiAnswer?: boolean, isQuestionable?: boolean, - commonSize: string = "small", + commonSize: string = "small" ): Mapping { const ctx = `${translationKey}.mappings.${i}` if (mapping.if === undefined) { @@ -349,7 +349,7 @@ export default class TagRenderingConfig { if (mapping.then === undefined) { if (mapping["render"] !== undefined) { throw `${ctx}: Invalid mapping: no 'then'-clause found. You might have typed 'render' instead of 'then', change it in ${JSON.stringify( - mapping, + mapping )}` } throw `${ctx}: Invalid mapping: no 'then'-clause found in ${JSON.stringify(mapping)}` @@ -360,7 +360,7 @@ export default class TagRenderingConfig { if (mapping["render"] !== undefined) { throw `${ctx}: Invalid mapping: a 'render'-key is present, this is probably a bug: ${JSON.stringify( - mapping, + mapping )}` } if (typeof mapping.if !== "string" && mapping.if["length"] !== undefined) { @@ -383,11 +383,11 @@ export default class TagRenderingConfig { } else if (mapping.hideInAnswer !== undefined) { hideInAnswer = TagUtils.Tag( mapping.hideInAnswer, - `${context}.mapping[${i}].hideInAnswer`, + `${context}.mapping[${i}].hideInAnswer` ) } const addExtraTags = (mapping.addExtraTags ?? []).map((str, j) => - TagUtils.SimpleTag(str, `${ctx}.addExtraTags[${j}]`), + TagUtils.SimpleTag(str, `${ctx}.addExtraTags[${j}]`) ) if (hideInAnswer === true && addExtraTags.length > 0) { throw `${ctx}: Invalid mapping: 'hideInAnswer' is set to 'true', but 'addExtraTags' is enabled as well. This means that extra tags will be applied if this mapping is chosen as answer, but it cannot be chosen as answer. This either indicates a thought error or obsolete code that must be removed.` @@ -483,7 +483,7 @@ export default class TagRenderingConfig { * @constructor */ public GetRenderValues( - tags: Record, + tags: Record ): { then: Translation; icon?: string; iconClass?: string }[] { if (!this.multiAnswer) { return [this.GetRenderValueWithImage(tags)] @@ -506,7 +506,7 @@ export default class TagRenderingConfig { return mapping } return undefined - }), + }) ) if (freeformKeyDefined && tags[this.freeform.key] !== undefined) { @@ -514,7 +514,7 @@ export default class TagRenderingConfig { applicableMappings ?.flatMap((m) => m.if?.usedTags() ?? []) ?.filter((kv) => kv.key === this.freeform.key) - ?.map((kv) => kv.value), + ?.map((kv) => kv.value) ) const freeformValues = tags[this.freeform.key].split(";") @@ -523,7 +523,7 @@ export default class TagRenderingConfig { applicableMappings.push({ then: new TypedTranslation( this.render.replace("{" + this.freeform.key + "}", leftover).translations, - this.render.context, + this.render.context ), }) } @@ -541,7 +541,7 @@ export default class TagRenderingConfig { * @constructor */ public GetRenderValueWithImage( - tags: Record, + tags: Record ): { then: TypedTranslation; icon?: string; iconClass?: string } | undefined { if (this.condition !== undefined) { if (!this.condition.matchesProperties(tags)) { @@ -610,7 +610,7 @@ export default class TagRenderingConfig { const answerMappings = this.mappings?.filter((m) => m.hideInAnswer !== true) if (key === undefined) { const values: { k: string; v: string }[][] = Utils.NoNull( - answerMappings?.map((m) => m.if.asChange({})) ?? [], + answerMappings?.map((m) => m.if.asChange({})) ?? [] ) if (values.length === 0) { return @@ -628,15 +628,15 @@ export default class TagRenderingConfig { return { key: commonKey, values: Utils.NoNull( - values.map((arr) => arr.filter((item) => item.k === commonKey)[0]?.v), + values.map((arr) => arr.filter((item) => item.k === commonKey)[0]?.v) ), } } let values = Utils.NoNull( answerMappings?.map( - (m) => m.if.asChange({}).filter((item) => item.k === key)[0]?.v, - ) ?? [], + (m) => m.if.asChange({}).filter((item) => item.k === key)[0]?.v + ) ?? [] ) if (values.length === undefined) { values = undefined @@ -700,7 +700,7 @@ export default class TagRenderingConfig { freeformValue: string | undefined, singleSelectedMapping: number, multiSelectedMapping: boolean[] | undefined, - currentProperties: Record, + currentProperties: Record ): UploadableTag { if (typeof freeformValue === "string") { freeformValue = freeformValue?.trim() @@ -775,7 +775,7 @@ export default class TagRenderingConfig { new And([ new Tag(this.freeform.key, freeformValue), ...(this.freeform.addExtraTags ?? []), - ]), + ]) ) } const and = TagUtils.FlattenMultiAnswer([...selectedMappings, ...unselectedMappings]) @@ -845,11 +845,11 @@ export default class TagRenderingConfig { } const msgs: string[] = [ icon + - " " + - "*" + - m.then.textFor(lang) + - "* is shown if with " + - m.if.asHumanString(true, false, {}), + " " + + "*" + + m.then.textFor(lang) + + "* is shown if with " + + m.if.asHumanString(true, false, {}), ] if (m.hideInAnswer === true) { @@ -858,11 +858,11 @@ export default class TagRenderingConfig { if (m.ifnot !== undefined) { msgs.push( "Unselecting this answer will add " + - m.ifnot.asHumanString(true, false, {}), + m.ifnot.asHumanString(true, false, {}) ) } return msgs.join(". ") - }), + }) ) } @@ -871,7 +871,7 @@ export default class TagRenderingConfig { const conditionAsLink = (this.condition.optimize()).asHumanString( true, false, - {}, + {} ) condition = "This tagrendering is only visible in the popup if the following condition is met: " + @@ -905,7 +905,7 @@ export default class TagRenderingConfig { this.metacondition, this.condition, this.freeform?.key ? new RegexTag(this.freeform?.key, /.*/) : undefined, - this.invalidValues, + this.invalidValues ) for (const m of this.mappings ?? []) { tags.push(m.if) @@ -925,21 +925,26 @@ export default class TagRenderingConfig { * The keys that should be erased if one has to revert to 'unknown'. * Might give undefined if setting to unknown is not possible */ - public removeToSetUnknown(partOfLayer: LayerConfig, currentTags: Record): string[] | undefined { + public removeToSetUnknown( + partOfLayer: LayerConfig, + currentTags: Record + ): string[] | undefined { if (!partOfLayer?.source || !currentTags) { return } const toDelete = new Set() if (this.freeform) { toDelete.add(this.freeform.key) - const extraTags = new And(this.freeform.addExtraTags ?? []).usedKeys().filter(k => k !== "fixme") + const extraTags = new And(this.freeform.addExtraTags ?? []) + .usedKeys() + .filter((k) => k !== "fixme") if (extraTags.length > 0) { return undefined } } if (this.mappings?.length > 0) { const mainkey = this.mappings[0].if.usedKeys() - mainkey.forEach(k => toDelete.add(k)) + mainkey.forEach((k) => toDelete.add(k)) for (const mapping of this.mappings) { if (mapping.addExtraTags?.length > 0) { return undefined @@ -953,7 +958,6 @@ export default class TagRenderingConfig { } } - currentTags = { ...currentTags } for (const key of toDelete) { delete currentTags[key] @@ -971,7 +975,7 @@ export class TagRenderingConfigUtils { public static withNameSuggestionIndex( config: TagRenderingConfig, tags: UIEventSource>, - feature?: Feature, + feature?: Feature ): Store { const isNSI = NameSuggestionIndex.supportedTypes().indexOf(config.freeform?.key) >= 0 if (!isNSI) { @@ -989,8 +993,8 @@ export class TagRenderingConfigUtils { tags, country.split(";"), center, - { sortByFrequency: true }, - ), + { sortByFrequency: true } + ) ) }) return extraMappings.map((extraMappings) => { @@ -1000,19 +1004,17 @@ export class TagRenderingConfigUtils { const clone: TagRenderingConfig = Object.create(config) // The original mappings get "priorityIf" set const oldMappingsCloned = - clone.mappings?.map( - (m) => { - const mapping = { - ...m, - priorityIf: m.priorityIf ?? TagUtils.Tag("id~*"), - } - if (m.if.usedKeys().indexOf("nobrand") < 0) { - // Erase 'nobrand=yes', unless this option explicitly sets it - mapping["addExtraTags"] = [new Tag("nobrand", "")] - } - return mapping - }, - ) ?? [] + clone.mappings?.map((m) => { + const mapping = { + ...m, + priorityIf: m.priorityIf ?? TagUtils.Tag("id~*"), + } + if (m.if.usedKeys().indexOf("nobrand") < 0) { + // Erase 'nobrand=yes', unless this option explicitly sets it + mapping["addExtraTags"] = [new Tag("nobrand", "")] + } + return mapping + }) ?? [] clone.mappings = [...oldMappingsCloned, ...extraMappings] return clone }) diff --git a/src/Models/ThemeConfig/ThemeConfig.ts b/src/Models/ThemeConfig/ThemeConfig.ts index b60375e12..9917007ea 100644 --- a/src/Models/ThemeConfig/ThemeConfig.ts +++ b/src/Models/ThemeConfig/ThemeConfig.ts @@ -38,7 +38,6 @@ export class ThemeInformation { keywords?: (Translatable | Translation)[] } - export default class ThemeConfig implements ThemeInformation { public static readonly defaultSocialImage = "assets/SocialImage.png" public readonly id: string @@ -193,7 +192,7 @@ export default class ThemeConfig implements ThemeInformation { icon: "./assets/svg/pop-out.svg", href: "https://{basepath}/{theme}.html?lat={lat}&lon={lon}&z={zoom}&language={language}", newTab: true, - requirements: ["iframe", "no-welcome-message"] + requirements: ["iframe", "no-welcome-message"], }, context + ".extraLink" ) @@ -292,9 +291,7 @@ export default class ThemeConfig implements ThemeInformation { if (!untranslated.has(ln)) { untranslated.set(ln, []) } - untranslated - .get(ln) - .push(translation.context) + untranslated.get(ln).push(translation.context) } }) }, @@ -330,7 +327,12 @@ export default class ThemeConfig implements ThemeInformation { } } } - console.trace("Fallthrough: could not find the appropriate layer for an object with tags", tags, "within layout", this) + console.trace( + "Fallthrough: could not find the appropriate layer for an object with tags", + tags, + "within layout", + this + ) return undefined } @@ -342,7 +344,7 @@ export default class ThemeConfig implements ThemeInformation { // The 'favourite'-layer contains pretty much all images as it bundles all layers, so we exclude it const jsonNoFavourites = { ...json, - layers: json.layers.filter((l) => l["id"] !== "favourite") + layers: json.layers.filter((l) => l["id"] !== "favourite"), } const usedImages = jsonNoFavourites._usedImages usedImages.sort() diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index 839b23b57..6a45af1e3 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -2,7 +2,11 @@ import ThemeConfig from "./ThemeConfig/ThemeConfig" import { SpecialVisualizationState } from "../UI/SpecialVisualization" import { Changes } from "../Logic/Osm/Changes" import { Store, UIEventSource } from "../Logic/UIEventSource" -import { FeatureSource, IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource" +import { + FeatureSource, + IndexedFeatureSource, + WritableFeatureSource, +} from "../Logic/FeatureSource/FeatureSource" import { OsmConnection } from "../Logic/Osm/OsmConnection" import { ExportableMap, MapProperties } from "./MapProperties" import LayerState from "../Logic/State/LayerState" @@ -46,7 +50,9 @@ import BackgroundLayerResetter from "../Logic/Actors/BackgroundLayerResetter" import SaveFeatureSourceToLocalStorage from "../Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage" import BBoxFeatureSource from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor" -import NoElementsInViewDetector, { FeatureViewState } from "../Logic/Actors/NoElementsInViewDetector" +import NoElementsInViewDetector, { + FeatureViewState, +} from "../Logic/Actors/NoElementsInViewDetector" import FilteredLayer from "./FilteredLayer" import { PreferredRasterLayerSelector } from "../Logic/Actors/PreferredRasterLayerSelector" import { ImageUploadManager } from "../Logic/ImageProviders/ImageUploadManager" @@ -166,7 +172,7 @@ export default class ThemeViewState implements SpecialVisualizationState { this.featureSwitches = new FeatureSwitchState(layout) this.guistate = new MenuState( this.featureSwitches.featureSwitchWelcomeMessage.data, - layout.id, + layout.id ) this.map = new UIEventSource(undefined) const geolocationState = new GeoLocationState() @@ -182,14 +188,14 @@ export default class ThemeViewState implements SpecialVisualizationState { oauth_token: QueryParameters.GetQueryParameter( "oauth_token", undefined, - "Used to complete the login", + "Used to complete the login" ), }) this.userRelatedState = new UserRelatedState( this.osmConnection, layout, this.featureSwitches, - this.mapProperties, + this.mapProperties ) this.userRelatedState.fixateNorth.addCallbackAndRunD((fixated) => { this.mapProperties.allowRotating.setData(fixated !== "yes") @@ -200,20 +206,20 @@ export default class ThemeViewState implements SpecialVisualizationState { geolocationState, this.selectedElement, this.mapProperties, - this.userRelatedState.gpsLocationHistoryRetentionTime, + this.userRelatedState.gpsLocationHistoryRetentionTime ) this.geolocationControl = new GeolocationControlState(this.geolocation, this.mapProperties) this.availableLayers = AvailableRasterLayers.layersAvailableAt( this.mapProperties.location, - this.osmConnection.isLoggedIn, + this.osmConnection.isLoggedIn ) this.layerState = new LayerState( this.osmConnection, layout.layers, layout.id, - this.featureSwitches.featureSwitchLayerDefault, + this.featureSwitches.featureSwitchLayerDefault ) { @@ -222,7 +228,7 @@ export default class ThemeViewState implements SpecialVisualizationState { const isDisplayed = QueryParameters.GetBooleanQueryParameter( "overlay-" + rasterInfo.id, rasterInfo.defaultState ?? true, - "Whether or not overlay layer " + rasterInfo.id + " is shown", + "Whether or not overlay layer " + rasterInfo.id + " is shown" ) const state = { isDisplayed } overlayLayerStates.set(rasterInfo.id, state) @@ -247,7 +253,7 @@ export default class ThemeViewState implements SpecialVisualizationState { this.osmConnection.Backend(), (id) => this.layerState.filteredLayers.get(id).isDisplayed, mvtAvailableLayers, - this.fullNodeDatabase, + this.fullNodeDatabase ) let currentViewIndex = 0 @@ -265,7 +271,7 @@ export default class ThemeViewState implements SpecialVisualizationState { id: "current_view_" + currentViewIndex, }), ] - }), + }) ) this.featuresInView = new BBoxFeatureSource(layoutSource, this.mapProperties.bounds) @@ -276,19 +282,19 @@ export default class ThemeViewState implements SpecialVisualizationState { this.changes = new Changes( this, layout?.isLeftRightSensitive() ?? false, - (e, extraMsg) => this.reportError(e, extraMsg), + (e, extraMsg) => this.reportError(e, extraMsg) ) this.historicalUserLocations = this.geolocation.historicalUserLocations this.newFeatures = new NewGeometryFromChangesFeatureSource( this.changes, layoutSource, - this.featureProperties, + this.featureProperties ) layoutSource.addSource(this.newFeatures) const perLayer = new PerLayerFeatureSourceSplitter( Array.from(this.layerState.filteredLayers.values()).filter( - (l) => l.layerDef?.source !== null, + (l) => l.layerDef?.source !== null ), new ChangeGeometryApplicator(this.indexedFeatures, this.changes), { @@ -299,10 +305,10 @@ export default class ThemeViewState implements SpecialVisualizationState { "Got ", features.length, "leftover features, such as", - features[0].properties, + features[0].properties ) }, - }, + } ) this.perLayer = perLayer.perLayer } @@ -342,12 +348,12 @@ export default class ThemeViewState implements SpecialVisualizationState { this.lastClickObject = new LastClickFeatureSource( this.theme, this.mapProperties.lastClickLocation, - this.userRelatedState.addNewFeatureMode, + this.userRelatedState.addNewFeatureMode ) this.osmObjectDownloader = new OsmObjectDownloader( this.osmConnection.Backend(), - this.changes, + this.changes ) this.perLayerFiltered = this.showNormalDataOn(this.map) @@ -357,8 +363,11 @@ export default class ThemeViewState implements SpecialVisualizationState { { currentZoom: this.mapProperties.zoom, layerState: this.layerState, - bounds: this.visualFeedbackViewportBounds.map(bounds => bounds ?? this.mapProperties.bounds?.data, [this.mapProperties.bounds]), - }, + bounds: this.visualFeedbackViewportBounds.map( + (bounds) => bounds ?? this.mapProperties.bounds?.data, + [this.mapProperties.bounds] + ), + } ) this.featureSummary = this.setupSummaryLayer() this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView @@ -370,7 +379,7 @@ export default class ThemeViewState implements SpecialVisualizationState { this.changes, this.geolocation.geolocationState.currentGPSLocation, this.indexedFeatures, - this.reportError, + this.reportError ) this.favourites = new FavouritesFeatureSource(this) const longAgo = new Date() @@ -417,7 +426,7 @@ export default class ThemeViewState implements SpecialVisualizationState { ThemeSource.fromCacheZoomLevel, fs, this.featureProperties, - fs.layer.layerDef.maxAgeOfCache, + fs.layer.layerDef.maxAgeOfCache ) toLocalStorage.set(layerId, storage) }) @@ -430,7 +439,7 @@ export default class ThemeViewState implements SpecialVisualizationState { const doShowLayer = this.mapProperties.zoom.map( (z) => (fs.layer.isDisplayed?.data ?? true) && z >= (fs.layer.layerDef?.minzoom ?? 0), - [fs.layer.isDisplayed], + [fs.layer.isDisplayed] ) if (!doShowLayer.data && this.featureSwitches.featureSwitchFilter.data === false) { @@ -447,7 +456,7 @@ export default class ThemeViewState implements SpecialVisualizationState { fs.layer, fs, (id) => this.featureProperties.getStore(id), - this.layerState.globalFilters, + this.layerState.globalFilters ) filteringFeatureSource.set(layerName, filtered) @@ -579,7 +588,6 @@ export default class ThemeViewState implements SpecialVisualizationState { this.guistate.pageStates.favourites.set(true) }) - Hotkeys.RegisterHotkey( { nomod: " ", @@ -593,11 +601,14 @@ export default class ThemeViewState implements SpecialVisualizationState { if (this.guistate.isSomethingOpen() || this.previewedImage.data !== undefined) { return } - if (document.activeElement.tagName === "button" || document.activeElement.tagName === "input") { + if ( + document.activeElement.tagName === "button" || + document.activeElement.tagName === "input" + ) { return } this.selectClosestAtCenter(0) - }, + } ) for (let i = 1; i < 9; i++) { @@ -615,15 +626,18 @@ export default class ThemeViewState implements SpecialVisualizationState { onUp: true, }, doc, - () => this.selectClosestAtCenter(i - 1), + () => this.selectClosestAtCenter(i - 1) ) } - - Hotkeys.RegisterHotkey({ ctrl: "F" }, Translations.t.hotkeyDocumentation.selectSearch, () => { - this.searchState.feedback.set(undefined) - this.searchState.searchIsFocused.set(true) - }) + Hotkeys.RegisterHotkey( + { ctrl: "F" }, + Translations.t.hotkeyDocumentation.selectSearch, + () => { + this.searchState.feedback.set(undefined) + this.searchState.searchIsFocused.set(true) + } + ) this.featureSwitches.featureSwitchBackgroundSelection.addCallbackAndRun((enable) => { if (!enable) { @@ -638,7 +652,7 @@ export default class ThemeViewState implements SpecialVisualizationState { if (this.featureSwitches.featureSwitchBackgroundSelection.data) { this.guistate.pageStates.background.setData(true) } - }, + } ) Hotkeys.RegisterHotkey( { @@ -649,7 +663,7 @@ export default class ThemeViewState implements SpecialVisualizationState { if (this.featureSwitches.featureSwitchFilter.data) { this.guistate.openFilterView() } - }, + } ) const setLayerCategory = (category: EliCategory, skipLayers: number = 0) => { const timeOfCall = new Date() @@ -664,7 +678,7 @@ export default class ThemeViewState implements SpecialVisualizationState { available, category, current.data, - skipLayers, + skipLayers ) if (!best) { return @@ -677,43 +691,43 @@ export default class ThemeViewState implements SpecialVisualizationState { Hotkeys.RegisterHotkey( { nomod: "O" }, Translations.t.hotkeyDocumentation.selectOsmbasedmap, - () => setLayerCategory("osmbasedmap"), + () => setLayerCategory("osmbasedmap") ) Hotkeys.RegisterHotkey( { nomod: "M" }, Translations.t.hotkeyDocumentation.selectMap, - () => setLayerCategory("map"), + () => setLayerCategory("map") ) Hotkeys.RegisterHotkey( { nomod: "P" }, Translations.t.hotkeyDocumentation.selectAerial, - () => setLayerCategory("photo"), + () => setLayerCategory("photo") ) Hotkeys.RegisterHotkey( { shift: "O" }, Translations.t.hotkeyDocumentation.selectOsmbasedmap, - () => setLayerCategory("osmbasedmap", 2), + () => setLayerCategory("osmbasedmap", 2) ) Hotkeys.RegisterHotkey( { shift: "M" }, Translations.t.hotkeyDocumentation.selectMap, - () => setLayerCategory("map", 2), + () => setLayerCategory("map", 2) ) Hotkeys.RegisterHotkey( { shift: "P" }, Translations.t.hotkeyDocumentation.selectAerial, - () => setLayerCategory("photo", 2), + () => setLayerCategory("photo", 2) ) Hotkeys.RegisterHotkey( { nomod: "L" }, Translations.t.hotkeyDocumentation.geolocate, () => { this.geolocationControl.handleClick() - }, + } ) return true }) @@ -730,7 +744,7 @@ export default class ThemeViewState implements SpecialVisualizationState { } else { tm.setData("false") } - }, + } ) } @@ -738,7 +752,7 @@ export default class ThemeViewState implements SpecialVisualizationState { /** * MaxZoom for the summary layer */ - const normalLayers = this.theme.layers.filter(l => l.isNormal()) + const normalLayers = this.theme.layers.filter((l) => l.isNormal()) const maxzoom = Math.min(...normalLayers.map((l) => l.minzoom)) @@ -746,7 +760,7 @@ export default class ThemeViewState implements SpecialVisualizationState { (l) => Constants.priviliged_layers.indexOf(l.id) < 0 && l.source.geojsonSource === undefined && - l.doCount, + l.doCount ) if (!Constants.SummaryServer || layers.length === 0) { return undefined @@ -758,7 +772,7 @@ export default class ThemeViewState implements SpecialVisualizationState { this.mapProperties, { isActive: this.mapProperties.zoom.map((z) => z < maxzoom), - }, + } ) return new SummaryTileSourceRewriter(summaryTileSource, this.layerState.filteredLayers) @@ -780,12 +794,12 @@ export default class ThemeViewState implements SpecialVisualizationState { gps_track: this.geolocation.historicalUserLocationsTrack, geocoded_image: new StaticFeatureSource(this.geocodedImages), selected_element: new StaticFeatureSource( - this.selectedElement.map((f) => (f === undefined ? empty : [f])), + this.selectedElement.map((f) => (f === undefined ? empty : [f])) ), range: new StaticFeatureSource( this.mapProperties.maxbounds.map((bbox) => - bbox === undefined ? empty : [bbox.asGeoJson({ id: "range" })], - ), + bbox === undefined ? empty : [bbox.asGeoJson({ id: "range" })] + ) ), current_view: this.currentView, favourite: this.favourites, @@ -794,7 +808,6 @@ export default class ThemeViewState implements SpecialVisualizationState { search: this.searchState.locationResults, } - this.closestFeatures.registerSource(specialLayers.favourite, "favourite") if (this.theme?.lockLocation) { const bbox = new BBox(this.theme.lockLocation) @@ -802,7 +815,7 @@ export default class ThemeViewState implements SpecialVisualizationState { ShowDataLayer.showRange( this.map, new StaticFeatureSource([bbox.asGeoJson({ id: "range" })]), - this.featureSwitches.featureSwitchIsTesting, + this.featureSwitches.featureSwitchIsTesting ) } const currentViewLayer = this.theme.layers.find((l) => l.id === "current_view") @@ -816,7 +829,7 @@ export default class ThemeViewState implements SpecialVisualizationState { currentViewLayer, this.theme, this.osmObjectDownloader, - this.featureProperties, + this.featureProperties ) }) } @@ -848,7 +861,6 @@ export default class ThemeViewState implements SpecialVisualizationState { layer: flayer.layerDef, metaTags: this.userRelatedState.preferencesAsTags, selectedElement: this.selectedElement, - } if (flayer.layerDef.id === "search") { options.onClick = (feature) => { @@ -863,20 +875,20 @@ export default class ThemeViewState implements SpecialVisualizationState { { const lastClickLayerConfig = new LayerConfig( last_click_layerconfig, - "last_click", + "last_click" ) const lastClickFiltered = lastClickLayerConfig.isShown === undefined ? specialLayers.last_click : specialLayers.last_click.features.mapD((fs) => - fs.filter((f) => { - const matches = lastClickLayerConfig.isShown.matchesProperties( - f.properties, - ) - console.debug("LastClick ", f, "matches", matches) - return matches - }), - ) + fs.filter((f) => { + const matches = lastClickLayerConfig.isShown.matchesProperties( + f.properties + ) + console.debug("LastClick ", f, "matches", matches) + return matches + }) + ) // show last click = new point/note marker new ShowDataLayer(this.map, { features: new StaticFeatureSource(lastClickFiltered), @@ -908,7 +920,6 @@ export default class ThemeViewState implements SpecialVisualizationState { * Setup various services for which no reference are needed */ private initActors() { - if (!this.theme.official) { // Add custom themes to the "visited custom themes" const th = this.theme @@ -916,13 +927,11 @@ export default class ThemeViewState implements SpecialVisualizationState { id: th.id, icon: th.icon, title: th.title.translations, - shortDescription: th.shortDescription.translations , - layers: th.layers.filter(l => l.isNormal()).map(l => l.id) - + shortDescription: th.shortDescription.translations, + layers: th.layers.filter((l) => l.isNormal()).map((l) => l.id), }) } - this.selectedElement.addCallback((selected) => { if (selected === undefined) { this.focusOnMap() @@ -942,28 +951,31 @@ export default class ThemeViewState implements SpecialVisualizationState { }) // Add the selected element to the recently visited history - this.selectedElement.addCallbackD(selected => { + this.selectedElement.addCallbackD((selected) => { const [osm_type, osm_id] = selected.properties.id.split("/") const [lon, lat] = GeoOperations.centerpointCoordinates(selected) const layer = this.theme.getMatchingLayer(selected.properties) const nameOptions = [ selected?.properties?.name, - selected?.properties?.alt_name, selected?.properties?.local_name, + selected?.properties?.alt_name, + selected?.properties?.local_name, layer?.title.GetRenderValue(selected?.properties ?? {}).txt, selected.properties.display_name, selected.properties.id, ] const r = { feature: selected, - display_name: nameOptions.find(opt => opt !== undefined), - osm_id, osm_type, - lon, lat, + display_name: nameOptions.find((opt) => opt !== undefined), + osm_id, + osm_type, + lon, + lat, } this.userRelatedState.recentlyVisitedSearch.add(r) }) - this.userRelatedState.showScale.addCallbackAndRun(showScale => { + this.userRelatedState.showScale.addCallbackAndRun((showScale) => { this.mapProperties.showScale.set(showScale) }) new ThemeViewStateHashActor(this) @@ -977,7 +989,7 @@ export default class ThemeViewState implements SpecialVisualizationState { this.mapProperties.rasterLayer, this.availableLayers, this.featureSwitches.backgroundLayerId, - this.userRelatedState.preferredBackgroundLayer, + this.userRelatedState.preferredBackgroundLayer ) } @@ -990,7 +1002,6 @@ export default class ThemeViewState implements SpecialVisualizationState { * Searches the appropriate layer - will first try if a special layer matches; if not, a normal layer will be used by delegating to the theme */ public getMatchingLayer(properties: Record) { - const id = properties.id if (id.startsWith("summary_")) { @@ -1024,7 +1035,7 @@ export default class ThemeViewState implements SpecialVisualizationState { ? ">>> _Not_ reporting error to report server as testmode is on" : ">>> Reporting error to", Constants.ErrorReportServer, - message, + message ) if (isTesting) { return diff --git a/src/UI/AllThemesGui.svelte b/src/UI/AllThemesGui.svelte index 4a66b37c8..5fa141abb 100644 --- a/src/UI/AllThemesGui.svelte +++ b/src/UI/AllThemesGui.svelte @@ -33,7 +33,7 @@ "oauth_token", undefined, "Used to complete the login" - ) + ), }) const state = new UserRelatedState(osmConnection) const t = Translations.t.index @@ -46,41 +46,56 @@ let searchIsFocused = new UIEventSource(true) - const officialThemes: MinimalThemeInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === false) - const hiddenThemes: MinimalThemeInformation[] = ThemeSearch.officialThemes.themes.filter(th => th.hideFromOverview === true) - let visitedHiddenThemes: Store = UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection) - .map((knownIds) => hiddenThemes.filter((theme) => - knownIds.indexOf(theme.id) >= 0 || state.osmConnection.userDetails.data.name === "Pieter Vander Vennet" - )) + const officialThemes: MinimalThemeInformation[] = ThemeSearch.officialThemes.themes.filter( + (th) => th.hideFromOverview === false + ) + const hiddenThemes: MinimalThemeInformation[] = ThemeSearch.officialThemes.themes.filter( + (th) => th.hideFromOverview === true + ) + let visitedHiddenThemes: Store = + UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection).map((knownIds) => + hiddenThemes.filter( + (theme) => + knownIds.indexOf(theme.id) >= 0 || + state.osmConnection.userDetails.data.name === "Pieter Vander Vennet" + ) + ) - const customThemes: Store = Stores.ListStabilized(state.installedUserThemes) - .mapD(stableIds => Utils.NoNullInplace(stableIds.map(id => state.getUnofficialTheme(id)))) + const customThemes: Store = Stores.ListStabilized( + state.installedUserThemes + ).mapD((stableIds) => Utils.NoNullInplace(stableIds.map((id) => state.getUnofficialTheme(id)))) function filtered(themes: Store): Store { - return searchStable.map(search => { - if (!search) { - return themes.data - } + return searchStable.map( + (search) => { + if (!search) { + return themes.data + } - const start = new Date().getTime() - const scores = ThemeSearch.sortedByLowestScores(search, themes.data) - const end = new Date().getTime() - console.trace("Scores for", search , "are", scores, "searching took", end - start,"ms") - const strict = scores.filter(sc => sc.lowest < 2) - if (strict.length > 0) { - return strict.map(sc => sc.theme) - } - return scores.filter(sc => sc.lowest < 4).slice(0, 6).map(sc => sc.theme) - }, [themes]) + const start = new Date().getTime() + const scores = ThemeSearch.sortedByLowestScores(search, themes.data) + const end = new Date().getTime() + console.trace("Scores for", search, "are", scores, "searching took", end - start, "ms") + const strict = scores.filter((sc) => sc.lowest < 2) + if (strict.length > 0) { + return strict.map((sc) => sc.theme) + } + return scores + .filter((sc) => sc.lowest < 4) + .slice(0, 6) + .map((sc) => sc.theme) + }, + [themes] + ) } - - let officialSearched : Store= filtered(new ImmutableStore(officialThemes)) - let hiddenSearched: Store = filtered(visitedHiddenThemes) + let officialSearched: Store = filtered( + new ImmutableStore(officialThemes) + ) + let hiddenSearched: Store = filtered(visitedHiddenThemes) let customSearched: Store = filtered(customThemes) - let searchIsFocussed = new UIEventSource(false) - document.addEventListener("keydown", function(event) { + document.addEventListener("keydown", function (event) { if (event.ctrlKey && event.code === "KeyF") { searchIsFocussed.set(true) event.preventDefault() @@ -101,10 +116,7 @@ } window.location.href = ThemeSearch.createUrlFor(candidate, undefined) - } - -
@@ -136,7 +148,13 @@ - applySearch()} autofocus isFocused={searchIsFocussed} /> + applySearch()} + autofocus + isFocused={searchIsFocussed} + /> @@ -166,8 +184,11 @@ {#if $customThemes.length > 0} -

@@ -177,7 +198,6 @@ {/if} - = 0){ + if (dotsPosition.indexOf("left-0") >= 0) { menuPosition = "left-0" - }else{ + } else { menuPosition = `right-0` } - if(dotsPosition.indexOf("top-0") > 0){ + if (dotsPosition.indexOf("top-0") > 0) { menuPosition += " bottom-0" - }else{ + } else { menuPosition += ` top-0` } function toggle() { open.set(!open.data) } - -
+ class={`absolute ${dotsPosition} ${dotsSize} dots-menu transition-colors ${ + $open ? "dots-menu-opened" : "" + }`} + on:click={toggle} + />
- diff --git a/src/UI/Base/DrawerRight.svelte b/src/UI/Base/DrawerRight.svelte index 6fd67c962..f6aaaf440 100644 --- a/src/UI/Base/DrawerRight.svelte +++ b/src/UI/Base/DrawerRight.svelte @@ -8,11 +8,11 @@ let transitionParams = { x: 640, duration: 200, - easing: sineIn + easing: sineIn, } let hidden = !shown.data - shown.addCallback(sh => { + shown.addCallback((sh) => { hidden = !sh }) @@ -23,19 +23,21 @@ }) - - +
-
+
diff --git a/src/UI/Base/Page.svelte b/src/UI/Base/Page.svelte index f284b8e32..1ec03cd03 100644 --- a/src/UI/Base/Page.svelte +++ b/src/UI/Base/Page.svelte @@ -3,12 +3,10 @@ import { UIEventSource } from "../../Logic/UIEventSource" import Popup from "./Popup.svelte" - export let onlyLink: boolean = false export let bodyPadding = "p-4 md:p-5 " export let fullscreen: boolean = false export let shown: UIEventSource - {#if !onlyLink} diff --git a/src/UI/Base/Popup.svelte b/src/UI/Base/Popup.svelte index 65fcd08f1..c90b33680 100644 --- a/src/UI/Base/Popup.svelte +++ b/src/UI/Base/Popup.svelte @@ -8,7 +8,8 @@ export let fullscreen: boolean = false - const shared = "in-page normal-background dark:bg-gray-800 rounded-lg border-gray-200 dark:border-gray-700 border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md" + const shared = + "in-page normal-background dark:bg-gray-800 rounded-lg border-gray-200 dark:border-gray-700 border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md" let defaultClass = "relative flex flex-col mx-auto w-full divide-y " + shared if (fullscreen) { defaultClass = shared @@ -27,20 +28,23 @@ export let shown: UIEventSource export let dismissable = true let _shown = false - shown.addCallbackAndRun(sh => { + shown.addCallbackAndRun((sh) => { _shown = sh }) - - - - shown.set(false)} outsideclose - size="xl" - {dismissable} - {defaultClass} {bodyClass} {dialogClass} {headerClass} - color="none"> - + shown.set(false)} + outsideclose + size="xl" + {dismissable} + {defaultClass} + {bodyClass} + {dialogClass} + {headerClass} + color="none" +> {#if $$slots.header}

diff --git a/src/UI/Base/Searchbar.svelte b/src/UI/Base/Searchbar.svelte index d92ae10e0..f85fba471 100644 --- a/src/UI/Base/Searchbar.svelte +++ b/src/UI/Base/Searchbar.svelte @@ -10,7 +10,7 @@ export let value: UIEventSource let _value = value.data ?? "" - value.addCallbackD(v => { + value.addCallbackD((v) => { _value = v }) $: value.set(_value) @@ -22,7 +22,7 @@ export let autofocus = false - isFocused?.addCallback(focussed => { + isFocused?.addCallback((focussed) => { if (focussed) { requestAnimationFrame(() => { if (document.activeElement !== inputElement) { @@ -33,41 +33,44 @@ } }) - if(autofocus){ + if (autofocus) { isFocused.set(true) } - - -
dispatch("search")} -> + dispatch("search")}>
+
{/if} diff --git a/src/UI/Base/SidebarUnit.svelte b/src/UI/Base/SidebarUnit.svelte index aa1b8fcdc..d868eca21 100644 --- a/src/UI/Base/SidebarUnit.svelte +++ b/src/UI/Base/SidebarUnit.svelte @@ -1,55 +1,64 @@ diff --git a/src/UI/BigComponents/Filterview.svelte b/src/UI/BigComponents/Filterview.svelte index d247c76df..26e084ed4 100644 --- a/src/UI/BigComponents/Filterview.svelte +++ b/src/UI/BigComponents/Filterview.svelte @@ -31,7 +31,7 @@ return state.sync( (f) => f === 0, [], - (b) => (b ? 0 : undefined), + (b) => (b ? 0 : undefined) ) } @@ -92,7 +92,7 @@ {/if}
{:else if $isDebugging} -
- {layer.id} (no name) -
+
+ {layer.id} (no name) +
{/if} diff --git a/src/UI/BigComponents/FilterviewWithFields.svelte b/src/UI/BigComponents/FilterviewWithFields.svelte index 5245c9c2f..98e9c100a 100644 --- a/src/UI/BigComponents/FilterviewWithFields.svelte +++ b/src/UI/BigComponents/FilterviewWithFields.svelte @@ -55,14 +55,18 @@ let feedback: UIEventSource = new UIEventSource(undefined) -
- - - diff --git a/src/UI/BigComponents/NewPointLocationInput.svelte b/src/UI/BigComponents/NewPointLocationInput.svelte index ced6c8f85..4156acade 100644 --- a/src/UI/BigComponents/NewPointLocationInput.svelte +++ b/src/UI/BigComponents/NewPointLocationInput.svelte @@ -83,7 +83,11 @@ let featuresForLayer: FeatureSource = state.perLayer.get(targetLayer.id) if (featuresForLayer) { if (dontShow) { - featuresForLayer = new StaticFeatureSource(featuresForLayer.features.map(feats => feats.filter(f => dontShow.indexOf(f.properties.id) < 0))) + featuresForLayer = new StaticFeatureSource( + featuresForLayer.features.map((feats) => + feats.filter((f) => dontShow.indexOf(f.properties.id) < 0) + ) + ) } new ShowDataLayer(map, { layer: targetLayer, @@ -116,7 +120,7 @@ allowUnsnapped: true, snappedTo, snapLocation: value, - }, + } ) const withCorrectedAttributes = new StaticFeatureSource( snappedLocation.features.mapD((feats) => @@ -130,8 +134,8 @@ ...f, properties, } - }), - ), + }) + ) ) // The actual point to be created, snapped at the new location new ShowDataLayer(map, { @@ -140,14 +144,13 @@ }) withCorrectedAttributes.features.addCallbackAndRunD((f) => console.log("Snapped point is", f)) } - 0 ? new UIEventSource(undefined) : value} + value={snapToLayers?.length > 0 ? new UIEventSource(undefined) : value} initialCoordinate={coordinate} {maxDistanceInMeters} > diff --git a/src/UI/BigComponents/PanoramaxLink.svelte b/src/UI/BigComponents/PanoramaxLink.svelte index 058674c06..dcd273d72 100644 --- a/src/UI/BigComponents/PanoramaxLink.svelte +++ b/src/UI/BigComponents/PanoramaxLink.svelte @@ -6,7 +6,7 @@ import { twMerge } from "tailwind-merge" import { PanoramaxXYZ, Panoramax } from "panoramax-js/dist" import Panoramax_bw from "../../assets/svg/Panoramax_bw.svelte" - import {default as Panoramax_svg} from "../../assets/svg/Panoramax.svelte" + import { default as Panoramax_svg } from "../../assets/svg/Panoramax.svelte" /* A subtleButton which opens panoramax in a new tab at the current location @@ -19,11 +19,14 @@ } let location = mapProperties.location let zoom = mapProperties.zoom - let href = location.mapD(location => - host.createViewLink({ - location, - zoom: zoom.data, - }), [zoom]) + let href = location.mapD( + (location) => + host.createViewLink({ + location, + zoom: zoom.data, + }), + [zoom] + ) export let large: boolean = true diff --git a/src/UI/BigComponents/ShareScreen.svelte b/src/UI/BigComponents/ShareScreen.svelte index e4c91b142..fe7280f51 100644 --- a/src/UI/BigComponents/ShareScreen.svelte +++ b/src/UI/BigComponents/ShareScreen.svelte @@ -116,7 +116,6 @@