Chore: housekeeping, formatting

This commit is contained in:
Pieter Vander Vennet 2023-10-16 14:27:05 +02:00
parent 132f2933cd
commit c2164e5951
66 changed files with 1698 additions and 1302 deletions

View file

@ -130,6 +130,7 @@
- food
- ghost_bike
- governments
- guidepost
- hackerspace
- hotel
- hydrant

View file

@ -1451,6 +1451,7 @@ The following layers are included in MapComplete:
- [gps_location](./Layers/gps_location.md)
- [gps_location_history](./Layers/gps_location_history.md)
- [gps_track](./Layers/gps_track.md)
- [guidepost](./Layers/guidepost.md)
- [hackerspace](./Layers/hackerspace.md)
- [home_location](./Layers/home_location.md)
- [hospital](./Layers/hospital.md)

View file

@ -15,6 +15,7 @@ Bicycle rental stations
- This layer is shown at zoomlevel **14** and higher
- Not visible in the layer selection by default. If you want to make this layer toggable, override `name`

150
Docs/Layers/guidepost.md Normal file
View file

@ -0,0 +1,150 @@
[//]: # (WARNING: this file is automatically generated. Please find the sources at the bottom and edit those sources)
guidepost
===========
<img src='https://mapcomplete.org/./assets/layers/guidepost/guidepost.svg' height="100px">
Guideposts (also known as fingerposts or finger posts) are often found along official hiking/cycling/riding/skiing routes to indicate the directions to different destinations
- This layer is shown at zoomlevel **14** and higher
#### Themes using this layer
- [climbing](https://mapcomplete.org/climbing)
- [guideposts](https://mapcomplete.org/guideposts)
- [personal](https://mapcomplete.org/personal)
This is a special layer - data is not sourced from OpenStreetMap
Supported attributes
----------------------
Warning:
this quick overview is incomplete
attribute | type | values which are supported by this layer
----------- | ------ | ------------------------------------------
[<img src='https://mapcomplete.org/assets/svg/statistics.svg' height='18px'>](https://taginfo.openstreetmap.org/keys/id#values) [id](https://wiki.openstreetmap.org/wiki/Key:id) | Multiple choice |
### just_created
This element shows a 'thank you' that the contributor has recently created this element
This tagrendering has no question and is thus read-only
- *You just created this element! Thanks for sharing this info with the world and helping people worldwide.* corresponds with `id~.+`
This tagrendering is only visible in the popup if the following condition is met: `_backend~.+&_last_edit:passed_time<300&|_version_number=1`
### images
This block shows the known images which are linked with the `image`-keys, but also via `mapillary` and `wikidata` and shows the button to upload new images
This tagrendering has no question and is thus read-only
### leftover-questions
This tagrendering has no question and is thus read-only
### minimap
Shows a small map with the feature. Added by default to every popup
This tagrendering has no question and is thus read-only
### move-button
This tagrendering has no question and is thus read-only
### delete-button
This tagrendering has no question and is thus read-only
### last_edit
Gives some metainfo about the last edit and who did edit it - rendering only
This tagrendering has no question and is thus read-only
This tagrendering is only visible in the popup if the following condition is met: `_last_edit:contributor~.+&_last_edit:changeset~.+`
### all-tags
This tagrendering has no question and is thus read-only
This document is autogenerated from [assets/layers/guidepost/guidepost.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/layers/guidepost/guidepost.json)

View file

@ -328,6 +328,7 @@ available_sports.1 | Basketball fields | sport=basketball
available_sports.2 | Soccer fields | sport=soccer
available_sports.3 | Ping-pong tables | sport=table_tennis
available_sports.4 | Tennis fields | sport=tennis
available_sports.5 | Badminton fields | sport=badminton

View file

@ -14,7 +14,7 @@ Find ticket machines for public transport tickets
- This layer is shown at zoomlevel **19** and higher
- This layer is shown at zoomlevel **18** and higher

View file

@ -14,7 +14,7 @@ Find ticket validators to validate public transport tickets
- This layer is shown at zoomlevel **19** and higher
- This layer is shown at zoomlevel **18** and higher
This is a special layer - data is not sourced from OpenStreetMap

View file

@ -3244,6 +3244,31 @@
"key": "bottle",
"description": "Layer 'Drinking water' shows bottle=no with a fixed text, namely 'Water bottles may not fit' and allows to pick this as a default answer (in the mapcomplete.org theme 'Climbing gyms, clubs and spots')",
"value": "no"
},
{
"key": "information",
"description": "The MapComplete theme Climbing gyms, clubs and spots has a layer Guideposts showing features with this tag",
"value": "guidepost"
},
{
"key": "id",
"description": "Layer 'Guideposts' shows id~.+ with a fixed text, namely 'You just created this element! Thanks for sharing this info with the world and helping people worldwide.' (in the mapcomplete.org theme 'Climbing gyms, clubs and spots') (This is only shown if _backend~.+&_last_edit:passed_time<300&|_version_number=1)"
},
{
"key": "image",
"description": "The layer 'Guideposts allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary"
},
{
"key": "mapillary",
"description": "The layer 'Guideposts allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary"
},
{
"key": "wikidata",
"description": "The layer 'Guideposts allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary"
},
{
"key": "wikipedia",
"description": "The layer 'Guideposts allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary"
}
]
}

View file

@ -0,0 +1,39 @@
{
"data_format": 1,
"project": {
"name": "MapComplete Guideposts",
"description": "Guideposts (also known as fingerposts or finger posts) are often found along official hiking, cycling, skiing or horseback riding routes to indicate the directions to different destinations\n\nThe position of a signpost can be used by a hiker/biker/rider...",
"project_url": "https://mapcomplete.org/guideposts",
"doc_url": "https://github.com/pietervdvn/MapComplete/tree/master/assets/themes/",
"icon_url": "https://mapcomplete.org/assets/layers/guidepost/guidepost.svg",
"contact_name": "Pieter Vander Vennet",
"contact_email": "pietervdvn@posteo.net"
},
"tags": [
{
"key": "information",
"description": "The MapComplete theme Guideposts has a layer Guideposts showing features with this tag",
"value": "guidepost"
},
{
"key": "id",
"description": "Layer 'Guideposts' shows id~.+ with a fixed text, namely 'You just created this element! Thanks for sharing this info with the world and helping people worldwide.' (in the mapcomplete.org theme 'Guideposts') (This is only shown if _backend~.+&_last_edit:passed_time<300&|_version_number=1)"
},
{
"key": "image",
"description": "The layer 'Guideposts allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary"
},
{
"key": "mapillary",
"description": "The layer 'Guideposts allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary"
},
{
"key": "wikidata",
"description": "The layer 'Guideposts allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary"
},
{
"key": "wikipedia",
"description": "The layer 'Guideposts allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary"
}
]
}

View file

@ -7962,6 +7962,31 @@
"key": "name",
"description": "Layer 'governments' shows and asks freeform values for key 'name' (in the mapcomplete.org theme 'Personal theme')"
},
{
"key": "information",
"description": "The MapComplete theme Personal theme has a layer Guideposts showing features with this tag",
"value": "guidepost"
},
{
"key": "id",
"description": "Layer 'Guideposts' shows id~.+ with a fixed text, namely 'You just created this element! Thanks for sharing this info with the world and helping people worldwide.' (in the mapcomplete.org theme 'Personal theme') (This is only shown if _backend~.+&_last_edit:passed_time<300&|_version_number=1)"
},
{
"key": "image",
"description": "The layer 'Guideposts allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary"
},
{
"key": "mapillary",
"description": "The layer 'Guideposts allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary"
},
{
"key": "wikidata",
"description": "The layer 'Guideposts allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary"
},
{
"key": "wikipedia",
"description": "The layer 'Guideposts allows to upload images and adds them under the 'image'-tag (and image:0, image:1, ... for multiple images). Furhtermore, this layer shows images based on the keys image, wikidata, wikipedia, wikimedia_commons and mapillary"
},
{
"key": "leisure",
"description": "The MapComplete theme Personal theme has a layer Hackerspace showing features with this tag",

View file

@ -37,6 +37,7 @@ Available languages:
- es
- cs
- zh_Hant
- pl
This document is autogenerated from [assets/themes/bag/bag.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/bag/bag.json)

View file

@ -38,6 +38,7 @@ Available languages:
- cs
- zh_Hant
- eu
- pl
This document is autogenerated from [assets/themes/bicycle_rental/bicycle_rental.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/bicycle_rental/bicycle_rental.json)

View file

@ -39,6 +39,7 @@ Available languages:
- pa_PK
- cs
- eu
- pl
This document is autogenerated from [assets/themes/binoculars/binoculars.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/binoculars/binoculars.json)

View file

@ -41,6 +41,7 @@ Available languages:
- pa_PK
- cs
- eu
- pl
This document is autogenerated from [assets/themes/bookcases/bookcases.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/bookcases/bookcases.json)

View file

@ -42,6 +42,7 @@ Available languages:
- pa_PK
- cs
- eu
- pl
This document is autogenerated from [assets/themes/campersite/campersite.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/campersite/campersite.json)

View file

@ -20,6 +20,7 @@ This theme contains the following layers:
- [shops](../Layers/shops.md)
- [toilet](../Layers/toilet.md)
- [drinking_water](../Layers/drinking_water.md)
- [guidepost](../Layers/guidepost.md)
- [selected_element](../Layers/selected_element.md)
- [gps_location](../Layers/gps_location.md)
- [gps_location_history](../Layers/gps_location_history.md)
@ -47,6 +48,7 @@ Available languages:
- da
- cs
- es
- pl
This document is autogenerated from [assets/themes/climbing/climbing.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/climbing/climbing.json)

View file

@ -36,6 +36,7 @@ Available languages:
- da
- pa_PK
- cs
- pl
This document is autogenerated from [assets/themes/cycle_highways/cycle_highways.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/cycle_highways/cycle_highways.json)

View file

@ -34,6 +34,7 @@ Available languages:
- fr
- ca
- cs
- pl
This document is autogenerated from [assets/themes/cyclenodes/cyclenodes.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/cyclenodes/cyclenodes.json)

View file

@ -42,6 +42,7 @@ Available languages:
- pa_PK
- cs
- eu
- pl
This document is autogenerated from [assets/themes/cyclestreets/cyclestreets.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/cyclestreets/cyclestreets.json)

View file

@ -29,6 +29,8 @@ Available languages:
- de
- es
- ca
- cs
- pl
This document is autogenerated from [assets/themes/elongated_coin/elongated_coin.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/elongated_coin/elongated_coin.json)

View file

@ -37,6 +37,7 @@ Available languages:
- es
- da
- cs
- pl
This document is autogenerated from [assets/themes/facadegardens/facadegardens.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/facadegardens/facadegardens.json)

View file

@ -38,6 +38,7 @@ Available languages:
- da
- cs
- ru
- pl
This document is autogenerated from [assets/themes/food/food.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/food/food.json)

View file

@ -35,6 +35,7 @@ Available languages:
- pa_PK
- cs
- es
- pl
This document is autogenerated from [assets/themes/fritures/fritures.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/fritures/fritures.json)

33
Docs/Themes/guideposts.md Normal file
View file

@ -0,0 +1,33 @@
[//]: # (WARNING: this file is automatically generated. Please find the sources at the bottom and edit those sources)
Guideposts ( [guideposts](https://mapcomplete.org/guideposts) )
-----------------------------------------------------------------
Guideposts (also known as fingerposts or finger posts) are often found along official hiking, cycling, skiing or horseback riding routes to indicate the directions to different destinations. Additionally, they are often named after a region or place and show the altitude.
The position of a signpost can be used by a hiker/biker/rider/skier as a confirmation of the current position, especially if they use a printed map without a GPS receiver.
This theme contains the following layers:
- [guidepost](../Layers/guidepost.md)
- [selected_element](../Layers/selected_element.md)
- [gps_location](../Layers/gps_location.md)
- [gps_location_history](../Layers/gps_location_history.md)
- [home_location](../Layers/home_location.md)
- [gps_track](../Layers/gps_track.md)
- [range](../Layers/range.md)
- [last_click](../Layers/last_click.md)
Available languages:
- en
This document is autogenerated from [assets/themes/guideposts/guideposts.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/guideposts/guideposts.json)

View file

@ -39,6 +39,7 @@ Available languages:
- cs
- es
- eu
- pl
This document is autogenerated from [assets/themes/hackerspaces/hackerspaces.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/hackerspaces/hackerspaces.json)

View file

@ -42,6 +42,7 @@ Available languages:
- ca
- da
- cs
- pl
This document is autogenerated from [assets/themes/hailhydrant/hailhydrant.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/hailhydrant/hailhydrant.json)

View file

@ -31,6 +31,9 @@ Available languages:
- de
- fr
- nl
- cs
- es
- pl
This document is autogenerated from [assets/themes/mapcomplete-changes/mapcomplete-changes.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/mapcomplete-changes/mapcomplete-changes.json)

View file

@ -42,6 +42,7 @@ Available languages:
- cs
- es
- zh_Hant
- pl
This document is autogenerated from [assets/themes/nature/nature.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/nature/nature.json)

View file

@ -55,6 +55,7 @@ Available languages:
- es
- cs
- eu
- pl
This document is autogenerated from [assets/themes/onwheels/onwheels.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/onwheels/onwheels.json)

View file

@ -39,6 +39,7 @@ Available languages:
- pa_PK
- es
- cs
- pl
This document is autogenerated from [assets/themes/openwindpowermap/openwindpowermap.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/openwindpowermap/openwindpowermap.json)

View file

@ -32,6 +32,7 @@ Available languages:
- es
- ca
- cs
- pl
This document is autogenerated from [assets/themes/osm_community_index/osm_community_index.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/osm_community_index/osm_community_index.json)

View file

@ -58,6 +58,7 @@ This theme contains the following layers:
- [food](../Layers/food.md)
- [ghost_bike](../Layers/ghost_bike.md)
- [governments](../Layers/governments.md)
- [guidepost](../Layers/guidepost.md)
- [hackerspace](../Layers/hackerspace.md)
- [hospital](../Layers/hospital.md)
- [hotel](../Layers/hotel.md)
@ -140,6 +141,7 @@ Available languages:
- da
- pa_PK
- cs
- pl
This document is autogenerated from [assets/themes/personal/personal.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/personal/personal.json)

View file

@ -39,6 +39,7 @@ Available languages:
- ca
- es
- cs
- pl
This document is autogenerated from [assets/themes/postboxes/postboxes.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/postboxes/postboxes.json)

View file

@ -37,6 +37,7 @@ Available languages:
- es
- cs
- zh_Hant
- pl
This document is autogenerated from [assets/themes/rainbow_crossings/rainbow_crossings.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/rainbow_crossings/rainbow_crossings.json)

View file

@ -38,6 +38,7 @@ Available languages:
- es
- da
- cs
- pl
This document is autogenerated from [assets/themes/sport_pitches/sport_pitches.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/sport_pitches/sport_pitches.json)

View file

@ -34,6 +34,9 @@ Available languages:
- de
- fr
- ca
- cs
- es
- pl
This document is autogenerated from [assets/themes/vending_machine/vending_machine.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/vending_machine/vending_machine.json)

View file

@ -40,6 +40,7 @@ Available languages:
- cs
- zh_Hant
- eu
- pl
This document is autogenerated from [assets/themes/waste/waste.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/waste/waste.json)

View file

@ -4,7 +4,7 @@
{{service_item
|name= [https://mapcomplete.org/personal personal]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:gl|en}}, {{#language:fr|en}}, {{#language:de|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}}, {{#language:it|en}}, {{#language:da|en}}, {{#language:cs|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:gl|en}}, {{#language:fr|en}}, {{#language:de|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}}, {{#language:it|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: Create a personal theme based on all the available layers of all themes
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -13,7 +13,7 @@
{{service_item
|name= [https://mapcomplete.org/cyclofix cyclofix]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:gl|en}}, {{#language:de|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:gl|en}}, {{#language:de|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: The goal of this map is to present cyclists with an easy-to-use solution to find the appropriate infrastructure for their needs
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -22,7 +22,7 @@
{{service_item
|name= [https://mapcomplete.org/waste waste]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:it|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:es|en}}, {{#language:cs|en}}, {{#language:zh_Hant|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:it|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:es|en}}, {{#language:cs|en}}, {{#language:zh_Hant|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: Map showing waste baskets and recycling facilities
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -31,7 +31,7 @@
{{service_item
|name= [https://mapcomplete.org/etymology etymology]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:zh_Hant|en}}, {{#language:hu|en}}, {{#language:fr|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:cs|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:zh_Hant|en}}, {{#language:hu|en}}, {{#language:fr|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: What is the origin of a toponym?
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -40,7 +40,7 @@
{{service_item
|name= [https://mapcomplete.org/food food]
|region= Worldwide
|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:de|en}}, {{#language:es|en}}, {{#language:nb_NO|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}
|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:de|en}}, {{#language:es|en}}, {{#language:nb_NO|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: Restaurants and fast food
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -49,7 +49,7 @@
{{service_item
|name= [https://mapcomplete.org/cafes_and_pubs cafes_and_pubs]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:nb_NO|en}}, {{#language:pa_PK|en}}, {{#language:cs|en}}, {{#language:it|en}}, {{#language:zh_Hant|en}}, {{#language:eu|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:nb_NO|en}}, {{#language:pa_PK|en}}, {{#language:cs|en}}, {{#language:it|en}}, {{#language:zh_Hant|en}}, {{#language:eu|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: Coffeehouses, pubs and bars
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -58,7 +58,7 @@
{{service_item
|name= [https://mapcomplete.org/shops shops]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:fr|en}}, {{#language:ja|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:nl|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:zh_Hant|en}}
|lang= {{#language:en|en}}, {{#language:fr|en}}, {{#language:ja|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:nl|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:zh_Hant|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: An editable map with basic shop information
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -76,7 +76,7 @@
{{service_item
|name= [https://mapcomplete.org/hailhydrant hailhydrant]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}}, {{#language:fr|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:nl|en}}, {{#language:da|en}}, {{#language:cs|en}}
|lang= {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}}, {{#language:fr|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:nl|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: Map to show hydrants, extinguishers, fire stations, and ambulance stations.
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -103,7 +103,7 @@
{{service_item
|name= [https://mapcomplete.org/bookcases bookcases]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:eu|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:eu|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: A public bookcase is a small streetside cabinet, box, old phone booth or some other objects where books are stored
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -148,7 +148,7 @@
{{service_item
|name= [https://mapcomplete.org/bicycle_rental bicycle_rental]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:id|en}}, {{#language:fr|en}}, {{#language:es|en}}, {{#language:nb_NO|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:eu|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:id|en}}, {{#language:fr|en}}, {{#language:es|en}}, {{#language:nb_NO|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:eu|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: A map with bicycle rental stations and bicycle rental shops
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -157,7 +157,7 @@
{{service_item
|name= [https://mapcomplete.org/bicyclelib bicyclelib]
|region= Worldwide
|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:fr|en}}, {{#language:zh_Hant|en}}, {{#language:de|en}}, {{#language:hu|en}}, {{#language:nb_NO|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:eu|en}}
|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:fr|en}}, {{#language:zh_Hant|en}}, {{#language:de|en}}, {{#language:hu|en}}, {{#language:nb_NO|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:eu|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: A bicycle library is a place where bicycles can be lent, often for a small yearly fee
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -166,7 +166,7 @@
{{service_item
|name= [https://mapcomplete.org/binoculars binoculars]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:nb_NO|en}}, {{#language:zh_Hant|en}}, {{#language:hu|en}}, {{#language:fr|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:eu|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:nb_NO|en}}, {{#language:zh_Hant|en}}, {{#language:hu|en}}, {{#language:fr|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:eu|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: A map with fixed binoculars
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -184,7 +184,7 @@
{{service_item
|name= [https://mapcomplete.org/campersite campersite]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:fr|en}}, {{#language:zh_Hant|en}}, {{#language:nl|en}}, {{#language:pt_BR|en}}, {{#language:de|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}
|lang= {{#language:en|en}}, {{#language:it|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:fr|en}}, {{#language:zh_Hant|en}}, {{#language:nl|en}}, {{#language:pt_BR|en}}, {{#language:de|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: Find sites to spend the night with your camper
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -193,7 +193,7 @@
{{service_item
|name= [https://mapcomplete.org/charging_stations charging_stations]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:it|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:nb_NO|en}}, {{#language:ru|en}}, {{#language:hu|en}}, {{#language:fr|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:eu|en}}
|lang= {{#language:en|en}}, {{#language:it|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:nb_NO|en}}, {{#language:ru|en}}, {{#language:hu|en}}, {{#language:fr|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:eu|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: A worldwide map of charging stations
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -202,7 +202,7 @@
{{service_item
|name= [https://mapcomplete.org/climbing climbing]
|region= Worldwide
|lang= {{#language:nl|en}}, {{#language:de|en}}, {{#language:en|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:fr|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:nb_NO|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}, {{#language:es|en}}
|lang= {{#language:nl|en}}, {{#language:de|en}}, {{#language:en|en}}, {{#language:ru|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:fr|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:nb_NO|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: On this map you will find various climbing opportunities such as climbing gyms, bouldering halls and rocks in nature
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -211,7 +211,7 @@
{{service_item
|name= [https://mapcomplete.org/clock clock]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:ca|en}}, {{#language:de|en}}, {{#language:es|en}}, {{#language:cs|en}}, {{#language:fr|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:ca|en}}, {{#language:de|en}}, {{#language:es|en}}, {{#language:cs|en}}, {{#language:fr|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: Map showing all public clocks
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -220,7 +220,7 @@
{{service_item
|name= [https://mapcomplete.org/cycle_infra cycle_infra]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:nb_NO|en}}, {{#language:zh_Hant|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:cs|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:nb_NO|en}}, {{#language:zh_Hant|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: A map where you can view and edit things related to the bicycle infrastructure.
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -229,7 +229,7 @@
{{service_item
|name= [https://mapcomplete.org/cyclestreets cyclestreets]
|region= Worldwide
|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:nb_NO|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:cs|en}}
|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:nb_NO|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: A map of cyclestreets
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -247,7 +247,7 @@
{{service_item
|name= [https://mapcomplete.org/education education]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:es|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:ca|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: On this map, you'll find information about all types of schools and education and can easily add more information
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -256,7 +256,7 @@
{{service_item
|name= [https://mapcomplete.org/elongated_coin elongated_coin]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:es|en}}, {{#language:ca|en}}
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: Find penny presses to create your own elongated coins
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -265,7 +265,7 @@
{{service_item
|name= [https://mapcomplete.org/facadegardens facadegardens]
|region= Worldwide
|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}}, {{#language:fr|en}}, {{#language:de|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}
|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:it|en}}, {{#language:fr|en}}, {{#language:de|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: This map shows facade gardens with pictures and useful info about orientation, sunshine and plant types.
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -274,7 +274,7 @@
{{service_item
|name= [https://mapcomplete.org/fritures fritures]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}, {{#language:es|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: On this map, you'll find your favourite fries shop!
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -283,16 +283,27 @@
{{service_item
|name= [https://mapcomplete.org/ghostbikes ghostbikes]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:fr|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:ca|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:fr|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: A ghost bike is a memorial for a cyclist who died in a traffic accident, in the form of a white bicycle placed permanently near the accident location
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
|genre= POI, editor, ghostbikes
}}
{{service_item
|name= [https://mapcomplete.org/guideposts guideposts]
|region= Worldwide
|lang= {{#language:en|en}}
|descr= A MapComplete theme: Guideposts (also known as fingerposts or finger posts) are often found along official hiking, cycling, skiing or horseback riding routes to indicate the directions to different destinations
The position of a signpost can be used by a hiker/biker/rider...
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
|genre= POI, editor, guideposts
}}
{{service_item
|name= [https://mapcomplete.org/hackerspaces hackerspaces]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:zh_Hant|en}}, {{#language:hu|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:ca|en}}
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:zh_Hant|en}}, {{#language:hu|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: A map of hackerspaces
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -301,7 +312,7 @@
{{service_item
|name= [https://mapcomplete.org/healthcare healthcare]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:ca|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:nl|en}}, {{#language:cs|en}}, {{#language:es|en}}
|lang= {{#language:en|en}}, {{#language:ca|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:nl|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: On this map, various healthcare related items are shown
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -310,7 +321,7 @@
{{service_item
|name= [https://mapcomplete.org/hotels hotels]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:da|en}}, {{#language:nb_NO|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:ca|en}}
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:da|en}}, {{#language:nb_NO|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: On this map, you'll find hotels in your area
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -319,7 +330,7 @@
{{service_item
|name= [https://mapcomplete.org/indoors indoors]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:nl|en}}, {{#language:cs|en}}, {{#language:nb_NO|en}}, {{#language:es|en}}, {{#language:ca|en}}
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:nl|en}}, {{#language:cs|en}}, {{#language:nb_NO|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: On this map, publicly accessible indoor places are shown
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -337,7 +348,7 @@
{{service_item
|name= [https://mapcomplete.org/maps maps]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: This theme shows all (touristic) maps that OpenStreetMap knows of
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -346,7 +357,7 @@
{{service_item
|name= [https://mapcomplete.org/maxspeed maxspeed]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:nl|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:zh_Hant|en}}
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:nl|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:zh_Hant|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: This map shows the legally allowed maximum speed on every road.
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -355,7 +366,7 @@
{{service_item
|name= [https://mapcomplete.org/nature nature]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}, {{#language:zh_Hant|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}, {{#language:zh_Hant|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: A map for nature lovers, with interesting POI's
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -364,7 +375,7 @@
{{service_item
|name= [https://mapcomplete.org/notes notes]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:hu|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}, {{#language:zh_Hant|en}}
|lang= {{#language:en|en}}, {{#language:hu|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:ca|en}}, {{#language:zh_Hant|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: A note is a pin on the map with some text to indicate something wrong
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -382,7 +393,7 @@
{{service_item
|name= [https://mapcomplete.org/onwheels onwheels]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:nl|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:ca|en}}
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:nl|en}}, {{#language:da|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: On this map, publicly weelchair accessible places are shown and can be easily added
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -400,7 +411,7 @@
{{service_item
|name= [https://mapcomplete.org/osm_community_index osm_community_index]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:cs|en}}
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:es|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: An index of community resources for OpenStreetMap.
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -409,7 +420,7 @@
{{service_item
|name= [https://mapcomplete.org/parkings parkings]
|region= Worldwide
|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:nb_NO|en}}, {{#language:zh_Hant|en}}, {{#language:id|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}
|lang= {{#language:nl|en}}, {{#language:en|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:nb_NO|en}}, {{#language:zh_Hant|en}}, {{#language:id|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: This map shows different parking spots
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -418,7 +429,7 @@
{{service_item
|name= [https://mapcomplete.org/pets pets]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:da|en}}, {{#language:de|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:cs|en}}
|lang= {{#language:en|en}}, {{#language:da|en}}, {{#language:de|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: On this map, you'll find various interesting places for you pets: veterinarians, dog parks, pet shops, dog-friendly restaurants,
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -427,7 +438,7 @@
{{service_item
|name= [https://mapcomplete.org/postboxes postboxes]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:zh_Hant|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:nb_NO|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:zh_Hant|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:nl|en}}, {{#language:fr|en}}, {{#language:nb_NO|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: A map showing postboxes and post offices
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -436,7 +447,7 @@
{{service_item
|name= [https://mapcomplete.org/rainbow_crossings rainbow_crossings]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:nl|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:cs|en}}
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:nl|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: On this map, rainbow-painted pedestrian crossings are shown and can be easily added
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -445,7 +456,7 @@
{{service_item
|name= [https://mapcomplete.org/sport_pitches sport_pitches]
|region= Worldwide
|lang= {{#language:nl|en}}, {{#language:fr|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}
|lang= {{#language:nl|en}}, {{#language:fr|en}}, {{#language:en|en}}, {{#language:ja|en}}, {{#language:zh_Hant|en}}, {{#language:ru|en}}, {{#language:de|en}}, {{#language:it|en}}, {{#language:hu|en}}, {{#language:es|en}}, {{#language:da|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: A map showing sport pitches
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -481,7 +492,7 @@
{{service_item
|name= [https://mapcomplete.org/transit transit]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:nl|en}}, {{#language:nb_NO|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:cs|en}}, {{#language:zh_Hant|en}}
|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:da|en}}, {{#language:nl|en}}, {{#language:nb_NO|en}}, {{#language:ca|en}}, {{#language:es|en}}, {{#language:cs|en}}, {{#language:zh_Hant|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: Plan your trip with the help of the public transport system
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png
@ -499,7 +510,7 @@
{{service_item
|name= [https://mapcomplete.org/vending_machine vending_machine]
|region= Worldwide
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:ca|en}}
|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}}, {{#language:ca|en}}, {{#language:cs|en}}, {{#language:es|en}}, {{#language:pl|en}}
|descr= A MapComplete theme: Find vending machines for everything
|material= {{yes|[https://mapcomplete.org/ Yes]}}
|image= MapComplete_Screenshot.png

File diff suppressed because it is too large Load diff

View file

@ -61,9 +61,9 @@ async function createSocialImage(layout: LayoutConfig, template: "" | "Wide"): P
if (!layout.icon.endsWith(".svg")) {
console.warn(
"Not creating a social image for " +
layout.id +
" as it is _not_ a .svg: " +
layout.icon,
layout.id +
" as it is _not_ a .svg: " +
layout.icon
)
return undefined
}
@ -85,7 +85,7 @@ async function createSocialImage(layout: LayoutConfig, template: "" | "Wide"): P
delete svg["defs"]
delete svg["$"]
let templateSvg = await ScriptUtils.ReadSvg(
"./public/assets/SocialImageTemplate" + template + ".svg",
"./public/assets/SocialImageTemplate" + template + ".svg"
)
templateSvg = Utils.WalkJson(
templateSvg,
@ -104,7 +104,7 @@ async function createSocialImage(layout: LayoutConfig, template: "" | "Wide"): P
return false
}
return mightBeTokenToReplace.circle[0]?.$?.style?.indexOf("fill:#ff00ff") >= 0
},
}
)
const builder = new xml2js.Builder()
@ -116,7 +116,7 @@ async function createSocialImage(layout: LayoutConfig, template: "" | "Wide"): P
async function createManifest(
layout: LayoutConfig,
alreadyWritten: string[],
alreadyWritten: string[]
): Promise<{
manifest: any
whiteIcons: string[]
@ -217,7 +217,11 @@ async function eliUrls(): Promise<string[]> {
}
const urls: string[] = []
const regex = /{switch:([^}]+)}/
const rasterLayers = [AvailableRasterLayers.maptilerDefaultLayer, ...eli.features, ...eli_global.layers.map(properties => ({ properties }))]
const rasterLayers = [
AvailableRasterLayers.maptilerDefaultLayer,
...eli.features,
...eli_global.layers.map((properties) => ({ properties })),
]
for (const feature of rasterLayers) {
const f = <RasterLayerPolygon>feature
const url = f.properties.url
@ -235,26 +239,24 @@ async function eliUrls(): Promise<string[]> {
const styleSpec = await Utils.downloadJsonCached(f.properties.url, 1000 * 120)
for (const key of Object.keys(styleSpec.sources)) {
const url = styleSpec.sources[key].url
if(!url){
if (!url) {
continue
}
let urlClipped = url
if(url.indexOf("?") > 0){
if (url.indexOf("?") > 0) {
urlClipped = url?.substring(0, url.indexOf("?"))
}
console.log("Source url ",key,url)
console.log("Source url ", key, url)
urls.push(url)
if(urlClipped.endsWith(".json")){
const tileInfo = await Utils.downloadJsonCached(url, 1000*120)
if (urlClipped.endsWith(".json")) {
const tileInfo = await Utils.downloadJsonCached(url, 1000 * 120)
urls.push(tileInfo["tiles"] ?? [])
}
}
urls.push(...(styleSpec["tiles"] ?? []))
urls.push(styleSpec["sprite"])
urls.push(styleSpec["glyphs"])
}
}
eliUrlsCached = urls
return Utils.NoNull(urls).sort()
@ -264,7 +266,7 @@ async function generateCsp(
layout: LayoutConfig,
options: {
scriptSrcs: string[]
},
}
): Promise<string> {
const apiUrls: string[] = [
"'self'",
@ -275,12 +277,12 @@ async function generateCsp(
"https://pietervdvn.goatcounter.com",
]
.concat(...SpecialVisualizations.specialVisualizations.map((sv) => sv.needsUrls))
.concat(...await eliUrls())
.concat(...(await eliUrls()))
const geojsonSources: string[] = layout.layers.map((l) => l.source?.geojsonSource)
const hosts = new Set<string>()
const eliLayers: RasterLayerPolygon[] = AvailableRasterLayers.layersAvailableAt(
new ImmutableStore({ lon: 0, lat: 0 }),
new ImmutableStore({ lon: 0, lat: 0 })
).data
const vectorLayers = eliLayers.filter((l) => l.properties.type === "vector")
const vectorSources = vectorLayers.map((l) => l.properties.url)
@ -307,14 +309,14 @@ async function generateCsp(
"connect-src items for theme",
layout.id,
"(extra sources: ",
newSrcs.join(" ") + ")",
newSrcs.join(" ") + ")"
)
previousSrc = hosts
const csp: Record<string, string> = {
"default-src": "'self'",
"script-src": ["'self'", "https://gc.zgo.at/count.js", ...(options?.scriptSrcs ?? [])].join(
" ",
" "
),
"img-src": "* data:", // maplibre depends on 'data:' to load
"connect-src": connectSrc.join(" "),
@ -344,12 +346,12 @@ const removeOtherLanguagesHash = crypto
async function createLandingPage(layout: LayoutConfig, manifest, whiteIcons, alreadyWritten) {
Locale.language.setData(layout.language[0])
const targetLanguage = layout.language[0]
const ogTitle = Translations.T(layout.title).textFor(targetLanguage).replace(/"/g, "\\\"")
const ogTitle = Translations.T(layout.title).textFor(targetLanguage).replace(/"/g, '\\"')
const ogDescr = Translations.T(
layout.shortDescription ?? "Easily add and edit geodata with OpenStreetMap",
layout.shortDescription ?? "Easily add and edit geodata with OpenStreetMap"
)
.textFor(targetLanguage)
.replace(/"/g, "\\\"")
.replace(/"/g, '\\"')
let ogImage = layout.socialImage
let twitterImage = ogImage
if (ogImage === LayoutConfig.defaultSocialImage && layout.official) {
@ -414,34 +416,34 @@ async function createLandingPage(layout: LayoutConfig, manifest, whiteIcons, alr
const loadingText = Translations.t.general.loadingTheme.Subs({ theme: layout.title })
const templateLines = template.split("\n")
const removeOtherLanguagesReference = templateLines.find(
(line) => line.indexOf("./src/UI/RemoveOtherLanguages.js") >= 0,
(line) => line.indexOf("./src/UI/RemoveOtherLanguages.js") >= 0
)
let output = template
.replace("Loading MapComplete, hang on...", asLangSpan(loadingText, "h1"))
.replace(
"Made with OpenStreetMap",
Translations.t.general.poweredByOsm.textFor(targetLanguage),
Translations.t.general.poweredByOsm.textFor(targetLanguage)
)
.replace(/<!-- THEME-SPECIFIC -->.*<!-- THEME-SPECIFIC-END-->/s, themeSpecific)
.replace(
/<!-- CSP -->/,
await generateCsp(layout, {
scriptSrcs: [`'sha256-${removeOtherLanguagesHash}'`],
}),
})
)
.replace(removeOtherLanguagesReference, "<script>" + removeOtherLanguages + "</script>")
.replace(
/<!-- DESCRIPTION START -->.*<!-- DESCRIPTION END -->/s,
asLangSpan(layout.shortDescription),
asLangSpan(layout.shortDescription)
)
.replace(
/<!-- IMAGE-START -->.*<!-- IMAGE-END -->/s,
"<img class='p-8 h-32 w-32 self-start' src='" + icon + "' />",
"<img class='p-8 h-32 w-32 self-start' src='" + icon + "' />"
)
.replace(
/.*\/src\/index\.ts.*/,
`<script type="module" src="./index_${layout.id}.ts"></script>`,
`<script type="module" src="./index_${layout.id}.ts"></script>`
)
.replace("Version", Constants.vNumber)
@ -533,7 +535,7 @@ async function main(): Promise<void> {
title: { en: "MapComplete" },
description: { en: "A thematic map viewer and editor based on OpenStreetMap" },
}),
alreadyWritten,
alreadyWritten
)
const manif = JSON.stringify(manifest, undefined, 2)

View file

@ -5,9 +5,10 @@ import { Utils } from "../../Utils"
import { Feature } from "geojson"
export default class PendingChangesUploader {
constructor(changes: Changes, selectedFeature: UIEventSource<Feature>) {
changes.pendingChanges.stabilized(Constants.updateTimeoutSec * 1000).addCallback(() => changes.flushChanges("Flushing changes due to timeout"))
changes.pendingChanges
.stabilized(Constants.updateTimeoutSec * 1000)
.addCallback(() => changes.flushChanges("Flushing changes due to timeout"))
selectedFeature.stabilized(1000).addCallback((feature) => {
if (feature === undefined) {

View file

@ -1,47 +1,47 @@
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig";
import { WritableFeatureSource } from "../FeatureSource";
import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource";
import { Feature, Point } from "geojson";
import { TagUtils } from "../../Tags/TagUtils";
import BaseUIElement from "../../../UI/BaseUIElement";
import { Utils } from "../../../Utils";
import { OsmTags } from "../../../Models/OsmFeature";
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"
import { WritableFeatureSource } from "../FeatureSource"
import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource"
import { Feature, Point } from "geojson"
import { TagUtils } from "../../Tags/TagUtils"
import BaseUIElement from "../../../UI/BaseUIElement"
import { Utils } from "../../../Utils"
import { OsmTags } from "../../../Models/OsmFeature"
/**
* Highly specialized feature source.
* Based on a lon/lat UIEVentSource, will generate the corresponding feature with the correct properties
*/
export class LastClickFeatureSource implements WritableFeatureSource {
public readonly features: UIEventSource<Feature[]> = new UIEventSource<Feature[]>([]);
public readonly hasNoteLayer: boolean;
public readonly renderings: string[];
public readonly hasPresets: boolean;
private i: number = 0;
public readonly features: UIEventSource<Feature[]> = new UIEventSource<Feature[]>([])
public readonly hasNoteLayer: boolean
public readonly renderings: string[]
public readonly hasPresets: boolean
private i: number = 0
constructor(location: Store<{ lon: number; lat: number }>, layout: LayoutConfig) {
this.hasNoteLayer = layout.layers.some((l) => l.id === "note");
this.hasPresets = layout.layers.some((l) => l.presets?.length > 0);
const allPresets: BaseUIElement[] = [];
this.hasNoteLayer = layout.layers.some((l) => l.id === "note")
this.hasPresets = layout.layers.some((l) => l.presets?.length > 0)
const allPresets: BaseUIElement[] = []
for (const layer of layout.layers)
for (let i = 0; i < (layer.presets ?? []).length; i++) {
const preset = layer.presets[i];
const tags = new ImmutableStore(TagUtils.KVtoProperties(preset.tags));
const preset = layer.presets[i]
const tags = new ImmutableStore(TagUtils.KVtoProperties(preset.tags))
const { html } = layer.mapRendering[0].RenderIcon(tags, false, {
noSize: true,
includeBadges: false
});
allPresets.push(html);
includeBadges: false,
})
allPresets.push(html)
}
this.renderings = Utils.Dedup(
allPresets.map((uiElem) =>
Utils.runningFromConsole ? "" : uiElem.ConstructElement().innerHTML
)
);
)
location.addCallbackAndRunD(({ lon, lat }) => {
this.features.setData([this.createFeature(lon, lat)]);
});
this.features.setData([this.createFeature(lon, lat)])
})
}
public createFeature(lon: number, lat: number): Feature<Point, OsmTags> {
@ -52,17 +52,17 @@ export class LastClickFeatureSource implements WritableFeatureSource {
has_presets: this.hasPresets ? "yes" : "no",
renderings: this.renderings.join(""),
number_of_presets: "" + this.renderings.length,
first_preset: this.renderings[0]
};
this.i++;
first_preset: this.renderings[0],
}
this.i++
return <Feature<Point, OsmTags>>{
type: "Feature",
properties,
geometry: {
type: "Point",
coordinates: [lon, lat]
}
};
coordinates: [lon, lat],
},
}
}
}

View file

@ -579,7 +579,7 @@ export class Changes {
)
const result = await self.flushSelectChanges(pendingChanges, openChangeset)
if(result){
if (result) {
this.errors.setData([])
}
return result

View file

@ -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<string, string>}) {
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(/&lt;/g,'<')?.replace(/&gt;/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'
}
}
public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) {
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(/&lt;/g, "<")?.replace(/&gt;/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"
}
}

View file

@ -6,7 +6,7 @@ import { AuthConfig } from "../Logic/Osm/AuthConfig"
export type PriviligedLayerType = (typeof Constants.priviliged_layers)[number]
export default class Constants {
public static vNumber : string = packagefile.version
public static vNumber: string = packagefile.version
/**
* API key for Maproulette
*

View file

@ -94,7 +94,7 @@ export default class LayoutConfig implements LayoutInformation {
}
const context = this.id
this.credits = json.credits
if(!json.title){
if (!json.title) {
throw `The theme ${json.id} does not have a title defined.`
}
this.language = json.mustHaveLanguage ?? Object.keys(json.title)

File diff suppressed because it is too large Load diff

View file

@ -12,28 +12,30 @@
let id = Math.random() * 1000000000 + ""
</script>
<form on:change|preventDefault={() => {
drawAttention = false
dispatcher("submit", inputElement.files)
}}
on:dragend={() => {
console.log("Drag end")
drawAttention = false
}}
on:dragenter|preventDefault|stopPropagation={(e) => {
console.log("Dragging enter")
drawAttention = true
e.dataTransfer.drop = "copy"
}}
on:dragstart={() => {
console.log("DragStart")
drawAttention = false
}}
on:drop|preventDefault|stopPropagation={(e) => {
console.log("Got a 'drop'")
drawAttention = false
dispatcher("submit", e.dataTransfer.files)
}}>
<form
on:change|preventDefault={() => {
drawAttention = false
dispatcher("submit", inputElement.files)
}}
on:dragend={() => {
console.log("Drag end")
drawAttention = false
}}
on:dragenter|preventDefault|stopPropagation={(e) => {
console.log("Dragging enter")
drawAttention = true
e.dataTransfer.drop = "copy"
}}
on:dragstart={() => {
console.log("DragStart")
drawAttention = false
}}
on:drop|preventDefault|stopPropagation={(e) => {
console.log("Got a 'drop'")
drawAttention = false
dispatcher("submit", e.dataTransfer.files)
}}
>
<label class={twMerge(cls, drawAttention ? "glowing-shadow" : "")} for={"fileinput" + id}>
<slot />
</label>
@ -44,7 +46,6 @@
id={"fileinput" + id}
{multiple}
name="file-input"
type="file"
/>
</form>

View file

@ -11,7 +11,9 @@
<div
class="absolute top-0 right-0 h-screen w-screen p-4 md:p-6"
style="background-color: #00000088"
on:click={() => {dispatch("close")}}
on:click={() => {
dispatch("close")
}}
>
<div class="content normal-background" on:click|stopPropagation={() => {}}>
<div class="h-full rounded-xl">

View file

@ -23,11 +23,11 @@ export default class Hotkeys {
>([])
private static textElementSelected(event: KeyboardEvent): boolean {
if(event.ctrlKey || event.altKey){
if (event.ctrlKey || event.altKey) {
// This is an event with a modifier-key, lets not ignore it
return false
}
if(event.key === "Escape"){
if (event.key === "Escape") {
return false // Another not-printable character that should not be ignored
}
return ["input", "textarea"].includes(document?.activeElement?.tagName?.toLowerCase())

View file

@ -1,32 +1,32 @@
<script lang="ts">
/**
* Thin wrapper around 'TabGroup' which binds the state
*/
/**
* Thin wrapper around 'TabGroup' which binds the state
*/
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@rgossiaux/svelte-headlessui";
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource";
import { twJoin } from "tailwind-merge";
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@rgossiaux/svelte-headlessui"
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
import { twJoin } from "tailwind-merge"
/**
* If a condition is given for a certain tab, it will only be shown if this condition is true.
* E.g.
* condition3 = new ImmutableStore(false) will always hide tab3 (the fourth tab)
*/
let tr = new ImmutableStore(true)
export let condition0: Store<boolean> = tr
export let condition1: Store<boolean> = tr
export let condition2: Store<boolean> = tr
export let condition3: Store<boolean> = tr
export let condition4: Store<boolean> = tr
export let tab: UIEventSource<number>;
let tabElements: HTMLElement[] = [];
$: tabElements[$tab]?.click();
$: {
if (tabElements[tab.data]) {
window.setTimeout(() => tabElements[tab.data].click(), 50);
}
/**
* If a condition is given for a certain tab, it will only be shown if this condition is true.
* E.g.
* condition3 = new ImmutableStore(false) will always hide tab3 (the fourth tab)
*/
let tr = new ImmutableStore(true)
export let condition0: Store<boolean> = tr
export let condition1: Store<boolean> = tr
export let condition2: Store<boolean> = tr
export let condition3: Store<boolean> = tr
export let condition4: Store<boolean> = tr
export let tab: UIEventSource<number>
let tabElements: HTMLElement[] = []
$: tabElements[$tab]?.click()
$: {
if (tabElements[tab.data]) {
window.setTimeout(() => tabElements[tab.data].click(), 50)
}
}
</script>
<div class="tabbedgroup flex h-full w-full">
@ -41,27 +41,37 @@
>
<div class="interactive sticky top-0 flex items-center justify-between">
<TabList class="flex flex-wrap">
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition0 && "hidden")}>
<Tab
class={({ selected }) => twJoin("tab", selected && "primary", !$condition0 && "hidden")}
>
<div bind:this={tabElements[0]} class="flex">
<slot name="title0">Tab 0</slot>
</div>
</Tab>
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition1 && "hidden")}>
<Tab
class={({ selected }) => twJoin("tab", selected && "primary", !$condition1 && "hidden")}
>
<div bind:this={tabElements[1]} class="flex">
<slot name="title1" />
</div>
</Tab>
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition2 && "hidden")}>
<Tab
class={({ selected }) => twJoin("tab", selected && "primary", !$condition2 && "hidden")}
>
<div bind:this={tabElements[2]} class="flex">
<slot name="title2" />
</div>
</Tab>
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition3 && "hidden")}>
<Tab
class={({ selected }) => twJoin("tab", selected && "primary", !$condition3 && "hidden")}
>
<div bind:this={tabElements[3]} class="flex">
<slot name="title3" />
</div>
</Tab>
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition4 && "hidden")}>
<Tab
class={({ selected }) => twJoin("tab", selected && "primary", !$condition4 && "hidden")}
>
<div bind:this={tabElements[4]} class="flex">
<slot name="title4" />
</div>
@ -102,44 +112,44 @@
</div>
<style>
.tabbedgroup {
max-height: 100vh;
height: 100%;
}
.tabbedgroup {
max-height: 100vh;
height: 100%;
}
:global(.tabpanel) {
height: 100%;
}
:global(.tabpanel) {
height: 100%;
}
:global(.tabpanels) {
height: calc(100% - 2rem);
}
:global(.tabpanels) {
height: calc(100% - 2rem);
}
:global(.tab) {
margin: 0.25rem;
padding: 0.25rem;
padding-left: 0.75rem;
padding-right: 0.75rem;
border-radius: 1rem;
}
:global(.tab) {
margin: 0.25rem;
padding: 0.25rem;
padding-left: 0.75rem;
padding-right: 0.75rem;
border-radius: 1rem;
}
:global(.tab .flex) {
align-items: center;
gap: 0.25rem;
}
:global(.tab .flex) {
align-items: center;
gap: 0.25rem;
}
:global(.tab span|div) {
align-items: center;
gap: 0.25rem;
display: flex;
}
:global(.tab span|div) {
align-items: center;
gap: 0.25rem;
display: flex;
}
:global(.tab-selected svg) {
fill: var(--catch-detail-color-contrast);
}
:global(.tab-selected svg) {
fill: var(--catch-detail-color-contrast);
}
:global(.tab-unselected) {
background-color: var(--background-color) !important;
color: var(--foreground-color) !important;
}
:global(.tab-unselected) {
background-color: var(--background-color) !important;
color: var(--foreground-color) !important;
}
</style>

View file

@ -38,7 +38,7 @@
if (value.data === undefined) {
value.setData(coordinate)
}
if(coordinate === undefined){
if (coordinate === undefined) {
coordinate = value.data
}
export let snapToLayers: string[] | undefined
@ -47,8 +47,6 @@
export let snappedTo: UIEventSource<string | undefined>
let preciseLocation: UIEventSource<{ lon: number; lat: number }> = new UIEventSource<{
lon: number
lat: number
@ -75,7 +73,7 @@
rasterLayer: UIEventSource.feedFrom(state.mapProperties.rasterLayer),
}
if(targetLayer){
if (targetLayer) {
const featuresForLayer = state.perLayer.get(targetLayer.id)
if (featuresForLayer) {
new ShowDataLayer(map, {

View file

@ -1,21 +1,23 @@
<script lang="ts">
import type { SpecialVisualizationState } from "../SpecialVisualization"
import { Store } from "../../Logic/UIEventSource"
import { Changes } from "../../Logic/Osm/Changes"
import Loading from "../Base/Loading.svelte"
import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import { Store } from "../../Logic/UIEventSource"
import { Changes } from "../../Logic/Osm/Changes"
import Loading from "../Base/Loading.svelte"
import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte"
export let state: SpecialVisualizationState
export let state: SpecialVisualizationState
const changes: Changes = state.changes
const isUploading: Store<boolean> = changes.isUploading
const pendingChangesCount: Store<number> = changes.pendingChanges.map(ls => ls.length)
const errors = changes.errors
const changes: Changes = state.changes
const isUploading: Store<boolean> = changes.isUploading
const pendingChangesCount: Store<number> = changes.pendingChanges.map((ls) => ls.length)
const errors = changes.errors
</script>
<div class="flex flex-col pointer-events-auto" on:click={() => changes.flushChanges("Pending changes indicator clicked")}>
<div
class="pointer-events-auto flex flex-col"
on:click={() => changes.flushChanges("Pending changes indicator clicked")}
>
{#if $isUploading}
<Loading>
<Tr cls="thx" t={Translations.t.general.uploadingChanges} />
@ -23,10 +25,13 @@
{:else if $pendingChangesCount === 1}
<Tr cls="alert" t={Translations.t.general.uploadPendingSingle} />
{:else if $pendingChangesCount > 1}
<Tr cls="alert" t={Translations.t.general.uploadPending.Subs({count: $pendingChangesCount})} />
<Tr
cls="alert"
t={Translations.t.general.uploadPending.Subs({ count: $pendingChangesCount })}
/>
{/if}
{#each $errors as error}
<Tr cls="alert" t={Translations.t.general.uploadError.Subs({error})} />
<Tr cls="alert" t={Translations.t.general.uploadError.Subs({ error })} />
{/each}
</div>

View file

@ -1,48 +1,48 @@
<script lang="ts">
import Translations from "../i18n/Translations";
import Svg from "../../Svg";
import Tr from "../Base/Tr.svelte";
import NextButton from "../Base/NextButton.svelte";
import Geosearch from "./Geosearch.svelte";
import ToSvelte from "../Base/ToSvelte.svelte";
import ThemeViewState from "../../Models/ThemeViewState";
import { Store, UIEventSource } from "../../Logic/UIEventSource";
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid";
import { twJoin } from "tailwind-merge";
import { Utils } from "../../Utils";
import type { GeolocationPermissionState } from "../../Logic/State/GeoLocationState";
import If from "../Base/If.svelte";
import Translations from "../i18n/Translations"
import Svg from "../../Svg"
import Tr from "../Base/Tr.svelte"
import NextButton from "../Base/NextButton.svelte"
import Geosearch from "./Geosearch.svelte"
import ToSvelte from "../Base/ToSvelte.svelte"
import ThemeViewState from "../../Models/ThemeViewState"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid"
import { twJoin } from "tailwind-merge"
import { Utils } from "../../Utils"
import type { GeolocationPermissionState } from "../../Logic/State/GeoLocationState"
import If from "../Base/If.svelte"
/**
* The theme introduction panel
*/
export let state: ThemeViewState;
let layout = state.layout;
let selectedElement = state.selectedElement;
let selectedLayer = state.selectedLayer;
/**
* The theme introduction panel
*/
export let state: ThemeViewState
let layout = state.layout
let selectedElement = state.selectedElement
let selectedLayer = state.selectedLayer
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined);
let searchEnabled = false;
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
let searchEnabled = false
let geopermission: Store<GeolocationPermissionState> =
state.geolocation.geolocationState.permission;
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation;
let geopermission: Store<GeolocationPermissionState> =
state.geolocation.geolocationState.permission
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation
geopermission.addCallback((perm) => console.log(">>>> Permission", perm));
geopermission.addCallback((perm) => console.log(">>>> Permission", perm))
function jumpToCurrentLocation() {
const glstate = state.geolocation.geolocationState;
if (glstate.currentGPSLocation.data !== undefined) {
const c: GeolocationCoordinates = glstate.currentGPSLocation.data;
state.guistate.themeIsOpened.setData(false);
const coor = { lon: c.longitude, lat: c.latitude };
state.mapProperties.location.setData(coor);
}
if (glstate.permission.data !== "granted") {
glstate.requestPermission();
return;
}
function jumpToCurrentLocation() {
const glstate = state.geolocation.geolocationState
if (glstate.currentGPSLocation.data !== undefined) {
const c: GeolocationCoordinates = glstate.currentGPSLocation.data
state.guistate.themeIsOpened.setData(false)
const coor = { lon: c.longitude, lat: c.latitude }
state.mapProperties.location.setData(coor)
}
if (glstate.permission.data !== "granted") {
glstate.requestPermission()
return
}
}
</script>
<div class="flex h-full flex-col justify-between">
@ -63,7 +63,6 @@
</div>
</NextButton>
<div class="flex w-full flex-wrap sm:flex-nowrap">
<If condition={state.featureSwitches.featureSwitchGeolocation}>
{#if $currentGPSLocation !== undefined || $geopermission === "prompt"}
@ -73,12 +72,15 @@
</button>
<!-- No geolocation granted - we don't show the button -->
{:else if $geopermission === "requested"}
<button class="disabled flex w-full items-center gap-x-2" on:click={jumpToCurrentLocation}>
<button
class="disabled flex w-full items-center gap-x-2"
on:click={jumpToCurrentLocation}
>
<!-- Even though disabled, when clicking we request the location again in case the contributor dismissed the location popup -->
<ToSvelte
construct={Svg.crosshair_svg()
.SetClass("w-8 h-8")
.SetStyle("animation: 3s linear 0s infinite normal none running spin;")}
.SetClass("w-8 h-8")
.SetStyle("animation: 3s linear 0s infinite normal none running spin;")}
/>
<Tr t={Translations.t.general.waitingForGeopermission} />
</button>
@ -91,24 +93,25 @@
<button class="disabled flex w-full items-center gap-x-2">
<ToSvelte
construct={Svg.crosshair_svg()
.SetClass("w-8 h-8")
.SetStyle("animation: 3s linear 0s infinite normal none running spin;")}
.SetClass("w-8 h-8")
.SetStyle("animation: 3s linear 0s infinite normal none running spin;")}
/>
<Tr t={Translations.t.general.waitingForLocation} />
</button>
{/if}
</If>
<If condition={state.featureSwitches.featureSwitchSearch}>
<div class=".button low-interaction m-1 flex w-full items-center gap-x-2 rounded border p-2">
<div
class=".button low-interaction m-1 flex w-full items-center gap-x-2 rounded border p-2"
>
<div class="w-full">
<Geosearch
bounds={state.mapProperties.bounds}
on:searchCompleted={() => state.guistate.themeIsOpened.setData(false)}
on:searchIsValid={(isValid) => {
searchEnabled = isValid
}}
searchEnabled = isValid
}}
perLayer={state.perLayer}
{selectedElement}
{selectedLayer}
@ -116,7 +119,10 @@
/>
</div>
<button
class={twJoin("flex items-center justify-between gap-x-2", !searchEnabled && "disabled")}
class={twJoin(
"flex items-center justify-between gap-x-2",
!searchEnabled && "disabled"
)}
on:click={() => triggerSearch.ping()}
>
<Tr t={Translations.t.general.search.searchShort} />

View file

@ -1,24 +1,24 @@
<script lang="ts">
/**
* Shows information about how much images are uploaded for the given feature
*
* Either pass in a store with tags or a featureId.
*/
/**
* Shows information about how much images are uploaded for the given feature
*
* Either pass in a store with tags or a featureId.
*/
import type { SpecialVisualizationState } from "../SpecialVisualization"
import { Store } from "../../Logic/UIEventSource"
import type { OsmTags } from "../../Models/OsmFeature"
import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte"
import Loading from "../Base/Loading.svelte"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import { Store } from "../../Logic/UIEventSource"
import type { OsmTags } from "../../Models/OsmFeature"
import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte"
import Loading from "../Base/Loading.svelte"
export let state: SpecialVisualizationState
export let tags: Store<OsmTags>
export let featureId = tags.data.id
export let showThankYou: boolean = true
const { uploadStarted, uploadFinished, retried, failed } =
state.imageUploadManager.getCountsFor(featureId)
const t = Translations.t.image
export let state: SpecialVisualizationState
export let tags: Store<OsmTags>
export let featureId = tags.data.id
export let showThankYou: boolean = true
const { uploadStarted, uploadFinished, retried, failed } =
state.imageUploadManager.getCountsFor(featureId)
const t = Translations.t.image
</script>
{#if $uploadStarted === 1}

View file

@ -1,14 +1,14 @@
import { Store, UIEventSource } from "../../Logic/UIEventSource";
import type { Map as MLMap } from "maplibre-gl";
import { Map as MlMap, SourceSpecification } from "maplibre-gl";
import { AvailableRasterLayers, RasterLayerPolygon } from "../../Models/RasterLayers";
import { Utils } from "../../Utils";
import { BBox } from "../../Logic/BBox";
import { ExportableMap, MapProperties } from "../../Models/MapProperties";
import SvelteUIElement from "../Base/SvelteUIElement";
import MaplibreMap from "./MaplibreMap.svelte";
import { RasterLayerProperties } from "../../Models/RasterLayerProperties";
import * as htmltoimage from "html-to-image";
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import type { Map as MLMap } from "maplibre-gl"
import { Map as MlMap, SourceSpecification } from "maplibre-gl"
import { AvailableRasterLayers, RasterLayerPolygon } from "../../Models/RasterLayers"
import { Utils } from "../../Utils"
import { BBox } from "../../Logic/BBox"
import { ExportableMap, MapProperties } from "../../Models/MapProperties"
import SvelteUIElement from "../Base/SvelteUIElement"
import MaplibreMap from "./MaplibreMap.svelte"
import { RasterLayerProperties } from "../../Models/RasterLayerProperties"
import * as htmltoimage from "html-to-image"
/**
* The 'MapLibreAdaptor' bridges 'MapLibre' with the various properties of the `MapProperties`

View file

@ -16,7 +16,7 @@ import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter"
import FilteredLayer from "../../Models/FilteredLayer"
import SimpleFeatureSource from "../../Logic/FeatureSource/Sources/SimpleFeatureSource"
import { CLIENT_RENEG_LIMIT } from "tls";
import { CLIENT_RENEG_LIMIT } from "tls"
class PointRenderingLayer {
private readonly _config: PointRenderingConfig
@ -409,7 +409,7 @@ class LineRenderingLayer {
this._listenerInstalledOn.add(id)
tags.addCallbackAndRunD((properties) => {
// Make sure to use 'getSource' here, the layer names are different!
if(map.getSource(this._layername) === undefined){
if (map.getSource(this._layername) === undefined) {
return true
}
map.setFeatureState(

View file

@ -162,16 +162,16 @@
<LoginButton osmConnection={state.osmConnection} slot="not-logged-in">
<Tr slot="message" t={Translations.t.general.add.pleaseLogin} />
</LoginButton>
{#if $zoom < Constants.minZoomLevelToAddNewPoint}
{#if $zoom < Constants.minZoomLevelToAddNewPoint}
<div class="alert">
<Tr t={Translations.t.general.add.zoomInFurther} />
</div>
{:else if $isLoading}
<div class="alert">
<Loading>
<Tr t={Translations.t.general.add.stillLoading} />
</Loading>
</div>
{:else if $isLoading}
<div class="alert">
<Loading>
<Tr t={Translations.t.general.add.stillLoading} />
</Loading>
</div>
{:else if selectedPreset === undefined}
<!-- First, select the correct preset -->
<PresetList

View file

@ -30,7 +30,12 @@
if (flayer.isDisplayed.data === false) {
// The layer is not displayed...
if (!state.featureSwitches.featureSwitchFilter.data) {
console.log("Not showing presets for layer", flayer.layerDef.id, "as not displayed and featureSwitchFilter.data is set",state.featureSwitches.featureSwitchFilter.data)
console.log(
"Not showing presets for layer",
flayer.layerDef.id,
"as not displayed and featureSwitchFilter.data is set",
state.featureSwitches.featureSwitchFilter.data
)
// ...and we cannot enable the layer control -> we skip, as these presets can never be shown anyway
continue
}

View file

@ -2,50 +2,50 @@
/**
* UIcomponent to create a new note at the given location
*/
import type { SpecialVisualizationState } from "../SpecialVisualization";
import { UIEventSource } from "../../Logic/UIEventSource";
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource";
import ValidatedInput from "../InputElement/ValidatedInput.svelte";
import SubtleButton from "../Base/SubtleButton.svelte";
import Tr from "../Base/Tr.svelte";
import Translations from "../i18n/Translations.js";
import type { Feature, Point } from "geojson";
import LoginToggle from "../Base/LoginToggle.svelte";
import FilteredLayer from "../../Models/FilteredLayer";
import NewPointLocationInput from "../BigComponents/NewPointLocationInput.svelte";
import ToSvelte from "../Base/ToSvelte.svelte";
import Svg from "../../Svg";
import type { SpecialVisualizationState } from "../SpecialVisualization"
import { UIEventSource } from "../../Logic/UIEventSource"
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"
import ValidatedInput from "../InputElement/ValidatedInput.svelte"
import SubtleButton from "../Base/SubtleButton.svelte"
import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations.js"
import type { Feature, Point } from "geojson"
import LoginToggle from "../Base/LoginToggle.svelte"
import FilteredLayer from "../../Models/FilteredLayer"
import NewPointLocationInput from "../BigComponents/NewPointLocationInput.svelte"
import ToSvelte from "../Base/ToSvelte.svelte"
import Svg from "../../Svg"
export let coordinate: UIEventSource<{ lon: number; lat: number }>;
export let state: SpecialVisualizationState;
export let coordinate: UIEventSource<{ lon: number; lat: number }>
export let state: SpecialVisualizationState
let comment: UIEventSource<string> = LocalStorageSource.Get("note-text");
let created = false;
let comment: UIEventSource<string> = LocalStorageSource.Get("note-text")
let created = false
let notelayer: FilteredLayer = state.layerState.filteredLayers.get("note");
let notelayer: FilteredLayer = state.layerState.filteredLayers.get("note")
let hasFilter = notelayer?.hasFilter;
let isDisplayed = notelayer?.isDisplayed;
let hasFilter = notelayer?.hasFilter
let isDisplayed = notelayer?.isDisplayed
function enableNoteLayer() {
state.guistate.closeAll();
isDisplayed.setData(true);
state.guistate.closeAll()
isDisplayed.setData(true)
}
async function uploadNote() {
let txt = comment.data;
let txt = comment.data
if (txt === undefined || txt === "") {
return;
return
}
const loc = coordinate.data;
txt += "\n\n #MapComplete #" + state?.layout?.id;
const id = await state?.osmConnection?.openNote(loc.lat, loc.lon, txt);
console.log("Created a note, got id", id);
const loc = coordinate.data
txt += "\n\n #MapComplete #" + state?.layout?.id
const id = await state?.osmConnection?.openNote(loc.lat, loc.lon, txt)
console.log("Created a note, got id", id)
const feature = <Feature<Point>>{
type: "Feature",
geometry: {
type: "Point",
coordinates: [loc.lon, loc.lat]
coordinates: [loc.lon, loc.lat],
},
properties: {
id: "" + id.id,
@ -56,22 +56,22 @@
text: txt,
html: txt,
user: state.osmConnection?.userDetails?.data?.name,
uid: state.osmConnection?.userDetails?.data?.uid
}
])
}
};
// Normally, the 'Changes' will generate the new element. The 'notes' are an exception to this
state.newFeatures.features.data.push(feature);
state.newFeatures.features.ping();
state.selectedElement?.setData(feature);
if (state.featureProperties.trackFeature) {
state.featureProperties.trackFeature(feature);
uid: state.osmConnection?.userDetails?.data?.uid,
},
]),
},
}
comment.setData("");
created = true;
state.selectedElement.setData(feature);
state.selectedLayer.setData(state.layerState.filteredLayers.get("note"));
// Normally, the 'Changes' will generate the new element. The 'notes' are an exception to this
state.newFeatures.features.data.push(feature)
state.newFeatures.features.ping()
state.selectedElement?.setData(feature)
if (state.featureProperties.trackFeature) {
state.featureProperties.trackFeature(feature)
}
comment.setData("")
created = true
state.selectedElement.setData(feature)
state.selectedLayer.setData(state.layerState.filteredLayers.get("note"))
}
</script>
@ -109,15 +109,14 @@
<ValidatedInput type="text" value={comment} />
</div>
<div class="w-full h-56">
<NewPointLocationInput value={coordinate} {state} >
<div class="h-56 w-full">
<NewPointLocationInput value={coordinate} {state}>
<div class="h-20 w-full pb-10" slot="image">
<ToSvelte construct={Svg.note_svg().SetClass("h-10 w-full")}/>
<ToSvelte construct={Svg.note_svg().SetClass("h-10 w-full")} />
</div>
</NewPointLocationInput>
</div>
<LoginToggle {state}>
<span slot="loading"><!--empty: don't show a loading message--></span>
<div slot="not-logged-in" class="alert">

View file

@ -112,7 +112,10 @@
<button
slot="save-button"
on:click={onDelete}
class={twJoin(selectedTags === undefined && "disabled", "primary flex bg-red-600 items-center")}
class={twJoin(
selectedTags === undefined && "disabled",
"primary flex items-center bg-red-600"
)}
>
<TrashIcon
class={twJoin(

View file

@ -1,116 +1,116 @@
<script lang="ts">
import { Store, UIEventSource } from "../Logic/UIEventSource";
import { Map as MlMap } from "maplibre-gl";
import MaplibreMap from "./Map/MaplibreMap.svelte";
import FeatureSwitchState from "../Logic/State/FeatureSwitchState";
import MapControlButton from "./Base/MapControlButton.svelte";
import ToSvelte from "./Base/ToSvelte.svelte";
import If from "./Base/If.svelte";
import { GeolocationControl } from "./BigComponents/GeolocationControl";
import type { Feature } from "geojson";
import SelectedElementView from "./BigComponents/SelectedElementView.svelte";
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
import Filterview from "./BigComponents/Filterview.svelte";
import ThemeViewState from "../Models/ThemeViewState";
import type { MapProperties } from "../Models/MapProperties";
import Geosearch from "./BigComponents/Geosearch.svelte";
import Translations from "./i18n/Translations";
import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
import { Store, UIEventSource } from "../Logic/UIEventSource"
import { Map as MlMap } from "maplibre-gl"
import MaplibreMap from "./Map/MaplibreMap.svelte"
import FeatureSwitchState from "../Logic/State/FeatureSwitchState"
import MapControlButton from "./Base/MapControlButton.svelte"
import ToSvelte from "./Base/ToSvelte.svelte"
import If from "./Base/If.svelte"
import { GeolocationControl } from "./BigComponents/GeolocationControl"
import type { Feature } from "geojson"
import SelectedElementView from "./BigComponents/SelectedElementView.svelte"
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import Filterview from "./BigComponents/Filterview.svelte"
import ThemeViewState from "../Models/ThemeViewState"
import type { MapProperties } from "../Models/MapProperties"
import Geosearch from "./BigComponents/Geosearch.svelte"
import Translations from "./i18n/Translations"
import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
import Tr from "./Base/Tr.svelte";
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte";
import FloatOver from "./Base/FloatOver.svelte";
import PrivacyPolicy from "./BigComponents/PrivacyPolicy";
import Constants from "../Models/Constants";
import TabbedGroup from "./Base/TabbedGroup.svelte";
import UserRelatedState from "../Logic/State/UserRelatedState";
import LoginToggle from "./Base/LoginToggle.svelte";
import LoginButton from "./Base/LoginButton.svelte";
import CopyrightPanel from "./BigComponents/CopyrightPanel";
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte";
import ModalRight from "./Base/ModalRight.svelte";
import { Utils } from "../Utils";
import Hotkeys from "./Base/Hotkeys";
import { VariableUiElement } from "./Base/VariableUIElement";
import SvelteUIElement from "./Base/SvelteUIElement";
import OverlayToggle from "./BigComponents/OverlayToggle.svelte";
import LevelSelector from "./BigComponents/LevelSelector.svelte";
import ExtraLinkButton from "./BigComponents/ExtraLinkButton";
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte";
import Svg from "../Svg";
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte";
import type { RasterLayerPolygon } from "../Models/RasterLayers";
import { AvailableRasterLayers } from "../Models/RasterLayers";
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte";
import IfHidden from "./Base/IfHidden.svelte";
import { onDestroy } from "svelte";
import { OpenJosm } from "./BigComponents/OpenJosm";
import MapillaryLink from "./BigComponents/MapillaryLink.svelte";
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte";
import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte";
import StateIndicator from "./BigComponents/StateIndicator.svelte";
import LanguagePicker from "./LanguagePicker";
import Locale from "./i18n/Locale";
import ShareScreen from "./BigComponents/ShareScreen.svelte";
import UploadingImageCounter from "./Image/UploadingImageCounter.svelte"
import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"
import Tr from "./Base/Tr.svelte"
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
import FloatOver from "./Base/FloatOver.svelte"
import PrivacyPolicy from "./BigComponents/PrivacyPolicy"
import Constants from "../Models/Constants"
import TabbedGroup from "./Base/TabbedGroup.svelte"
import UserRelatedState from "../Logic/State/UserRelatedState"
import LoginToggle from "./Base/LoginToggle.svelte"
import LoginButton from "./Base/LoginButton.svelte"
import CopyrightPanel from "./BigComponents/CopyrightPanel"
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte"
import ModalRight from "./Base/ModalRight.svelte"
import { Utils } from "../Utils"
import Hotkeys from "./Base/Hotkeys"
import { VariableUiElement } from "./Base/VariableUIElement"
import SvelteUIElement from "./Base/SvelteUIElement"
import OverlayToggle from "./BigComponents/OverlayToggle.svelte"
import LevelSelector from "./BigComponents/LevelSelector.svelte"
import ExtraLinkButton from "./BigComponents/ExtraLinkButton"
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte"
import Svg from "../Svg"
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte"
import type { RasterLayerPolygon } from "../Models/RasterLayers"
import { AvailableRasterLayers } from "../Models/RasterLayers"
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte"
import IfHidden from "./Base/IfHidden.svelte"
import { onDestroy } from "svelte"
import { OpenJosm } from "./BigComponents/OpenJosm"
import MapillaryLink from "./BigComponents/MapillaryLink.svelte"
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"
import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte"
import StateIndicator from "./BigComponents/StateIndicator.svelte"
import LanguagePicker from "./LanguagePicker"
import Locale from "./i18n/Locale"
import ShareScreen from "./BigComponents/ShareScreen.svelte"
import UploadingImageCounter from "./Image/UploadingImageCounter.svelte"
import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"
export let state: ThemeViewState;
let layout = state.layout;
export let state: ThemeViewState
let layout = state.layout
let maplibremap: UIEventSource<MlMap> = state.map;
let selectedElement: UIEventSource<Feature> = state.selectedElement;
let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer;
let maplibremap: UIEventSource<MlMap> = state.map
let selectedElement: UIEventSource<Feature> = state.selectedElement
let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer
const selectedElementView = selectedElement.map(
(selectedElement) => {
// Svelte doesn't properly reload some of the legacy UI-elements
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
const layer = selectedLayer.data;
if (selectedElement === undefined || layer === undefined) {
return undefined;
}
const selectedElementView = selectedElement.map(
(selectedElement) => {
// Svelte doesn't properly reload some of the legacy UI-elements
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
const layer = selectedLayer.data
if (selectedElement === undefined || layer === undefined) {
return undefined
}
if (!(layer.tagRenderings?.length > 0) || layer.title === undefined) {
return undefined;
}
if (!(layer.tagRenderings?.length > 0) || layer.title === undefined) {
return undefined
}
const tags = state.featureProperties.getStore(selectedElement.properties.id);
return new SvelteUIElement(SelectedElementView, { state, layer, selectedElement, tags });
},
[selectedLayer]
);
const tags = state.featureProperties.getStore(selectedElement.properties.id)
return new SvelteUIElement(SelectedElementView, { state, layer, selectedElement, tags })
},
[selectedLayer]
)
const selectedElementTitle = selectedElement.map(
(selectedElement) => {
// Svelte doesn't properly reload some of the legacy UI-elements
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
const layer = selectedLayer.data;
if (selectedElement === undefined || layer === undefined) {
return undefined;
}
const selectedElementTitle = selectedElement.map(
(selectedElement) => {
// Svelte doesn't properly reload some of the legacy UI-elements
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
const layer = selectedLayer.data
if (selectedElement === undefined || layer === undefined) {
return undefined
}
const tags = state.featureProperties.getStore(selectedElement.properties.id);
return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags });
},
[selectedLayer]
);
const tags = state.featureProperties.getStore(selectedElement.properties.id)
return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags })
},
[selectedLayer]
)
let mapproperties: MapProperties = state.mapProperties;
let featureSwitches: FeatureSwitchState = state.featureSwitches;
let availableLayers = state.availableLayers;
let userdetails = state.osmConnection.userDetails;
let currentViewLayer = layout.layers.find((l) => l.id === "current_view");
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer;
let rasterLayerName =
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name;
onDestroy(
rasterLayer.addCallbackAndRunD((l) => {
rasterLayerName = l.properties.name;
})
);
let mapproperties: MapProperties = state.mapProperties
let featureSwitches: FeatureSwitchState = state.featureSwitches
let availableLayers = state.availableLayers
let userdetails = state.osmConnection.userDetails
let currentViewLayer = layout.layers.find((l) => l.id === "current_view")
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
let rasterLayerName =
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name
onDestroy(
rasterLayer.addCallbackAndRunD((l) => {
rasterLayerName = l.properties.name
})
)
</script>
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden">
@ -156,8 +156,8 @@
<ToSvelte
construct={() => new ExtraLinkButton(state, layout.extraLink).SetClass("pointer-events-auto")}
/>
<UploadingImageCounter {state} featureId="*" showThankYou={false}/>
<PendingChangesIndicator {state}/>
<UploadingImageCounter {state} featureId="*" showThankYou={false} />
<PendingChangesIndicator {state} />
<If condition={state.featureSwitchIsTesting}>
<div class="alert w-fit">Testmode</div>
</If>
@ -175,7 +175,12 @@
<div class="flex flex-col">
<If condition={featureSwitches.featureSwitchEnableLogin}>
{#if state.lastClickObject.hasPresets || state.lastClickObject.hasNoteLayer}
<button class="w-fit pointer-events-auto" on:click={() => {state.openNewDialog()}}>
<button
class="pointer-events-auto w-fit"
on:click={() => {
state.openNewDialog()
}}
>
{#if state.lastClickObject.hasPresets}
<Tr t={Translations.t.general.add.title} />
{:else}
@ -198,9 +203,9 @@
<a
class="bg-black-transparent pointer-events-auto h-fit max-h-12 cursor-pointer self-end overflow-hidden rounded-2xl pl-1 pr-2 text-white opacity-50 hover:opacity-100"
on:click={() => {
state.guistate.themeViewTab.setData("copyright")
state.guistate.themeIsOpened.setData(true)
}}
state.guistate.themeViewTab.setData("copyright")
state.guistate.themeIsOpened.setData(true)
}}
>
© OpenStreetMap, <span class="w-24">{rasterLayerName}</span>
</a>
@ -282,7 +287,10 @@
<!-- Theme menu -->
<FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}>
<span slot="close-button"><!-- Disable the close button --></span>
<TabbedGroup condition1={state.featureSwitches.featureSwitchFilter} tab={state.guistate.themeViewTabIndex}>
<TabbedGroup
condition1={state.featureSwitches.featureSwitchFilter}
tab={state.guistate.themeViewTabIndex}
>
<div slot="post-tablist">
<XCircleIcon
class="mr-2 h-8 w-8"
@ -351,7 +359,11 @@
<IfHidden condition={state.guistate.backgroundLayerSelectionIsOpened}>
<!-- background layer selector -->
<FloatOver on:close={() => {state.guistate.backgroundLayerSelectionIsOpened.setData(false)}}>
<FloatOver
on:close={() => {
state.guistate.backgroundLayerSelectionIsOpened.setData(false)
}}
>
<div class="h-full p-2">
<RasterLayerOverview
{availableLayers}
@ -366,10 +378,13 @@
<If condition={state.guistate.menuIsOpened}>
<!-- Menu page -->
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false) }>
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}>
<span slot="close-button"><!-- Hide the default close button --></span>
<TabbedGroup condition1={featureSwitches.featureSwitchEnableLogin} condition2={state.featureSwitches. featureSwitchCommunityIndex}
tab={state.guistate.menuViewTabIndex}>
<TabbedGroup
condition1={featureSwitches.featureSwitchEnableLogin}
condition2={state.featureSwitches.featureSwitchCommunityIndex}
tab={state.guistate.menuViewTabIndex}
>
<div slot="post-tablist">
<XCircleIcon
class="mr-2 h-8 w-8"
@ -458,7 +473,7 @@
<OpenIdEditor mapProperties={state.mapProperties} />
<ToSvelte
construct={() =>
new OpenJosm(state.osmConnection, state.mapProperties.bounds).SetClass("w-full")}
new OpenJosm(state.osmConnection, state.mapProperties.bounds).SetClass("w-full")}
/>
<MapillaryLink mapProperties={state.mapProperties} />
</If>

View file

@ -1,7 +1,7 @@
{
"contributors": [
{
"commits": 6092,
"commits": 6139,
"contributor": "Pieter Vander Vennet"
},
{
@ -30,14 +30,14 @@
},
{
"commits": 30,
"contributor": "paunofu"
},
{
"commits": 29,
"contributor": "Hosted Weblate"
},
{
"commits": 27,
"commits": 30,
"contributor": "paunofu"
},
{
"commits": 28,
"contributor": "riQQ"
},
{
@ -53,7 +53,7 @@
"contributor": "Ward"
},
{
"commits": 21,
"commits": 22,
"contributor": "dependabot[bot]"
},
{

View file

@ -30,10 +30,6 @@
"AT": [
"de"
],
"AU": [
"en",
"en"
],
"AZ": [
"az"
],

View file

@ -3009,7 +3009,6 @@
"_meta": {
"countries": [
"AG",
"AU",
"BB",
"BI",
"BN",

View file

@ -1,11 +1,11 @@
{
"contributors": [
{
"commits": 311,
"commits": 314,
"contributor": "kjon"
},
{
"commits": 288,
"commits": 292,
"contributor": "Pieter Vander Vennet"
},
{
@ -96,6 +96,10 @@
"commits": 13,
"contributor": "Joost"
},
{
"commits": 11,
"contributor": "Jaime Marquínez Ferrándiz"
},
{
"commits": 11,
"contributor": "Túllio Franca"
@ -124,6 +128,10 @@
"commits": 10,
"contributor": "Irina"
},
{
"commits": 9,
"contributor": "Piotr Strebski"
},
{
"commits": 9,
"contributor": "Ettore Atalan"
@ -132,10 +140,6 @@
"commits": 9,
"contributor": "deep map"
},
{
"commits": 9,
"contributor": "Jaime Marquínez Ferrándiz"
},
{
"commits": 9,
"contributor": "Fjuro"
@ -152,6 +156,10 @@
"commits": 8,
"contributor": "Vinicius"
},
{
"commits": 7,
"contributor": "gallegonovato"
},
{
"commits": 7,
"contributor": "NetworkedPoncho"
@ -172,6 +180,10 @@
"commits": 7,
"contributor": "Niels Elgaard Larsen"
},
{
"commits": 6,
"contributor": "macpac"
},
{
"commits": 6,
"contributor": "Juele juele"
@ -216,14 +228,6 @@
"commits": 6,
"contributor": "lvgx"
},
{
"commits": 5,
"contributor": "Piotr Strebski"
},
{
"commits": 5,
"contributor": "gallegonovato"
},
{
"commits": 5,
"contributor": "ⵣⵓⵀⵉⵔ ⴰⵎⴰⵣⵉⵖ ZOUHIR DEHBI"
@ -348,10 +352,6 @@
"commits": 3,
"contributor": "SiegbjornSitumeang"
},
{
"commits": 2,
"contributor": "macpac"
},
{
"commits": 2,
"contributor": "Peter Brodersen"
@ -448,6 +448,10 @@
"commits": 2,
"contributor": "Leo Alcaraz"
},
{
"commits": 1,
"contributor": "Julio Salas"
},
{
"commits": 1,
"contributor": "Michal Čermák"