Merge master
This commit is contained in:
commit
89a0be8903
150 changed files with 4201 additions and 9581 deletions
14
Docs/ServerConfig/cache/Caddyfile
vendored
Normal file
14
Docs/ServerConfig/cache/Caddyfile
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
cache.mapcomplete.org {
|
||||
reverse_proxy /summary/* {
|
||||
to http://127.0.0.1:2345
|
||||
}
|
||||
|
||||
reverse_proxy /extractgraph {
|
||||
to http://127.0.0.1:2346
|
||||
}
|
||||
|
||||
reverse_proxy /* {
|
||||
to http://127.0.0.1:7800
|
||||
}
|
||||
|
||||
}
|
15
Docs/ServerConfig/cache/cache.txt
vendored
Normal file
15
Docs/ServerConfig/cache/cache.txt
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Cache.mapComplete.org server config
|
||||
|
||||
The "cache"-server is hosted at nerdlab.
|
||||
|
||||
It has a full OSM-copy on disk, and has a Postgis/Postgres database with a table for every layer for quick access. It should run the tileserver and the summaryserver
|
||||
|
||||
## Dyndns
|
||||
|
||||
https://dynamicdns.park-your-domain.com/update?host=cache&domain=mapcomplete.org&password=[ddns_password]
|
||||
|
||||
## Setup
|
||||
|
||||
See SettingUpPSQL.md
|
||||
|
||||
|
5
Docs/ServerConfig/hetzner/hetzner.txt
Normal file
5
Docs/ServerConfig/hetzner/hetzner.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Hetzner
|
||||
|
||||
This server hosts the studio files and is used for expermintal builds.
|
||||
|
||||
For used hosts, see the Caddyfile
|
75
Docs/SettingUpPSQL.md
Normal file
75
Docs/SettingUpPSQL.md
Normal file
|
@ -0,0 +1,75 @@
|
|||
# Setting up a synced OSM-server for quick layer access
|
||||
|
||||
## Setting up the SQL-server:
|
||||
|
||||
`sudo docker run --name some-postgis -e POSTGRES_PASSWORD=password -e POSTGRES_USER=user -d -p 5444:5432 -v /home/pietervdvn/data/pgsql/:/var/lib/postgresql/data postgis/postgis`
|
||||
|
||||
Then, connect to this databank with PGAdmin, create a database within it.
|
||||
Then activate following extensions for this database (right click > Create > Extension):
|
||||
|
||||
- Postgis activeren (rechtsklikken > Create > extension)
|
||||
- HStore activeren
|
||||
|
||||
Increase the max number of connections. osm2pgsql needs connection one per table (and a few more), and since we are making one table per layer in MapComplete, this amounts to a lot.
|
||||
|
||||
- Open PGAdmin, open the PGSQL-tool (CLI-button at the top)
|
||||
- Run `max_connections = 2000;` and `show config_file;` to get the config file location (in docker). This is probably `/var/lib/postgresql/data/postgresql.conf`
|
||||
- In a terminal, run `sudo docker exec -i <docker-container-id> bash` (run `sudo docker ps` to get the container id)
|
||||
- `sed -i "s/max_connections = 100/max_connections = 5000/" /var/lib/postgresql/data/postgresql.conf`
|
||||
- Validate with `cat /var/lib/postgresql/data/postgresql.conf | grep "max_connections"`
|
||||
- `sudo docker restart <ID>`
|
||||
|
||||
## Create export scripts for every layer
|
||||
|
||||
Use `vite-node ./scripts/osm2pgsql/generateBuildDbScript.ts`
|
||||
|
||||
## Importing data
|
||||
|
||||
Install osm2pgsql (hint: compile from source is painless)
|
||||
To seed the database:
|
||||
|
||||
````
|
||||
osm2pgsql -O flex -S build_db.lua -s --flat-nodes=import-help-file -d postgresql://user:password@localhost:5444/osm-poi <file>.osm.pbf
|
||||
````
|
||||
Storing properties to table '"public"."osm2pgsql_properties" takes about 25 minutes with planet.osm
|
||||
|
||||
Belgium (~555mb) takes 15m
|
||||
World (80GB) should take 15m*160 = 2400m = 40hr
|
||||
|
||||
73G Jan 23 00:22 planet-240115.osm.pbf: 2024-02-10 16:45:11 osm2pgsql took 871615s (242h 6m 55s; 10 days) overall on lain.local with RAID5 on 4 HDD disks, database is over 1Terrabyte (!)
|
||||
|
||||
Server specs
|
||||
|
||||
Lenovo thinkserver RD350, Intel Xeon E5-2600, 2Rx4 PC3
|
||||
11 watt powered off, 73 watt idle, ~100 watt when importing
|
||||
|
||||
HP ProLiant DL360 G7 (1U): 2Rx4 DDR3-memory (PC3)
|
||||
Intel Xeon X56**
|
||||
|
||||
|
||||
## Updating data
|
||||
|
||||
`osm2pgsql-replication update -d postgresql://user:password@localhost:5444/osm-poi -- -O flex -S build_db.lua -s --flat-nodes=import-help-file`
|
||||
|
||||
|
||||
## Deploying a tile server
|
||||
|
||||
pg_tileserv kan hier gedownload worden: https://github.com/CrunchyData/pg_tileserv
|
||||
|
||||
````
|
||||
export DATABASE_URL=postgresql://user:password@localhost:5444/osm-poi
|
||||
nohup ./pg_tileserv &
|
||||
````
|
||||
|
||||
Tiles are available at:
|
||||
````
|
||||
map.addSource("drinking_water", {
|
||||
"type": "vector",
|
||||
"tiles": ["http://127.0.0.2:7800/public.drinking_water/{z}/{x}/{y}.pbf"] // http://127.0.0.2:7800/public.drinking_water.json",
|
||||
})
|
||||
````
|
||||
|
||||
# Rebooting:
|
||||
|
||||
-> Restart the docker container
|
||||
->
|
|
@ -1,4 +1,28 @@
|
|||
{
|
||||
"id": "aerialway",
|
||||
"name": {
|
||||
"en": "Aerialways",
|
||||
"de": "Seilbahnen"
|
||||
},
|
||||
"description": {
|
||||
"en": "Various forms of transport for passengers and goods that use wires, including cable cars, gondolas, chair lifts, drag lifts, and zip lines. ",
|
||||
"de": "Alle Arten von seil- oder drahtgestütztem Personen- oder Gütertransport, wie Seilbahnen, Gondeln, Sessellifte, Schlepplifte. "
|
||||
},
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
"aerialway~*",
|
||||
"aerialway!=pylon",
|
||||
"aerialway!=station"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Aerialway {name}",
|
||||
"de": "Seilbahn {name}"
|
||||
}
|
||||
},
|
||||
"pointRendering": [
|
||||
{
|
||||
"location": [
|
||||
|
@ -27,6 +51,27 @@
|
|||
"labelCssClasses": "bg-white px-2 py-1 no-weblate"
|
||||
}
|
||||
],
|
||||
"lineRendering": [
|
||||
{
|
||||
"width": "4",
|
||||
"color": "black",
|
||||
"imageAlongWay": [
|
||||
{
|
||||
"if": "oneway=no",
|
||||
"then": "./assets/png/twoway.png"
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"or": [
|
||||
"oneway=yes",
|
||||
"oneway="
|
||||
]
|
||||
},
|
||||
"then": "./assets/png/oneway.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
"images",
|
||||
{
|
||||
|
@ -155,20 +200,23 @@
|
|||
{
|
||||
"id": "oneway",
|
||||
"question": {
|
||||
"en": "In what direction can this aerialway be taken?"
|
||||
"en": "In what direction can this aerialway be taken?",
|
||||
"de": "In welche Richtung kann diese Seilbahn genutzt werden?"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "oneway=yes",
|
||||
"alsoShowIf": "oneway=",
|
||||
"then": {
|
||||
"en": "This aerialway can only be taken to the top"
|
||||
"en": "This aerialway can only be taken to the top",
|
||||
"de": "Diese Seilbahn kann nur nach oben gefahren werden"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "oneway=no",
|
||||
"then": {
|
||||
"en": "This aerialway can be taken in both directions"
|
||||
"en": "This aerialway can be taken in both directions",
|
||||
"de": "Diese Seilbahn kann in beide Richtungen befahren werden"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -180,50 +228,5 @@
|
|||
"de": "Die Seilbahn hat eine Länge von {_length:km} km"
|
||||
}
|
||||
}
|
||||
],
|
||||
"lineRendering": [
|
||||
{
|
||||
"width": "4",
|
||||
"color": "black",
|
||||
"imageAlongWay": [
|
||||
{
|
||||
"if": "oneway=no",
|
||||
"then": "./assets/png/twoway.png"
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"or": [
|
||||
"oneway=yes",
|
||||
"oneway="
|
||||
]
|
||||
},
|
||||
"then": "./assets/png/oneway.png"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"id": "aerialway",
|
||||
"name": {
|
||||
"en": "Aerialways",
|
||||
"de": "Seilbahnen"
|
||||
},
|
||||
"description": {
|
||||
"en": "Various forms of transport for passengers and goods that use wires, including cable cars, gondolas, chair lifts, drag lifts, and zip lines. ",
|
||||
"de": "Alle Arten von seil- oder drahtgestütztem Personen- oder Gütertransport, wie Seilbahnen, Gondeln, Sessellifte, Schlepplifte. "
|
||||
},
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
"aerialway~*",
|
||||
"aerialway!=pylon",
|
||||
"aerialway!=station"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Aerialway {name}",
|
||||
"de": "Seilbahn {name}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
{
|
||||
"allowMove": {
|
||||
"enableRelocation": false,
|
||||
"enableImproveAccuracy": true
|
||||
},
|
||||
"id": "assembly_point",
|
||||
"name": {
|
||||
"en": "Emergency assembly points",
|
||||
"it": "Punti di raccolta per emergenze",
|
||||
|
@ -13,9 +10,6 @@
|
|||
"it": "Questo livello contiene punti di raccolta e aree di attesa in cui tutti i dipendenti, i passeggeri o una grande folla si riuniscono in caso di emergenza.",
|
||||
"de": "Diese Ebene enthält Sammelplätze und Wartebereiche, in denen sich alle Mitarbeiter, Fahrgäste oder eine große Menschenmenge im Notfall versammeln."
|
||||
},
|
||||
"docs": "https://wiki.openstreetmap.org/wiki/Tag:emergency%3Dassembly_point",
|
||||
"id": "assembly_point",
|
||||
"minzoom": 10,
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
|
@ -23,6 +17,13 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"minzoom": 10,
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Assembly point during emergencies",
|
||||
"de": "Sammelplatz bei Notfällen"
|
||||
}
|
||||
},
|
||||
"pointRendering": [
|
||||
{
|
||||
"iconSize": "20,20",
|
||||
|
@ -50,12 +51,6 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Assembly point during emergencies",
|
||||
"de": "Sammelplatz bei Notfällen"
|
||||
}
|
||||
},
|
||||
"tagRenderings": [
|
||||
"images",
|
||||
{
|
||||
|
@ -154,5 +149,10 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"allowMove": {
|
||||
"enableRelocation": false,
|
||||
"enableImproveAccuracy": true
|
||||
},
|
||||
"docs": "https://wiki.openstreetmap.org/wiki/Tag:emergency%3Dassembly_point"
|
||||
}
|
||||
|
|
|
@ -5,7 +5,12 @@
|
|||
"nl": "Oplaadpunten",
|
||||
"de": "Ladestationen"
|
||||
},
|
||||
"minzoom": 10,
|
||||
"description": {
|
||||
"en": "A charging station",
|
||||
"nl": "Oplaadpunten",
|
||||
"ca": "Una estació de càrrega",
|
||||
"de": "Eine Ladestation"
|
||||
},
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
|
@ -20,6 +25,7 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"minzoom": 10,
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Charging station",
|
||||
|
@ -65,13 +71,103 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"en": "A charging station",
|
||||
"nl": "Oplaadpunten",
|
||||
"ca": "Una estació de càrrega",
|
||||
"de": "Eine Ladestation"
|
||||
},
|
||||
"#": "no-question-hint-check",
|
||||
"pointRendering": [
|
||||
{
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
],
|
||||
"marker": [
|
||||
{
|
||||
"icon": "pin",
|
||||
"color": "#fff"
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"render": "./assets/themes/charging_stations/plug.svg",
|
||||
"mappings": [
|
||||
{
|
||||
"if": "bicycle=yes",
|
||||
"then": "./assets/themes/charging_stations/bicycle.svg"
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"or": [
|
||||
"car=yes",
|
||||
"motorcar=yes"
|
||||
]
|
||||
},
|
||||
"then": "./assets/themes/charging_stations/car.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"iconBadges": [
|
||||
{
|
||||
"if": {
|
||||
"or": [
|
||||
"disused:amenity=charging_station",
|
||||
"operational_status=broken"
|
||||
]
|
||||
},
|
||||
"then": "close:#c22;"
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"or": [
|
||||
"proposed:amenity=charging_station",
|
||||
"planned:amenity=charging_station"
|
||||
]
|
||||
},
|
||||
"then": "./assets/layers/charging_station/under_construction.svg"
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"and": [
|
||||
"bicycle=yes",
|
||||
{
|
||||
"or": [
|
||||
"motorcar=yes",
|
||||
"car=yes"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": "circle:#fff;./assets/themes/charging_stations/car.svg"
|
||||
}
|
||||
],
|
||||
"anchor": "bottom",
|
||||
"iconSize": "50,50"
|
||||
}
|
||||
],
|
||||
"lineRendering": [],
|
||||
"presets": [
|
||||
{
|
||||
"tags": [
|
||||
"amenity=charging_station",
|
||||
"motorcar=no",
|
||||
"bicycle=yes"
|
||||
],
|
||||
"title": {
|
||||
"en": "charging station for electrical bikes",
|
||||
"nl": "oplaadpunt voor elektrische fietsen",
|
||||
"de": "Ladestation für Elektrofahrräder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tags": [
|
||||
"amenity=charging_station",
|
||||
"motorcar=yes",
|
||||
"bicycle=no"
|
||||
],
|
||||
"title": {
|
||||
"en": "charging station for cars",
|
||||
"nl": "oplaadstation voor elektrische auto's",
|
||||
"de": "Ladestation für Elektrofahrräder"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
"images",
|
||||
{
|
||||
|
@ -2316,103 +2412,6 @@
|
|||
}
|
||||
}
|
||||
],
|
||||
"lineRendering": [],
|
||||
"pointRendering": [
|
||||
{
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
],
|
||||
"marker": [
|
||||
{
|
||||
"icon": "pin",
|
||||
"color": "#fff"
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"render": "./assets/themes/charging_stations/plug.svg",
|
||||
"mappings": [
|
||||
{
|
||||
"if": "bicycle=yes",
|
||||
"then": "./assets/themes/charging_stations/bicycle.svg"
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"or": [
|
||||
"car=yes",
|
||||
"motorcar=yes"
|
||||
]
|
||||
},
|
||||
"then": "./assets/themes/charging_stations/car.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"iconBadges": [
|
||||
{
|
||||
"if": {
|
||||
"or": [
|
||||
"disused:amenity=charging_station",
|
||||
"operational_status=broken"
|
||||
]
|
||||
},
|
||||
"then": "close:#c22;"
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"or": [
|
||||
"proposed:amenity=charging_station",
|
||||
"planned:amenity=charging_station"
|
||||
]
|
||||
},
|
||||
"then": "./assets/layers/charging_station/under_construction.svg"
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"and": [
|
||||
"bicycle=yes",
|
||||
{
|
||||
"or": [
|
||||
"motorcar=yes",
|
||||
"car=yes"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": "circle:#fff;./assets/themes/charging_stations/car.svg"
|
||||
}
|
||||
],
|
||||
"anchor": "bottom",
|
||||
"iconSize": "50,50"
|
||||
}
|
||||
],
|
||||
"presets": [
|
||||
{
|
||||
"tags": [
|
||||
"amenity=charging_station",
|
||||
"motorcar=no",
|
||||
"bicycle=yes"
|
||||
],
|
||||
"title": {
|
||||
"en": "charging station for electrical bikes",
|
||||
"nl": "oplaadpunt voor elektrische fietsen",
|
||||
"de": "Ladestation für Elektrofahrräder"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tags": [
|
||||
"amenity=charging_station",
|
||||
"motorcar=yes",
|
||||
"bicycle=no"
|
||||
],
|
||||
"title": {
|
||||
"en": "charging station for cars",
|
||||
"nl": "oplaadstation voor elektrische auto's",
|
||||
"de": "Ladestation für Elektrofahrräder"
|
||||
}
|
||||
}
|
||||
],
|
||||
"filter": [
|
||||
{
|
||||
"id": "vehicle-type",
|
||||
|
@ -2611,6 +2610,20 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"deletion": {
|
||||
"softDeletionTags": {
|
||||
"and": [
|
||||
"amenity=",
|
||||
"disused:amenity=charging_station"
|
||||
]
|
||||
},
|
||||
"neededChangesets": 10
|
||||
},
|
||||
"allowMove": {
|
||||
"enableRelocation": false,
|
||||
"enableImproveAccuracy": true
|
||||
},
|
||||
"#": "no-question-hint-check",
|
||||
"units": [
|
||||
{
|
||||
"maxstay": {
|
||||
|
@ -2759,18 +2772,5 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"allowMove": {
|
||||
"enableRelocation": false,
|
||||
"enableImproveAccuracy": true
|
||||
},
|
||||
"deletion": {
|
||||
"softDeletionTags": {
|
||||
"and": [
|
||||
"amenity=",
|
||||
"disused:amenity=charging_station"
|
||||
]
|
||||
},
|
||||
"neededChangesets": 10
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -147,5 +147,6 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"doCount": false
|
||||
}
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
"source": {
|
||||
"osmTags": "HUISNR~*",
|
||||
"geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/CRAB_2021_10_26/tile_{z}_{x}_{y}.geojson",
|
||||
"geoJsonZoomLevel": 18,
|
||||
"maxCacheAge": 0
|
||||
"geoJsonZoomLevel": 18
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_HNRLABEL=(() => {const lbl = feat.properties.HNRLABEL?.split('-')?.map(l => Number(l))?.filter(i => !isNaN (i)) ;if(lbl?.length != 2) {return feat.properties.HNRLABEL}; const addresses = []; for(let i = lbl[0]; i <= lbl[1]; i += 1){addresses.push(''+i);}; return addresses.join(';') })()"
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
{
|
||||
"id": "disaster_response",
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Disaster response organization",
|
||||
"it": "Organizzazione per la risposta ai disastri",
|
||||
"de": "Katastrophenschutzorganisation"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"en": "Disaster response organizations",
|
||||
"de": "Katastrophenschutzorganisationen"
|
||||
|
@ -16,8 +9,36 @@
|
|||
"it": "Questo livello contiene organizzazioni che hanno come obiettivo principale quello di aiutare la popolazione civile durante e dopo disastri naturali o antropogenici, lavorando nell'area colpita.",
|
||||
"de": "Diese Ebene umfasst Organisationen, deren Hauptziel es ist, der Zivilbevölkerung während und nach Natur- oder anthropogenen Katastrophen zu helfen, indem sie in dem betroffenen Gebiet tätig sind."
|
||||
},
|
||||
"docs": "https://wiki.openstreetmap.org/wiki/Tag:emergency%3Ddisaster_response",
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
"emergency=disaster_response"
|
||||
]
|
||||
}
|
||||
},
|
||||
"minzoom": 10,
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Disaster response organization",
|
||||
"it": "Organizzazione per la risposta ai disastri",
|
||||
"de": "Katastrophenschutzorganisation"
|
||||
}
|
||||
},
|
||||
"pointRendering": [
|
||||
{
|
||||
"iconSize": "40,40",
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
],
|
||||
"anchor": "bottom",
|
||||
"marker": [
|
||||
{
|
||||
"icon": "./assets/themes/disaster_response/CivilDefence.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"lineRendering": [
|
||||
{
|
||||
"color": "#6BC4F7",
|
||||
|
@ -36,28 +57,6 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
"emergency=disaster_response"
|
||||
]
|
||||
}
|
||||
},
|
||||
"pointRendering": [
|
||||
{
|
||||
"iconSize": "40,40",
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
],
|
||||
"anchor": "bottom",
|
||||
"marker": [
|
||||
{
|
||||
"icon": "./assets/themes/disaster_response/CivilDefence.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
"images",
|
||||
"website",
|
||||
|
@ -77,5 +76,6 @@
|
|||
"de": "Diese Organisation heißt <b>{name}</b>"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"docs": "https://wiki.openstreetmap.org/wiki/Tag:emergency%3Ddisaster_response"
|
||||
}
|
||||
|
|
|
@ -441,4 +441,4 @@
|
|||
],
|
||||
"deletion": true,
|
||||
"allowMove": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
}
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_same_name_ids=closestn(feat)('*', 250, undefined, 5000)?.filter(f => f.feat.properties.name === feat.properties.name)?.map(f => f.feat.properties.id)??[]"
|
||||
"_same_name_ids=closestn(feat)('*', 250, undefined, 3000)?.filter(f => f.feat.properties.name === feat.properties.name)?.map(f => f.feat.properties.id)??[]"
|
||||
],
|
||||
"minzoom": 12,
|
||||
"title": {
|
||||
|
|
|
@ -309,7 +309,8 @@
|
|||
"keyToShowWikipediaFor": "subject:wikidata"
|
||||
},
|
||||
"before": {
|
||||
"en": "<h3>Wikipedia page about the deceased person</h3>"
|
||||
"en": "<h3>Wikipedia page about the deceased person</h3>",
|
||||
"de": "<h3>Wikipedia-Seite über die verstorbene Person</h3>"
|
||||
}
|
||||
},
|
||||
"condition": "subject:wikidata~*"
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"source": {
|
||||
"osmTags": "amenity=ice_cream"
|
||||
},
|
||||
"minzoom": 14,
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Ice cream parlor",
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
{
|
||||
"id": "item_with_image",
|
||||
"name": {
|
||||
"en": "Items with at least one image"
|
||||
},
|
||||
"description": "All items with an image. All alone, not a layer which is relevant for any MapComplete theme, as it is a random collection of items. However, when put into the databank, this allows to quickly fetch (the URL of) pictures nearby a different object, to quickly link this",
|
||||
"minzoom": 14,
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"or": [
|
||||
|
@ -15,22 +17,20 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"tagRenderings": [
|
||||
"images"
|
||||
],
|
||||
"minzoom": 14,
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "POI with image"
|
||||
},
|
||||
"mappings": [{
|
||||
"if": "name~*",
|
||||
"then": {"*": "name"}
|
||||
}]
|
||||
"mappings": [
|
||||
{
|
||||
"if": "name~*",
|
||||
"then": {
|
||||
"*": "name"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"en": "Items with at least one image"
|
||||
},
|
||||
"lineRendering": [],
|
||||
"pointRendering": [
|
||||
{
|
||||
"marker": [
|
||||
|
@ -43,5 +43,9 @@
|
|||
"point"
|
||||
]
|
||||
}
|
||||
],
|
||||
"lineRendering": [],
|
||||
"tagRenderings": [
|
||||
"images"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "last_click",
|
||||
"name": null,
|
||||
"description": "This layer defines how to render the 'last click'-location. By default, it will show a marker with the possibility to add a new point (if there are some presets) and/or to add a new note (if the 'note' layer attribute is set). If none are possible, this layer won't show up",
|
||||
"description": "This 'layer' is not really a layer, but contains part of the code how the popup to 'add a new marker' is displayed",
|
||||
"source": "special",
|
||||
"isShown": {
|
||||
"or": [
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
},
|
||||
"source": {
|
||||
"osmTags": "mr_taskId~*",
|
||||
"geoJson": "https://maproulette.org/api/v2/challenge/view/27971",
|
||||
"isOsmCache": false
|
||||
"geoJson": "https://maproulette.org/api/v2/challenge/view/27971"
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
|
|
|
@ -1,6 +1,23 @@
|
|||
{
|
||||
"credits": "Not logged in",
|
||||
"id": "mountain_rescue",
|
||||
"name": {
|
||||
"en": "Mountain rescue stations",
|
||||
"de": "Bergrettungsstationen"
|
||||
},
|
||||
"description": {
|
||||
"en": "A building where first aid responders store material and might be on watch",
|
||||
"de": "Ein Gebäude, in dem die Ersthelfer Material lagern und möglicherweise Wache halten"
|
||||
},
|
||||
"source": {
|
||||
"osmTags": "emergency=mountain_rescue"
|
||||
},
|
||||
"minzoom": 10,
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Mountain rescue station",
|
||||
"de": "Bergrettungsstation"
|
||||
}
|
||||
},
|
||||
"pointRendering": [
|
||||
{
|
||||
"location": [
|
||||
|
@ -19,42 +36,29 @@
|
|||
"rotation": "45"
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
"images"
|
||||
],
|
||||
"lineRendering": [
|
||||
{
|
||||
"width": "3",
|
||||
"color": "#ed333b"
|
||||
}
|
||||
],
|
||||
"id": "mountain_rescue",
|
||||
"name": {
|
||||
"en": "Mountain rescue stations"
|
||||
},
|
||||
"description": {
|
||||
"en": "A building where first aid responders store material and might be on watch"
|
||||
},
|
||||
"source": {
|
||||
"osmTags": "emergency=mountain_rescue"
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Mountain rescue station"
|
||||
}
|
||||
},
|
||||
"deletion": true,
|
||||
"allowMove": {
|
||||
"enableRelocation": false
|
||||
},
|
||||
"presets": [
|
||||
{
|
||||
"title": {
|
||||
"en": "a mountain rescue station"
|
||||
"en": "a mountain rescue station",
|
||||
"de": "eine Bergrettungsstation"
|
||||
},
|
||||
"tags": [
|
||||
"emergency=mountain_rescue"
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"tagRenderings": [
|
||||
"images"
|
||||
],
|
||||
"deletion": true,
|
||||
"allowMove": {
|
||||
"enableRelocation": false
|
||||
},
|
||||
"credits": "Not logged in"
|
||||
}
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
"source": {
|
||||
"osmTags": "date_created~*",
|
||||
"geoJson": "https://api.openstreetmap.org/api/0.6/notes.json?limit=10000&closed=7&bbox={x_min},{y_min},{x_max},{y_max}",
|
||||
"geoJsonZoomLevel": 12,
|
||||
"maxCacheAge": 0,
|
||||
"isOsmCache": false
|
||||
"geoJsonZoomLevel": 12
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_total_comments:=get(feat)('comments').length",
|
||||
|
|
|
@ -19,8 +19,7 @@
|
|||
"source": {
|
||||
"geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/community_index/tile_{z}_{x}_{y}.geojson",
|
||||
"geoJsonZoomLevel": 6,
|
||||
"osmTags": "resources~*",
|
||||
"isOsmCache": false
|
||||
"osmTags": "resources~*"
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_community_links=Object.values(JSON.parse(feat.properties.resources || '{}')).map(value =>{return value.resolved.nameHTML + '<br> ' + value.resolved.descriptionHTML}).join('<br>')"
|
||||
|
|
|
@ -42,7 +42,8 @@
|
|||
"if": "parking_space=charging",
|
||||
"then": {
|
||||
"en": "Electric Vehicle Charging Parking Space",
|
||||
"nl": "Parkeerplek voor opladen van elektrische voertuigen"
|
||||
"nl": "Parkeerplek voor opladen van elektrische voertuigen",
|
||||
"de": "Parkplatz zum Laden von Elektrofahrzeugen"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -62,10 +63,6 @@
|
|||
"if": "parking_space=charging",
|
||||
"then": "./assets/themes/charging_stations/plug.svg"
|
||||
},
|
||||
{
|
||||
"if": "parking_space=private",
|
||||
"then": "./assets/layers/parking_spaces/parking_space_private.svg"
|
||||
},
|
||||
{
|
||||
"if": "parking_space=bus",
|
||||
"then": "./assets/layers/transit_stops/bus_stop.svg"
|
||||
|
@ -138,17 +135,6 @@
|
|||
"cs": "Jedná se o parkovací místo pro osoby se zdravotním postižením."
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "parking_space=private",
|
||||
"then": {
|
||||
"en": "This is a private parking space.",
|
||||
"de": "Dies ist ein privater Stellplatz.",
|
||||
"nl": "Dit is een privéparkeerplek.",
|
||||
"pl": "To jest prywatne miejsce parkingowe.",
|
||||
"ca": "Es tracta d'una plaça d'aparcament privada.",
|
||||
"cs": "Jedná se o soukromé parkovací místo."
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "parking_space=charging",
|
||||
"then": {
|
||||
|
@ -287,4 +273,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,20 @@
|
|||
{
|
||||
"id": "playground_equipment",
|
||||
"name": {
|
||||
"en": "Playground equipment"
|
||||
},
|
||||
"description": {
|
||||
"en": "Layer showing playground equipment"
|
||||
},
|
||||
"source": {
|
||||
"osmTags": "playground~*"
|
||||
},
|
||||
"minzoom": 18,
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Playground device"
|
||||
}
|
||||
},
|
||||
"pointRendering": [
|
||||
{
|
||||
"location": [
|
||||
|
@ -18,6 +33,25 @@
|
|||
"iconSize": "20,20"
|
||||
}
|
||||
],
|
||||
"lineRendering": [
|
||||
{
|
||||
"width": 1,
|
||||
"color": "blue"
|
||||
}
|
||||
],
|
||||
"presets": [
|
||||
{
|
||||
"tags": [
|
||||
"playground=yes"
|
||||
],
|
||||
"title": {
|
||||
"en": "a playground device"
|
||||
},
|
||||
"description": {
|
||||
"en": "An exact type is asked later"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
"images",
|
||||
{
|
||||
|
@ -299,39 +333,5 @@
|
|||
}
|
||||
}
|
||||
],
|
||||
"lineRendering": [
|
||||
{
|
||||
"width": 1,
|
||||
"color": "blue"
|
||||
}
|
||||
],
|
||||
"id": "playground_equipment",
|
||||
"name": {
|
||||
"en": "Playground equipment"
|
||||
},
|
||||
"description": {
|
||||
"en": "Layer showing playground equipment"
|
||||
},
|
||||
"source": {
|
||||
"osmTags": "playground~*"
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Playground device"
|
||||
}
|
||||
},
|
||||
"allowMove": true,
|
||||
"presets": [
|
||||
{
|
||||
"tags": [
|
||||
"playground=yes"
|
||||
],
|
||||
"title": {
|
||||
"en": "a playground device"
|
||||
},
|
||||
"description": {
|
||||
"en": "An exact type is asked later"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"allowMove": true
|
||||
}
|
||||
|
|
|
@ -634,4 +634,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"source": {
|
||||
"osmTags": "amenity=shower"
|
||||
},
|
||||
"minzoom": 12,
|
||||
"minzoom": 8,
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Shower",
|
||||
|
|
|
@ -1,4 +1,32 @@
|
|||
{
|
||||
"id": "ski_piste",
|
||||
"name": {
|
||||
"en": "Ski and snowboard pistes",
|
||||
"de": "Ski- und Snowboardpisten"
|
||||
},
|
||||
"description": {
|
||||
"en": "Ski and snowboard pistes",
|
||||
"de": "Ski- und Snowboardpisten"
|
||||
},
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
{
|
||||
"or": [
|
||||
"piste:type=downhill",
|
||||
"piste:type=connection"
|
||||
]
|
||||
},
|
||||
"area!=yes"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Ski piste {name}",
|
||||
"de": "Skipiste {name}"
|
||||
}
|
||||
},
|
||||
"pointRendering": [
|
||||
{
|
||||
"location": [
|
||||
|
@ -12,6 +40,40 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"lineRendering": [
|
||||
{
|
||||
"width": "10",
|
||||
"color": {
|
||||
"mappings": [
|
||||
{
|
||||
"then": "green",
|
||||
"if": "piste:difficulty=novice"
|
||||
},
|
||||
{
|
||||
"if": "piste:difficulty=easy",
|
||||
"then": "blue"
|
||||
},
|
||||
{
|
||||
"if": "piste:difficulty=intermediate",
|
||||
"then": "red"
|
||||
},
|
||||
{
|
||||
"if": "piste:difficulty=advanced",
|
||||
"then": "black"
|
||||
},
|
||||
{
|
||||
"if": "piste:difficulty=expert",
|
||||
"then": "orange"
|
||||
},
|
||||
{
|
||||
"if": "piste:difficulty=",
|
||||
"then": "gray"
|
||||
}
|
||||
]
|
||||
},
|
||||
"imageAlongWay": "./assets/png/oneway.png"
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
"images",
|
||||
{
|
||||
|
@ -73,67 +135,5 @@
|
|||
"de": "Dieser Teil der Skipiste hat eine Länge von {_length:km} km"
|
||||
}
|
||||
}
|
||||
],
|
||||
"lineRendering": [
|
||||
{
|
||||
"width": "10",
|
||||
"color": {
|
||||
"mappings": [
|
||||
{
|
||||
"then": "green",
|
||||
"if": "piste:difficulty=novice"
|
||||
},
|
||||
{
|
||||
"if": "piste:difficulty=easy",
|
||||
"then": "blue"
|
||||
},
|
||||
{
|
||||
"if": "piste:difficulty=intermediate",
|
||||
"then": "red"
|
||||
},
|
||||
{
|
||||
"if": "piste:difficulty=advanced",
|
||||
"then": "black"
|
||||
},
|
||||
{
|
||||
"if": "piste:difficulty=expert",
|
||||
"then": "orange"
|
||||
},
|
||||
{
|
||||
"if": "piste:difficulty=",
|
||||
"then": "gray"
|
||||
}
|
||||
]
|
||||
},
|
||||
"imageAlongWay": "./assets/png/oneway.png"
|
||||
}
|
||||
],
|
||||
"id": "ski_piste",
|
||||
"name": {
|
||||
"en": "Ski and snowboard pistes",
|
||||
"de": "Ski- und Snowboardpisten"
|
||||
},
|
||||
"description": {
|
||||
"en": "Ski and snowboard pistes",
|
||||
"de": "Ski- und Snowboardpisten"
|
||||
},
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
{
|
||||
"or": [
|
||||
"piste:type=downhill",
|
||||
"piste:type=connection"
|
||||
]
|
||||
},
|
||||
"area!=yes"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Ski piste {name}",
|
||||
"de": "Skipiste {name}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,4 +1,24 @@
|
|||
{
|
||||
"id": "souvenir_coin",
|
||||
"name": {
|
||||
"en": "Souvenir Coin Machines"
|
||||
},
|
||||
"description": {
|
||||
"en": "Layer showing machines selling souvenir coins"
|
||||
},
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
"amenity=vending_machine",
|
||||
"vending~.*souvenir_coins.*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Souvenir Coin Machine"
|
||||
}
|
||||
},
|
||||
"pointRendering": [
|
||||
{
|
||||
"location": [
|
||||
|
@ -22,6 +42,26 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"lineRendering": [
|
||||
{
|
||||
"width": 1,
|
||||
"color": "blue"
|
||||
}
|
||||
],
|
||||
"presets": [
|
||||
{
|
||||
"title": {
|
||||
"en": "a souvenir coin machine"
|
||||
},
|
||||
"description": {
|
||||
"en": "Add a machine selling souvenir coins"
|
||||
},
|
||||
"tags": [
|
||||
"amenity=vending_machine",
|
||||
"vending=souvenir_coins"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
"images",
|
||||
"opening_hours_24_7",
|
||||
|
@ -110,46 +150,6 @@
|
|||
"level",
|
||||
"check_date"
|
||||
],
|
||||
"lineRendering": [
|
||||
{
|
||||
"width": 1,
|
||||
"color": "blue"
|
||||
}
|
||||
],
|
||||
"id": "souvenir_coin",
|
||||
"name": {
|
||||
"en": "Souvenir Coin Machines"
|
||||
},
|
||||
"description": {
|
||||
"en": "Layer showing machines selling souvenir coins"
|
||||
},
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
"amenity=vending_machine",
|
||||
"vending~.*souvenir_coins.*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Souvenir Coin Machine"
|
||||
}
|
||||
},
|
||||
"presets": [
|
||||
{
|
||||
"title": {
|
||||
"en": "a souvenir coin machine"
|
||||
},
|
||||
"description": {
|
||||
"en": "Add a machine selling souvenir coins"
|
||||
},
|
||||
"tags": [
|
||||
"amenity=vending_machine",
|
||||
"vending=souvenir_coins"
|
||||
]
|
||||
}
|
||||
],
|
||||
"filter": [
|
||||
"open_now",
|
||||
"accepts_debit_cards",
|
||||
|
@ -157,4 +157,4 @@
|
|||
],
|
||||
"deletion": true,
|
||||
"allowMove": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,24 @@
|
|||
{
|
||||
"id": "souvenir_note",
|
||||
"name": {
|
||||
"en": "Souvenir Banknote Machines"
|
||||
},
|
||||
"description": {
|
||||
"en": "Layer showing machines selling souvenir banknotes"
|
||||
},
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
"amenity=vending_machine",
|
||||
"vending~.*souvenir_notes.*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Souvenir Banknote Machine"
|
||||
}
|
||||
},
|
||||
"pointRendering": [
|
||||
{
|
||||
"location": [
|
||||
|
@ -22,6 +42,26 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"lineRendering": [
|
||||
{
|
||||
"width": 1,
|
||||
"color": "blue"
|
||||
}
|
||||
],
|
||||
"presets": [
|
||||
{
|
||||
"title": {
|
||||
"en": "a souvenir banknote machine"
|
||||
},
|
||||
"description": {
|
||||
"en": "Add a machine selling souvenir banknotes"
|
||||
},
|
||||
"tags": [
|
||||
"amenity=vending_machine",
|
||||
"vending=souvenir_notes"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
"images",
|
||||
"opening_hours_24_7",
|
||||
|
@ -128,46 +168,6 @@
|
|||
"level",
|
||||
"check_date"
|
||||
],
|
||||
"lineRendering": [
|
||||
{
|
||||
"width": 1,
|
||||
"color": "blue"
|
||||
}
|
||||
],
|
||||
"id": "souvenir_note",
|
||||
"name": {
|
||||
"en": "Souvenir Banknote Machines"
|
||||
},
|
||||
"description": {
|
||||
"en": "Layer showing machines selling souvenir banknotes"
|
||||
},
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
"amenity=vending_machine",
|
||||
"vending~.*souvenir_notes.*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Souvenir Banknote Machine"
|
||||
}
|
||||
},
|
||||
"presets": [
|
||||
{
|
||||
"title": {
|
||||
"en": "a souvenir banknote machine"
|
||||
},
|
||||
"description": {
|
||||
"en": "Add a machine selling souvenir banknotes"
|
||||
},
|
||||
"tags": [
|
||||
"amenity=vending_machine",
|
||||
"vending=souvenir_notes"
|
||||
]
|
||||
}
|
||||
],
|
||||
"filter": [
|
||||
"open_now",
|
||||
"accepts_debit_cards",
|
||||
|
@ -175,4 +175,4 @@
|
|||
],
|
||||
"deletion": true,
|
||||
"allowMove": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
"de": "Ein Ort, an dem erotische Tanz-, Striptease- oder Lapdances kommerziell durchgeführt werden. "
|
||||
},
|
||||
"source": {
|
||||
"osmTags": "amenity=stripclub",
|
||||
"isOsmCache": false
|
||||
"osmTags": "amenity=stripclub"
|
||||
},
|
||||
"minzoom": 6,
|
||||
"title": {
|
||||
|
|
25
assets/layers/summary/summary.json
Normal file
25
assets/layers/summary/summary.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"id": "summary",
|
||||
"description": "Special layer which shows `count`",
|
||||
"source": "special",
|
||||
"title": {
|
||||
"render": {
|
||||
"en": "Summary"
|
||||
}
|
||||
},
|
||||
"pointRendering": [
|
||||
{
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
],
|
||||
"iconSize": "40,40",
|
||||
"label": "{total_metric}",
|
||||
"labelCss": "background: #ffffffbb",
|
||||
"labelCssClasses": "w-12 text-lg rounded-xl p-1 px-2"
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
"all_tags"
|
||||
]
|
||||
}
|
|
@ -553,25 +553,29 @@
|
|||
"id": "gender_segregated",
|
||||
"question": {
|
||||
"en": "Are these toilets gender-segregated?",
|
||||
"nl": "Zijn deze toiletten gescheiden op basis van geslacht?"
|
||||
"nl": "Zijn deze toiletten gescheiden op basis van geslacht?",
|
||||
"de": "Sind diese Toiletten geschlechtergetrennt?"
|
||||
},
|
||||
"questionHint": {
|
||||
"en": "Are there separate stalls or separate areas for men and women and are they signposted as such?",
|
||||
"nl": "Is er een aparte ruimte voor mannen en vrouwen en zijn deze ruimtes ook expliciet aangegeven?"
|
||||
"nl": "Is er een aparte ruimte voor mannen en vrouwen en zijn deze ruimtes ook expliciet aangegeven?",
|
||||
"de": "Gibt es getrennte Kabinen oder getrennte Bereiche für Männer und Frauen, und sind sie als solche ausgeschildert?"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "gender_segregated=yes",
|
||||
"then": {
|
||||
"en": "There is a separate, signposted area for men and women",
|
||||
"nl": "Er zijn aparte ruimtes of toiletten voor mannen en vrouwen"
|
||||
"nl": "Er zijn aparte ruimtes of toiletten voor mannen en vrouwen",
|
||||
"de": "Es gibt einen separaten, ausgeschilderten Bereich für Männer und Frauen"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "gender_segregated=no",
|
||||
"then": {
|
||||
"en": "There is no separate, signposted area for men and women",
|
||||
"nl": "Mannen en vrouwen gebruiken dezelfde ruimtes en toiletten"
|
||||
"nl": "Mannen en vrouwen gebruiken dezelfde ruimtes en toiletten",
|
||||
"de": "Es gibt keinen getrennten, ausgeschilderten Bereich für Männer und Frauen"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -580,25 +584,29 @@
|
|||
"id": "menstrual_products",
|
||||
"question": {
|
||||
"en": "Are free, menstrual products distributed here?",
|
||||
"nl": "Zijn er gratis menstruatieproducten beschikbaar?"
|
||||
"nl": "Zijn er gratis menstruatieproducten beschikbaar?",
|
||||
"de": "Werden hier kostenlose Menstruationsprodukte verteilt?"
|
||||
},
|
||||
"questionHint": {
|
||||
"en": "This is only about menstrual products that are free of charge. If e.g. a vending machine is available which charges for menstrual products, ignore it for this question.",
|
||||
"nl": "Dit gaat enkel over menstruatieproducten die gratis geschikbaar zijn. Indien er bv. een verkoopautomaat met menstruatieproducten is, negeer deze dan"
|
||||
"nl": "Dit gaat enkel over menstruatieproducten die gratis geschikbaar zijn. Indien er bv. een verkoopautomaat met menstruatieproducten is, negeer deze dan",
|
||||
"de": "Hier geht es nur um Menstruationsprodukte, die kostenlos sind. Wenn es z. B. einen Automaten gibt, an dem Menstruationsprodukte kostenpflichtig sind, wird er bei dieser Frage nicht berücksichtigt."
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "toilets:menstrual_products=yes",
|
||||
"then": {
|
||||
"en": "Free menstrual products are available to all visitors of these toilets",
|
||||
"nl": "Er zijn gratis menstruatieprocten beschikbaar voor alle bezoekers van deze toiletten"
|
||||
"nl": "Er zijn gratis menstruatieprocten beschikbaar voor alle bezoekers van deze toiletten",
|
||||
"de": "Für alle Besucher dieser Toiletten stehen kostenlose Menstruationsprodukte zur Verfügung"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "toilets:menstrual_products=limited",
|
||||
"then": {
|
||||
"en": "Free menstrual products are available to some visitors of these toilets",
|
||||
"nl": "De gratis menstruatieproducten zijn enkel beschikbaar in een deel van de toiletten"
|
||||
"nl": "De gratis menstruatieproducten zijn enkel beschikbaar in een deel van de toiletten",
|
||||
"de": "Für einige Besucher dieser Toiletten gibt es kostenlose Menstruationsprodukte"
|
||||
},
|
||||
"hideInAnswer": "gender_segregated=yes"
|
||||
},
|
||||
|
@ -607,7 +615,8 @@
|
|||
"alsoShowIf": "toilets:menstrual_products=",
|
||||
"then": {
|
||||
"en": "No free menstrual products are available here",
|
||||
"nl": "Er zijn geen gratis menstruatieproducten beschikbaar"
|
||||
"nl": "Er zijn geen gratis menstruatieproducten beschikbaar",
|
||||
"de": "Hier gibt es keine kostenlosen Menstruationsprodukte"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -616,7 +625,8 @@
|
|||
"id": "menstrual_products_location",
|
||||
"question": {
|
||||
"en": "Where are the free menstrual products located?",
|
||||
"nl": "Waar bevinden de gratis menstruatieproducten zich?"
|
||||
"nl": "Waar bevinden de gratis menstruatieproducten zich?",
|
||||
"de": "Wo befinden sich die kostenlosen Menstruationsprodukte?"
|
||||
},
|
||||
"condition": {
|
||||
"or": [
|
||||
|
@ -626,7 +636,8 @@
|
|||
},
|
||||
"render": {
|
||||
"en": "The menstrual products are located in {toilets:menstrual_products:location}",
|
||||
"nl": "De menstruatieproducten bevinden zich in {toilets:menstrual_products:location}"
|
||||
"nl": "De menstruatieproducten bevinden zich in {toilets:menstrual_products:location}",
|
||||
"de": "Die Menstruationsprodukte befinden sich in {toilets:menstrual_products:location}"
|
||||
},
|
||||
"freeform": {
|
||||
"key": "toilets:menstrual_products:location",
|
||||
|
@ -636,7 +647,8 @@
|
|||
{
|
||||
"then": {
|
||||
"en": "The free, menstrual products are located in the toilet for women",
|
||||
"nl": "De gratis menstruatieproducten bevinden zich in het vrouwentoilet"
|
||||
"nl": "De gratis menstruatieproducten bevinden zich in het vrouwentoilet",
|
||||
"de": "Die kostenlosen Menstruationsprodukte befinden sich in der Toilette für Frauen"
|
||||
},
|
||||
"if": "toilets:menstrual_products:location=female_toilet",
|
||||
"alsoShowIf": "toilets:menstrual_products:location="
|
||||
|
@ -644,7 +656,8 @@
|
|||
{
|
||||
"then": {
|
||||
"en": "The free, menstrual products are located in the toilet for men",
|
||||
"nl": "De gratis menstruatieproducten bevinden zich in het mannentoilet"
|
||||
"nl": "De gratis menstruatieproducten bevinden zich in het mannentoilet",
|
||||
"de": "Die kostenlosen Menstruationsprodukte befinden sich in der Toilette für Männer"
|
||||
},
|
||||
"if": "toilets:menstrual_products:location=male_toilet"
|
||||
},
|
||||
|
@ -652,7 +665,8 @@
|
|||
"if": "toilets:menstrual_products:location=wheelchair_toilet",
|
||||
"then": {
|
||||
"en": "The free, menstrual products are located in the toilet for wheelchair users",
|
||||
"nl": "De gratis menstruatieproducten bevinden zich in het rolstoeltoegankelijke toilet"
|
||||
"nl": "De gratis menstruatieproducten bevinden zich in het rolstoeltoegankelijke toilet",
|
||||
"de": "Die kostenlosen Menstruationsprodukte befinden sich auf der Toilette für Rollstuhlfahrende"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,40 +1,69 @@
|
|||
{
|
||||
"id": "trolley_bay",
|
||||
"name": {
|
||||
"en": "Trolley Bays"
|
||||
"en": "Trolley Bays",
|
||||
"de": "Einkaufswagenbuchten"
|
||||
},
|
||||
"description": {
|
||||
"en": "Find trolley bays for shopping trolleys."
|
||||
},
|
||||
"title": {
|
||||
"en": "Trolley Bay"
|
||||
"en": "Find trolley bays for shopping trolleys.",
|
||||
"de": "Finde Einkaufswagenbuchten für Einkaufswagen."
|
||||
},
|
||||
"source": {
|
||||
"osmTags": "amenity=trolley_bay"
|
||||
},
|
||||
"minzoom": 18,
|
||||
"allowMove": {
|
||||
"enableImproveAccuracy": true,
|
||||
"enableRelocation": true
|
||||
"title": {
|
||||
"en": "Trolley Bay",
|
||||
"de": "Einkaufswagenbucht"
|
||||
},
|
||||
"deletion": true,
|
||||
"pointRendering": [
|
||||
{
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
],
|
||||
"marker": [
|
||||
{
|
||||
"icon": "./assets/layers/trolley_bay/trolley_bay.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"presets": [
|
||||
{
|
||||
"title": {
|
||||
"en": "a trolley bay",
|
||||
"de": "eine Einkaufswagenbucht"
|
||||
},
|
||||
"tags": [
|
||||
"amenity=trolley_bay"
|
||||
],
|
||||
"description": {
|
||||
"en": "A trolley bay for parking shopping carts.",
|
||||
"de": "Ein Platz zum Abstellen von Einkaufswagen."
|
||||
}
|
||||
}
|
||||
],
|
||||
"tagRenderings": [
|
||||
{
|
||||
"id": "covered",
|
||||
"question": {
|
||||
"en": "Is this trolley bay covered?"
|
||||
"en": "Is this trolley bay covered?",
|
||||
"de": "Ist dieser Einkaufswagenplatz überdacht?"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "covered=yes",
|
||||
"then": {
|
||||
"en": "This trolley bay is covered"
|
||||
"en": "This trolley bay is covered",
|
||||
"de": "Dieser Einkaufswagenplatz ist überdacht"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "covered=no",
|
||||
"then": {
|
||||
"en": "This trolley bay is not covered"
|
||||
"en": "This trolley bay is not covered",
|
||||
"de": "Dieser Einkaufswagenplatz ist nicht überdacht"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -42,19 +71,22 @@
|
|||
{
|
||||
"id": "deposit",
|
||||
"question": {
|
||||
"en": "Is a deposit (e.g. a coin) required for the trolleys?"
|
||||
"en": "Is a deposit (e.g. a coin) required for the trolleys?",
|
||||
"de": "Ist für die Einkaufswagen ein Pfand (z.B. eine Münze) erforderlich?"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "trolley:deposit=yes",
|
||||
"then": {
|
||||
"en": "A deposit is required for the trolleys"
|
||||
"en": "A deposit is required for the trolleys",
|
||||
"de": "Für die Einkaufswagen ist ein Pfand zu hinterlegen"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "trolley:deposit=no",
|
||||
"then": {
|
||||
"en": "No deposit is required for the trolleys"
|
||||
"en": "No deposit is required for the trolleys",
|
||||
"de": "Für die Einkaufswagen ist kein Pfand erforderlich"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -68,7 +100,8 @@
|
|||
]
|
||||
},
|
||||
"question": {
|
||||
"en": "What coins are accepted for the deposit?"
|
||||
"en": "What coins are accepted for the deposit?",
|
||||
"de": "Welche Münzen werden für die Pfandgebühr akzeptiert?"
|
||||
},
|
||||
"multiAnswer": true,
|
||||
"mappings": [
|
||||
|
@ -125,7 +158,8 @@
|
|||
{
|
||||
"id": "cart_types",
|
||||
"question": {
|
||||
"en": "What kind of special trolleys are available?"
|
||||
"en": "What kind of special trolleys are available?",
|
||||
"de": "Welche Art von speziellen Einkaufswagen gibt es?"
|
||||
},
|
||||
"multiAnswer": true,
|
||||
"mappings": [
|
||||
|
@ -133,42 +167,48 @@
|
|||
"if": "trolley:magnifier=yes",
|
||||
"ifnot": "trolley:magnifier=no",
|
||||
"then": {
|
||||
"en": "Trolleys with a magnifier are available"
|
||||
"en": "Trolleys with a magnifier are available",
|
||||
"de": "Einkaufswagen mit Lupe sind erhältlich"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "trolley:wheelchair=yes",
|
||||
"ifnot": "trolley:wheelchair=no",
|
||||
"then": {
|
||||
"en": "Trolleys for wheelchair users are available"
|
||||
"en": "Trolleys for wheelchair users are available",
|
||||
"de": "Einkaufswagen für Rollstuhlfahrer sind verfügbar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "trolley:children=yes",
|
||||
"ifnot": "trolley:children=no",
|
||||
"then": {
|
||||
"en": "Trolleys for children are available"
|
||||
"en": "Trolleys for children are available",
|
||||
"de": "Einkaufswagen für Kinder sind verfügbar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "trolley:seats=yes",
|
||||
"ifnot": "trolley:seats=no",
|
||||
"then": {
|
||||
"en": "Trolleys with seats for children are available"
|
||||
"en": "Trolleys with seats for children are available",
|
||||
"de": "Einkaufswagen mit Sitzen für Kinder sind verfügbar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "trolley:flatbed=yes",
|
||||
"ifnot": "trolley:flatbed=no",
|
||||
"then": {
|
||||
"en": "Trolleys with a flatbed are available"
|
||||
"en": "Trolleys with a flatbed are available",
|
||||
"de": "Einkaufswagen mit Flachbett sind verfügbar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "trolley:vertical=yes",
|
||||
"ifnot": "trolley:vertical=no",
|
||||
"then": {
|
||||
"en": "Vertical trolleys for sheet-like goods are available"
|
||||
"en": "Vertical trolleys for sheet-like goods are available",
|
||||
"de": "Vertikale Einkaufswagen für plattenförmige Güter sind verfügbar"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -177,30 +217,9 @@
|
|||
"id": "indoor"
|
||||
}
|
||||
],
|
||||
"presets": [
|
||||
{
|
||||
"title": {
|
||||
"en": "a trolley bay"
|
||||
},
|
||||
"tags": [
|
||||
"amenity=trolley_bay"
|
||||
],
|
||||
"description": {
|
||||
"en": "A trolley bay for parking shopping carts."
|
||||
}
|
||||
}
|
||||
],
|
||||
"pointRendering": [
|
||||
{
|
||||
"location": [
|
||||
"point",
|
||||
"centroid"
|
||||
],
|
||||
"marker": [
|
||||
{
|
||||
"icon": "./assets/layers/trolley_bay/trolley_bay.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
"deletion": true,
|
||||
"allowMove": {
|
||||
"enableImproveAccuracy": true,
|
||||
"enableRelocation": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -516,11 +516,13 @@
|
|||
"type": "import_mangrove_key",
|
||||
"text": {
|
||||
"en": "Import a mangrove private key from backup",
|
||||
"nl": "Herstel een Mangrove Private sleutel van backup"
|
||||
"nl": "Herstel een Mangrove Private sleutel van backup",
|
||||
"de": "Privaten Mangrove-Schlüssel aus Backup importieren"
|
||||
}
|
||||
},
|
||||
"after": {
|
||||
"en": "Uploading a private key erases your current private key. If you made reviews with it, download your current private key first"
|
||||
"en": "Uploading a private key erases your current private key. If you made reviews with it, download your current private key first",
|
||||
"de": "Wenn du einen privaten Schlüssel hochlädst, wird dein aktueller privater Schlüssel gelöscht. Wenn du damit Rezensionen geschrieben hast, lade zuerst deinen aktuellen privaten Schlüssel hoch"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -121,6 +121,7 @@
|
|||
"condition": "_biggest_width_id~*"
|
||||
}
|
||||
],
|
||||
"isCounted": false,
|
||||
"units": [
|
||||
{
|
||||
"width": {
|
||||
|
|
|
@ -47,4 +47,4 @@
|
|||
],
|
||||
"widenFactor": 0.01,
|
||||
"maintainer": "Offsel"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,8 @@
|
|||
"name": null,
|
||||
"filter": {
|
||||
"sameAs": "bank_with_atm"
|
||||
}
|
||||
},
|
||||
"doCount": false
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -45,15 +45,15 @@
|
|||
"hideFromOverview": true,
|
||||
"layers": [
|
||||
{
|
||||
"id": "osm:buildings",
|
||||
"id": "osm_buildings",
|
||||
"name": "OSM Buildings",
|
||||
"title": "OSM Building",
|
||||
"description": "Layer showing buildings that are in OpenStreetMap",
|
||||
"source": {
|
||||
"osmTags": "building~*",
|
||||
"maxCacheAge": 0
|
||||
"osmTags": "building~*"
|
||||
},
|
||||
"minzoom": 18,
|
||||
"doCount": false,
|
||||
"calculatedTags": [
|
||||
"_surface:strict:=feat(get)('_surface')"
|
||||
],
|
||||
|
@ -147,7 +147,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"id": "osm:adresses",
|
||||
"id": "osm_adresses",
|
||||
"name": "OSM Adresses",
|
||||
"title": "OSM Adress",
|
||||
"description": "Layer showing adresses that are in OpenStreetMap",
|
||||
|
@ -160,10 +160,10 @@
|
|||
"addr:postcode~*",
|
||||
"addr:street~*"
|
||||
]
|
||||
},
|
||||
"maxCacheAge": 0
|
||||
}
|
||||
},
|
||||
"minzoom": 18,
|
||||
"doCount": false,
|
||||
"pointRendering": [
|
||||
{
|
||||
"label": {
|
||||
|
@ -185,7 +185,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"id": "bag:pand",
|
||||
"id": "bag_pand",
|
||||
"name": "BAG Buildings",
|
||||
"title": "BAG Building",
|
||||
"description": {
|
||||
|
@ -202,12 +202,11 @@
|
|||
"source": {
|
||||
"geoJson": "https://service.pdok.nl/lv/bag/wfs/v2_0?request=GetFeature&service=WFS&version=2.0.0&outputFormat=application%2Fjson%3B%20subtype%3Dgeojson&typeName=bag%3Apand&bbox={x_min}%2C{y_min}%2C{x_max}%2C{y_max}%2CCRS84&srsName=EPSG%3A4326",
|
||||
"geoJsonZoomLevel": 18,
|
||||
"osmTags": "identificatie~*",
|
||||
"maxCacheAge": 0
|
||||
"osmTags": "identificatie~*"
|
||||
},
|
||||
"minzoom": 18,
|
||||
"calculatedTags": [
|
||||
"_overlaps_with_buildings=overlapWith(feat)('osm:buildings').filter(f => f.feat.properties.id.indexOf('-') < 0)",
|
||||
"_overlaps_with_buildings=overlapWith(feat)('osm_buildings').filter(f => f.feat.properties.id.indexOf('-') < 0)",
|
||||
"_overlaps_with=feat(get)('_overlaps_with_buildings').find(f => f.overlap > 1 /* square meter */ )",
|
||||
"_overlaps_with_properties=feat(get)('_overlaps_with')?.feat?.properties",
|
||||
"_overlap_percentage=Math.round(100 * (feat(get)('_overlaps_with')?.overlap / feat(get)('_overlaps_with_properties')['_surface:strict']))",
|
||||
|
@ -228,7 +227,7 @@
|
|||
"render": {
|
||||
"special": {
|
||||
"type": "import_way_button",
|
||||
"targetLayer": "osm:buildings",
|
||||
"targetLayer": "osm_buildings",
|
||||
"tags": "building=$_bag_obj:building; ref:bag=$_bag_obj:ref:bag; source=BAG; source:date=$_bag_obj:source:date; start_date=$_bag_obj:start_date",
|
||||
"text": {
|
||||
"*": "Upload this building to OpenStreetMap"
|
||||
|
@ -258,7 +257,7 @@
|
|||
},
|
||||
{
|
||||
"if": "_overlaps_with!=",
|
||||
"then": "{conflate_button(osm:buildings, building=$_bag_obj:building; ref:bag=$_bag_obj:ref:bag; source=BAG; source:date=$_bag_obj:source:date; start_date=$_bag_obj:start_date, Replace the geometry in OpenStreetMap, , _osm_obj:id)}"
|
||||
"then": "{conflate_button(osm_buildings, building=$_bag_obj:building; ref:bag=$_bag_obj:ref:bag; source=BAG; source:date=$_bag_obj:source:date; start_date=$_bag_obj:start_date, Replace the geometry in OpenStreetMap, , _osm_obj:id)}"
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
|
@ -268,7 +267,7 @@
|
|||
"_bag_obj:in_construction=true"
|
||||
]
|
||||
},
|
||||
"then": "{import_way_button(osm:buildings, building=$_bag_obj:building; construction=$_bag_obj:construction; ref:bag=$_bag_obj:ref:bag; source=BAG; source:date=$_bag_obj:source:date; start_date=$_bag_obj:start_date, Upload this building to OpenStreetMap)}"
|
||||
"then": "{import_way_button(osm_buildings, building=$_bag_obj:building; construction=$_bag_obj:construction; ref:bag=$_bag_obj:ref:bag; source=BAG; source:date=$_bag_obj:source:date; start_date=$_bag_obj:start_date, Upload this building to OpenStreetMap)}"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -348,7 +347,7 @@
|
|||
},
|
||||
{
|
||||
"id": "Overlapping building",
|
||||
"render": "<div>The overlapping <a href=https://osm.org/{_osm_obj:id} target=_blank>osm:buildings</a> is a <b>{_osm_obj:building}</b> and covers <b>{_overlap_percentage}%</b> of the BAG building.<br>The BAG-building covers <b>{_reverse_overlap_percentage}%</b> of the OSM building<div><h3>BAG geometry:</h3>{minimap(21, id):height:10rem;border-radius:1rem;overflow:hidden}<h3>OSM geometry:</h3>{minimap(21,_osm_obj:id):height:10rem;border-radius:1rem;overflow:hidden}</div></div>",
|
||||
"render": "<div>The overlapping <a href=https://osm.org/{_osm_obj:id} target=_blank>osm_buildings</a> is a <b>{_osm_obj:building}</b> and covers <b>{_overlap_percentage}%</b> of the BAG building.<br>The BAG-building covers <b>{_reverse_overlap_percentage}%</b> of the OSM building<div><h3>BAG geometry:</h3>{minimap(21, id):height:10rem;border-radius:1rem;overflow:hidden}<h3>OSM geometry:</h3>{minimap(21,_osm_obj:id):height:10rem;border-radius:1rem;overflow:hidden}</div></div>",
|
||||
"condition": "_overlaps_with!="
|
||||
},
|
||||
{
|
||||
|
@ -386,19 +385,18 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"id": "bag:verblijfsobject",
|
||||
"id": "bag_verblijfsobject",
|
||||
"name": "BAG Addresses",
|
||||
"title": "BAG Address",
|
||||
"description": "Address information from the BAG register",
|
||||
"source": {
|
||||
"geoJson": "https://service.pdok.nl/lv/bag/wfs/v2_0?request=GetFeature&service=WFS&version=2.0.0&outputFormat=application%2Fjson%3B%20subtype%3Dgeojson&typeName=bag%3Averblijfsobject&bbox={x_min}%2C{y_min}%2C{x_max}%2C{y_max}%2CCRS84&srsName=EPSG%3A4326",
|
||||
"geoJsonZoomLevel": 19,
|
||||
"osmTags": "identificatie~*",
|
||||
"maxCacheAge": 0
|
||||
"osmTags": "identificatie~*"
|
||||
},
|
||||
"minzoom": 18,
|
||||
"calculatedTags": [
|
||||
"_closed_osm_addr:=closest(feat)('osm:adresses').properties",
|
||||
"_closed_osm_addr:=closest(feat)('osm_adresses').properties",
|
||||
"_bag_obj:addr:housenumber=`${feat.properties.huisnummer}${feat.properties.huisletter}${(feat.properties.toevoeging != '') ? '-' : ''}${feat.properties.toevoeging}`",
|
||||
"_bag_obj:ref:bag=Number(feat.properties.identificatie)",
|
||||
"_bag_obj:source:date=new Date().toISOString().split('T')[0]",
|
||||
|
@ -411,7 +409,7 @@
|
|||
"tagRenderings": [
|
||||
{
|
||||
"id": "Import button",
|
||||
"render": "{import_button(osm:adresses, addr:city=$woonplaats; addr:housenumber=$_bag_obj:addr:housenumber; addr:postcode=$postcode; addr:street=$openbare_ruimte; ref:bag=$_bag_obj:ref:bag; source=BAG; source:date=$_bag_obj:source:date, Upload this adress to OpenStreetMap)}",
|
||||
"render": "{import_button(osm_adresses, addr:city=$woonplaats; addr:housenumber=$_bag_obj:addr:housenumber; addr:postcode=$postcode; addr:street=$openbare_ruimte; ref:bag=$_bag_obj:ref:bag; source=BAG; source:date=$_bag_obj:source:date, Upload this adress to OpenStreetMap)}",
|
||||
"condition": "_imported_osm_object_found=false"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -73,4 +73,4 @@
|
|||
"bench_at_pt"
|
||||
],
|
||||
"widenFactor": 1.5
|
||||
}
|
||||
}
|
||||
|
|
|
@ -394,6 +394,7 @@
|
|||
},
|
||||
"minzoom": 16,
|
||||
"name": null,
|
||||
"doCount": false,
|
||||
"+tagRenderings": [
|
||||
{
|
||||
"id": "repairs_climbing_shoes",
|
||||
|
@ -460,6 +461,7 @@
|
|||
],
|
||||
"override": {
|
||||
"minzoom": 15,
|
||||
"doCount": false,
|
||||
"pointRendering": [
|
||||
{
|
||||
"iconSize": "30,30"
|
||||
|
|
|
@ -266,12 +266,6 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"enableDownload": true,
|
||||
"enablePdfDownload": true,
|
||||
"overpassTimeout": 60,
|
||||
"widenFactor": 1.1,
|
||||
"#overpassUrl": "https://overpass.kumi.systems/api/interpreter",
|
||||
"clustering": {
|
||||
"maxZoom": 1
|
||||
}
|
||||
"widenFactor": 1.1
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
"hideFromOverview": true,
|
||||
"layers": [
|
||||
{
|
||||
"id": "node2node",
|
||||
"id": "node2node_bicycle",
|
||||
"name": {
|
||||
"en": "Node to node links",
|
||||
"de": "Knotenpunktverbindungen",
|
||||
|
@ -126,7 +126,7 @@
|
|||
"pointRendering": null
|
||||
},
|
||||
{
|
||||
"id": "node",
|
||||
"id": "node_bicycle",
|
||||
"name": {
|
||||
"en": "Nodes",
|
||||
"de": "Knotenpunkte",
|
||||
|
@ -327,6 +327,7 @@
|
|||
],
|
||||
"override": {
|
||||
"minzoom": 16,
|
||||
"id": "bicycle_guidepost",
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
|
|
|
@ -290,7 +290,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"id": "all_streets",
|
||||
"id": "not_cyclestreets",
|
||||
"name": {
|
||||
"nl": "Alle straten",
|
||||
"en": "All streets",
|
||||
|
@ -403,7 +403,8 @@
|
|||
},
|
||||
"width": "5"
|
||||
}
|
||||
]
|
||||
],
|
||||
"isCounted": false
|
||||
}
|
||||
],
|
||||
"overrideAll": {
|
||||
|
@ -768,9 +769,5 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"widenFactor": 2,
|
||||
"clustering": {
|
||||
"maxZoom": 12,
|
||||
"minNeededElements": 200
|
||||
}
|
||||
"widenFactor": 2
|
||||
}
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
{
|
||||
"description": {
|
||||
"en": "This map contains elements meant for disaster preparedness and response.",
|
||||
"it": "Questa mappa contiene elementi pensati per la preparazione e risposta ai disastri.",
|
||||
"de": "Diese Karte enthält Elemente, die der Katastrophenvorsorge und dem Katastrophenschutz dienen.",
|
||||
"es": "Este mapa contiene elementos destinados a la preparación y respuesta ante desastres."
|
||||
},
|
||||
"docs": "https://wiki.openstreetmap.org/wiki/Emergency_facilities_and_amenities",
|
||||
"icon": "./assets/themes/disaster_response/CivilDefence.svg",
|
||||
"id": "disaster_response",
|
||||
"socialImage": "./assets/themes/disaster_response/social.svg",
|
||||
"title": {
|
||||
"en": "Disaster response",
|
||||
"it": "Risposta ai disastri",
|
||||
"de": "Katastrophenschutz",
|
||||
"es": "Respuesta ante desastres"
|
||||
},
|
||||
"description": {
|
||||
"en": "This map contains elements meant for disaster preparedness and response.",
|
||||
"it": "Questa mappa contiene elementi pensati per la preparazione e risposta ai disastri.",
|
||||
"de": "Diese Karte enthält Elemente, die der Katastrophenvorsorge und dem Katastrophenschutz dienen.",
|
||||
"es": "Este mapa contiene elementos destinados a la preparación y respuesta ante desastres."
|
||||
},
|
||||
"icon": "./assets/themes/disaster_response/CivilDefence.svg",
|
||||
"socialImage": "./assets/themes/disaster_response/social.svg",
|
||||
"layers": [
|
||||
"hospital",
|
||||
"assembly_point",
|
||||
"disaster_response"
|
||||
]
|
||||
],
|
||||
"docs": "https://wiki.openstreetmap.org/wiki/Emergency_facilities_and_amenities"
|
||||
}
|
|
@ -68,6 +68,7 @@
|
|||
"pl": "Ulice bez informacji o etymologii"
|
||||
},
|
||||
"minzoom": 15,
|
||||
"isCounted": false,
|
||||
"source": {
|
||||
"=osmTags": {
|
||||
"and": [
|
||||
|
@ -99,6 +100,7 @@
|
|||
"pl": "Parki i lasy bez informacji o etymologii"
|
||||
},
|
||||
"minzoom": 18,
|
||||
"isCounted": false,
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
|
@ -131,6 +133,7 @@
|
|||
"pl": "Instytucje edukacyjne bez informacji o etymologii"
|
||||
},
|
||||
"minzoom": 18,
|
||||
"isCounted": false,
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
|
@ -166,6 +169,7 @@
|
|||
"pl": "Miejsca kulturowe bez informacji o etymologii"
|
||||
},
|
||||
"minzoom": 18,
|
||||
"isCounted": false,
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
|
@ -201,6 +205,7 @@
|
|||
"pl": "Miejsca turystyczne bez informacji o etymologii"
|
||||
},
|
||||
"minzoom": 18,
|
||||
"isCounted": false,
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
|
@ -235,6 +240,7 @@
|
|||
"pl": "Miejsca związane ze zdrowiem i społeczeństwem bez informacji o etymologii"
|
||||
},
|
||||
"minzoom": 18,
|
||||
"isCounted": false,
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
|
@ -268,6 +274,7 @@
|
|||
"pl": "Miejsca sportowe bez informacji o etymologii"
|
||||
},
|
||||
"minzoom": 18,
|
||||
"isCounted": false,
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
|
@ -284,10 +291,5 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"widenFactor": 2,
|
||||
"clustering": {
|
||||
"maxZoom": 14,
|
||||
"minNeededElements": 250
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -96,7 +96,8 @@
|
|||
"override": {
|
||||
"minzoom": 18,
|
||||
"filter": null,
|
||||
"name": null
|
||||
"name": null,
|
||||
"isCounted": false
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -43,8 +43,5 @@
|
|||
"layers": [
|
||||
"ghost_bike"
|
||||
],
|
||||
"widenFactor": 5,
|
||||
"clustering": {
|
||||
"maxZoom": 0
|
||||
}
|
||||
}
|
||||
"widenFactor": 5
|
||||
}
|
||||
|
|
|
@ -140,7 +140,12 @@
|
|||
"source": {
|
||||
"osmTags": "advertising=wall_painting"
|
||||
},
|
||||
"id": "advertising_wall_paintings",
|
||||
"minzoom": 18,
|
||||
"name": {
|
||||
"en": "All advertentie wall paintings",
|
||||
"nl": "Alle adverterende muurschilderingen"
|
||||
},
|
||||
"+tagRenderings": [
|
||||
{
|
||||
"id": "historic",
|
||||
|
@ -172,8 +177,9 @@
|
|||
{
|
||||
"iconSize": "20,20"
|
||||
}
|
||||
]
|
||||
],
|
||||
"isCounted": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
"hideFromOverview": true,
|
||||
"layers": [
|
||||
{
|
||||
"id": "osm-buildings",
|
||||
"id": "osm_buildings_no_points",
|
||||
"name": "All OSM-buildings",
|
||||
"source": {
|
||||
"osmTags": {
|
||||
|
@ -42,8 +42,7 @@
|
|||
},
|
||||
"building~*"
|
||||
]
|
||||
},
|
||||
"maxCacheAge": 0
|
||||
}
|
||||
},
|
||||
"title": "OSM-gebouw",
|
||||
"tagRenderings": [
|
||||
|
@ -290,13 +289,12 @@
|
|||
"geoJson": "https://betadata.grbosm.site/grb?bbox={x_min},{y_min},{x_max},{y_max}",
|
||||
"geoJsonZoomLevel": 18,
|
||||
"mercatorCrs": true,
|
||||
"maxCacheAge": 0,
|
||||
"idKey": "osm_id"
|
||||
},
|
||||
"name": "GRB geometries",
|
||||
"title": "GRB outline",
|
||||
"calculatedTags": [
|
||||
"_overlaps_with_buildings=overlapWith(feat)('osm-buildings').filter(f => f.feat.properties.id.indexOf('-') < 0) ?? []",
|
||||
"_overlaps_with_buildings=overlapWith(feat)('osm_buildings_no_points').filter(f => f.feat.properties.id.indexOf('-') < 0) ?? []",
|
||||
"_overlaps_with=get(feat)('_overlaps_with_buildings').find(f => f.overlap > 1 /* square meter */ )",
|
||||
"_osm_obj:source:ref=get(feat)('_overlaps_with')?.feat?.properties['source:geometry:ref']",
|
||||
"_osm_obj:id=get(feat)('_overlaps_with')?.feat?.properties?.id",
|
||||
|
@ -319,7 +317,7 @@
|
|||
"tagRenderings": [
|
||||
{
|
||||
"id": "Import-button",
|
||||
"render": "{import_way_button(osm-buildings,building=$building;man_made=$man_made; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref; addr:street=$addr:street; addr:housenumber=$addr:housenumber; building:min_level=$_building:min_level, Upload this building to OpenStreetMap,,_is_part_of_building=true,1,_moveable=true)}",
|
||||
"render": "{import_way_button(osm_buildings_no_points,building=$building;man_made=$man_made; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref; addr:street=$addr:street; addr:housenumber=$addr:housenumber; building:min_level=$_building:min_level, Upload this building to OpenStreetMap,,_is_part_of_building=true,1,_moveable=true)}",
|
||||
"mappings": [
|
||||
{
|
||||
"#": "Failsafe",
|
||||
|
@ -371,7 +369,7 @@
|
|||
"addr:housenumber!:={_osm_obj:addr:housenumber}"
|
||||
]
|
||||
},
|
||||
"then": "{conflate_button(osm-buildings,building=$_target_building_type; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref; addr:street=$addr:street; addr:housenumber=$addr:housenumber, Replace the geometry in OpenStreetMap and add the address,,_osm_obj:id)}"
|
||||
"then": "{conflate_button(osm_buildings_no_points,building=$_target_building_type; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref; addr:street=$addr:street; addr:housenumber=$addr:housenumber, Replace the geometry in OpenStreetMap and add the address,,_osm_obj:id)}"
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
|
@ -380,7 +378,7 @@
|
|||
"_reverse_overlap_percentage>50"
|
||||
]
|
||||
},
|
||||
"then": "{conflate_button(osm-buildings,building=$_target_building_type; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref, Replace the geometry in OpenStreetMap,,_osm_obj:id)}"
|
||||
"then": "{conflate_button(osm_buildings_no_points,building=$_target_building_type; source:geometry:date=$_grb_date; source:geometry:ref=$_grb_ref, Replace the geometry in OpenStreetMap,,_osm_obj:id)}"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -587,8 +585,7 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"maxCacheAge": 0
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
|
@ -612,7 +609,7 @@
|
|||
"builtin": "crab_address",
|
||||
"override": {
|
||||
"calculatedTags+": [
|
||||
"_embedded_in=overlapWith(feat)('osm-buildings').filter(b => /* Do not match newly created objects */ b.feat.properties.id.indexOf('-') < 0)[0]?.feat?.properties ?? {}",
|
||||
"_embedded_in=overlapWith(feat)('osm_buildings_no_points').filter(b => /* Do not match newly created objects */ b.feat.properties.id.indexOf('-') < 0)[0]?.feat?.properties ?? {}",
|
||||
"_embedding_nr=get(feat)('_embedded_in')['addr:housenumber']+(get(feat)('_embedded_in')['addr:unit'] ?? '')",
|
||||
"_embedding_street=get(feat)('_embedded_in')['addr:street']",
|
||||
"_embedding_id=get(feat)('_embedded_in').id",
|
||||
|
@ -709,7 +706,7 @@
|
|||
"text": {
|
||||
"nl": "Voeg dit adres als een nieuw adrespunt toe"
|
||||
},
|
||||
"snap_onto_layers": "osm-buildings"
|
||||
"snap_onto_layers": "osm_buildings_no_points"
|
||||
}
|
||||
},
|
||||
"mappings": [
|
||||
|
@ -785,12 +782,10 @@
|
|||
}
|
||||
],
|
||||
"overrideAll": {
|
||||
"minzoom": 17
|
||||
"minzoom": 17,
|
||||
"doCount": false
|
||||
},
|
||||
"widenFactor": 2,
|
||||
"overpassMaxZoom": 15,
|
||||
"osmApiTileSize": 17,
|
||||
"clustering": {
|
||||
"maxZoom": 15
|
||||
}
|
||||
"osmApiTileSize": 17
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
{
|
||||
"builtin": "shops",
|
||||
"override": {
|
||||
"id": "medical-shops",
|
||||
"id": "medical_shops",
|
||||
"minzoom": 13,
|
||||
"=filter": [
|
||||
"open_now",
|
||||
|
@ -113,6 +113,7 @@
|
|||
"override": {
|
||||
"=presets": [],
|
||||
"name": null,
|
||||
"doCount": false,
|
||||
"minzoom": 18
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,5 @@
|
|||
"icon": "./assets/layers/ice_cream/ice_cream.svg",
|
||||
"layers": [
|
||||
"ice_cream"
|
||||
],
|
||||
"minzoom": "14"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"id": "items_with_image",
|
||||
"hideFromOverview": true,
|
||||
"title": {
|
||||
"en": "All items with images"
|
||||
},
|
||||
|
@ -8,6 +7,7 @@
|
|||
"en": "A map showing all items on OSM which have an image. This theme is a very bad fit for MapComplete as someone is not able to directly add a picture. However, this theme is mostly here to include this all into the database, which'll allow this to quickly fetch images nearby for other features"
|
||||
},
|
||||
"icon": "./assets/layers/item_with_image/camera.svg",
|
||||
"hideFromOverview": true,
|
||||
"layers": [
|
||||
"item_with_image"
|
||||
]
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
{
|
||||
"builtin": "crossings",
|
||||
"override": {
|
||||
"id": "crossings_no_traffic_lights",
|
||||
"=presets": [
|
||||
{
|
||||
"title": {
|
||||
|
@ -87,4 +88,4 @@
|
|||
},
|
||||
"kerbs"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
"startLat": 0,
|
||||
"startLon": 0,
|
||||
"startZoom": 1,
|
||||
"widenFactor": 0.05,
|
||||
"clustering": false,
|
||||
"layers": [
|
||||
{
|
||||
"id": "mapcomplete-changes",
|
||||
|
@ -26,8 +24,7 @@
|
|||
"source": {
|
||||
"osmTags": "editor~*",
|
||||
"geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/mapcomplete-changes/tile_{z}_{x}_{y}.geojson",
|
||||
"geoJsonZoomLevel": 8,
|
||||
"maxCacheAge": 0
|
||||
"geoJsonZoomLevel": 8
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
"startLat": 0,
|
||||
"startLon": 0,
|
||||
"startZoom": 1,
|
||||
"widenFactor": 0.05,
|
||||
"clustering": false,
|
||||
"layers": [
|
||||
{
|
||||
"id": "mapcomplete-changes",
|
||||
|
@ -26,8 +24,7 @@
|
|||
"source": {
|
||||
"osmTags": "editor~*",
|
||||
"geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/mapcomplete-changes/tile_{z}_{x}_{y}.geojson",
|
||||
"geoJsonZoomLevel": 8,
|
||||
"maxCacheAge": 0
|
||||
"geoJsonZoomLevel": 8
|
||||
},
|
||||
"title": {
|
||||
"render": {
|
||||
|
|
|
@ -32,6 +32,5 @@
|
|||
"note",
|
||||
"fixme"
|
||||
],
|
||||
"enableDownload": true,
|
||||
"clustering": false
|
||||
"enableDownload": true
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
{
|
||||
"builtin": "shops",
|
||||
"override": {
|
||||
"id": "erotic-shop",
|
||||
"id": "erotic_shop",
|
||||
"source": {
|
||||
"osmTags": "shop=erotic"
|
||||
},
|
||||
|
@ -51,6 +51,7 @@
|
|||
"minzoom": 18,
|
||||
"=presets": [],
|
||||
"=name": null,
|
||||
"doCount": false,
|
||||
"=filter": {
|
||||
"sameAs": "erotic-shop"
|
||||
}
|
||||
|
@ -125,6 +126,7 @@
|
|||
"override": {
|
||||
"minzoom": 18,
|
||||
"=presets": [],
|
||||
"doCount": false,
|
||||
"=name": null
|
||||
}
|
||||
},
|
||||
|
@ -203,6 +205,7 @@
|
|||
"override": {
|
||||
"minzoom": 18,
|
||||
"=presets": [],
|
||||
"doCount": false,
|
||||
"=name": null
|
||||
}
|
||||
},
|
||||
|
@ -237,6 +240,7 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"doCount": false,
|
||||
"=presets": []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,8 +41,5 @@
|
|||
],
|
||||
"layers": [
|
||||
"windturbine"
|
||||
],
|
||||
"clustering": {
|
||||
"maxZoom": 8
|
||||
}
|
||||
]
|
||||
}
|
|
@ -36,6 +36,5 @@
|
|||
"startLon": 4.351697,
|
||||
"layers": [
|
||||
"osm_community_index"
|
||||
],
|
||||
"clustering": false
|
||||
]
|
||||
}
|
|
@ -44,8 +44,5 @@
|
|||
"enableNoteImports": false,
|
||||
"widenFactor": 1.2,
|
||||
"overpassMaxZoom": 15,
|
||||
"clustering": {
|
||||
"maxZoom": 19
|
||||
},
|
||||
"#note": "The 'overpassMaxZoom' should be exactly the same as or less then the minzzom in overrideAll"
|
||||
}
|
|
@ -231,6 +231,5 @@
|
|||
}
|
||||
],
|
||||
"overpassTimeout": 180,
|
||||
"widenFactor": 0.05,
|
||||
"clustering": false
|
||||
"widenFactor": 0.05
|
||||
}
|
||||
|
|
|
@ -103,9 +103,5 @@
|
|||
}
|
||||
}
|
||||
],
|
||||
"widenFactor": 1.5,
|
||||
"clustering": {
|
||||
"maxZoom": 14,
|
||||
"minNeededElements": 100
|
||||
}
|
||||
"widenFactor": 1.5
|
||||
}
|
|
@ -55,5 +55,7 @@
|
|||
"ice_cream",
|
||||
"trolley_bay"
|
||||
],
|
||||
"widenFactor": 3
|
||||
}
|
||||
"overrideAll": {
|
||||
"minzoom": 16
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
"es": "Todo lo que necesitas para esquiar"
|
||||
},
|
||||
"icon": "./assets/layers/aerialway/chair_lift.svg",
|
||||
"enableTerrain": true,
|
||||
"layers": [
|
||||
"ski_piste",
|
||||
"aerialway",
|
||||
|
@ -50,5 +49,6 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"enableTerrain": true
|
||||
}
|
||||
|
|
|
@ -23,8 +23,7 @@
|
|||
"title": null,
|
||||
"source": {
|
||||
"geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete/master/assets/themes/speelplekken/shadow.geojson",
|
||||
"osmTags": "shadow=yes",
|
||||
"isOsmCache": false
|
||||
"osmTags": "shadow=yes"
|
||||
},
|
||||
"pointRendering": null,
|
||||
"lineRendering": [
|
||||
|
@ -37,11 +36,6 @@
|
|||
{
|
||||
"builtin": "play_forest",
|
||||
"override": {
|
||||
"source": {
|
||||
"geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/speelplekken_cache/speelplekken_{layer}_{z}_{x}_{y}.geojson",
|
||||
"geoJsonZoomLevel": 14,
|
||||
"isOsmCache": true
|
||||
},
|
||||
"minzoom": 12,
|
||||
"calculatedTags": [
|
||||
"_is_shadowed=overlapWith(feat)('shadow').length > 0 ? 'yes': ''",
|
||||
|
@ -53,12 +47,6 @@
|
|||
"builtin": "playground",
|
||||
"override": {
|
||||
"minzoom": 14,
|
||||
"source": {
|
||||
"geoJsonLocal": "http://127.0.0.1:8080/speelplekken_{layer}_{z}_{x}_{y}.geojson",
|
||||
"geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/speelplekken_cache/speelplekken_{layer}_{z}_{x}_{y}.geojson",
|
||||
"geoJsonZoomLevel": 14,
|
||||
"isOsmCache": true
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_is_shadowed=overlapWith(feat)('shadow').length > 0 ? 'yes': ''",
|
||||
"_video:id=feat.properties.video === undefined ? undefined : new URL(feat.properties.video).searchParams.get('v')"
|
||||
|
@ -69,12 +57,6 @@
|
|||
"builtin": "village_green",
|
||||
"override": {
|
||||
"minzoom": 14,
|
||||
"source": {
|
||||
"geoJsonLocal": "http://127.0.0.1:8080/speelplekken_{layer}_{z}_{x}_{y}.geojson",
|
||||
"geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/speelplekken_cache/speelplekken_{layer}_{z}_{x}_{y}.geojson",
|
||||
"geoJsonZoomLevel": 14,
|
||||
"isOsmCache": true
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_is_shadowed=overlapWith(feat)('shadow').length > 0 ? 'yes': ''",
|
||||
"_video:id=feat.properties.video === undefined ? undefined : new URL(feat.properties.video).searchParams.get('v')"
|
||||
|
@ -85,12 +67,6 @@
|
|||
"builtin": "sport_pitch",
|
||||
"override": {
|
||||
"minzoom": 15,
|
||||
"source": {
|
||||
"geoJsonLocal": "http://127.0.0.1:8080/speelplekken_{layer}_{z}_{x}_{y}.geojson",
|
||||
"geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/speelplekken_cache/speelplekken_{layer}_{z}_{x}_{y}.geojson",
|
||||
"geoJsonZoomLevel": 14,
|
||||
"isOsmCache": true
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_is_shadowed=overlapWith(feat)('shadow').length > 0 ? 'yes': ''",
|
||||
"_video:id=feat.properties.video === undefined ? undefined : new URL(feat.properties.video).searchParams.get('v')"
|
||||
|
@ -102,13 +78,7 @@
|
|||
"override": {
|
||||
"calculatedTags": [
|
||||
"_is_shadowed=overlapWith(feat)('shadow').length > 0 ? 'yes': ''"
|
||||
],
|
||||
"source": {
|
||||
"geoJsonLocal": "http://127.0.0.1:8080/speelplekken_{layer}_{z}_{x}_{y}.geojson",
|
||||
"geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/speelplekken_cache/speelplekken_{layer}_{z}_{x}_{y}.geojson",
|
||||
"geoJsonZoomLevel": 14,
|
||||
"isOsmCache": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -124,10 +94,7 @@
|
|||
"route=foot",
|
||||
"operator~[pP]rovincie Antwerpen"
|
||||
]
|
||||
},
|
||||
"geoJson": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/speelplekken_cache/speelplekken_{layer}_{z}_{x}_{y}.geojson",
|
||||
"geoJsonZoomLevel": 14,
|
||||
"isOsmCache": true
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": "Wandeling <i>{name}</i>",
|
||||
|
@ -260,9 +227,5 @@
|
|||
51.23978120350654
|
||||
]
|
||||
],
|
||||
"widenFactor": 1.2,
|
||||
"clustering": {
|
||||
"maxZoom": 6,
|
||||
"minNeededElements": 100
|
||||
}
|
||||
"widenFactor": 1.2
|
||||
}
|
||||
|
|
|
@ -23,8 +23,7 @@
|
|||
"osmTags": "Lichtmastnummer~*",
|
||||
"#geoJson": "https://opendata.arcgis.com/datasets/ba37cdb372064b3199c548b75d16a609_0.geojson",
|
||||
"geoJson": "https://robinlinde.github.io/tiles/assen_street_lighting/{z}/{x}/{y}.json",
|
||||
"geoJsonZoomLevel": 16,
|
||||
"isOsmCache": false
|
||||
"geoJsonZoomLevel": 16
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_closest_osm_street_lamp=closest(feat)('street_lamps')?.properties?.id",
|
||||
|
|
|
@ -44,4 +44,4 @@
|
|||
"shower"
|
||||
],
|
||||
"widenFactor": 3
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
"builtin": "shelter",
|
||||
"override": {
|
||||
"minzoom": 18,
|
||||
"id": "pt_shelter",
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
|
|
|
@ -71,9 +71,5 @@
|
|||
"tree_node"
|
||||
],
|
||||
"widenFactor": 0.2,
|
||||
"osmApiTileSize": 18,
|
||||
"clustering": {
|
||||
"maxZoom": 19,
|
||||
"minNeededElements": 25
|
||||
}
|
||||
"osmApiTileSize": 18
|
||||
}
|
||||
|
|
|
@ -32,8 +32,7 @@
|
|||
"source": {
|
||||
"geoJson": "https://osm-uk-addresses.russss.dev/inspire/{z}/{x}/{y}.json",
|
||||
"osmTags": "inspireid~*",
|
||||
"geoJsonZoomLevel": 18,
|
||||
"isOsmCache": false
|
||||
"geoJsonZoomLevel": 18
|
||||
},
|
||||
"minzoom": 18,
|
||||
"calculatedTags": [
|
||||
|
@ -69,8 +68,7 @@
|
|||
"id!~node/.*"
|
||||
]
|
||||
},
|
||||
"geoJsonZoomLevel": 16,
|
||||
"isOsmCache": false
|
||||
"geoJsonZoomLevel": 16
|
||||
},
|
||||
"name": "Addresses to check",
|
||||
"minzoom": 18,
|
||||
|
@ -185,7 +183,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"id": "address",
|
||||
"id": "uk_address",
|
||||
"name": {
|
||||
"en": "Known addresses in OSM"
|
||||
},
|
||||
|
@ -703,9 +701,5 @@
|
|||
],
|
||||
"enableShareScreen": false,
|
||||
"enableMoreQuests": false,
|
||||
"widenFactor": 1.01,
|
||||
"clustering": {
|
||||
"minNeededFeatures": 25,
|
||||
"maxZoom": 16
|
||||
}
|
||||
"widenFactor": 1.01
|
||||
}
|
||||
|
|
|
@ -3,45 +3,33 @@
|
|||
"title": {
|
||||
"nl": "Velopark naar OpenStreetMap sync tool"
|
||||
},
|
||||
"shortDescription": {
|
||||
"en": "A tool to import data from velopark.be into OpenStreetMap",
|
||||
"nl": "Een hulpmiddel om data van velopark.be in OpenStreetMap in te laden"
|
||||
},
|
||||
"mustHaveLanguage": [
|
||||
"nl"
|
||||
],
|
||||
"description": {
|
||||
"en": "<p><a href='https://velopark.be' target='_blank'>Velopark.be</a> is a website collecting data about bicycle parkings in a semi-crowdsourced way. However, only 'authorized' instances are allowed to make changes there, in practice the operator of the bicycle parking such as SNCB, de Lijn or the municipality. They have now decided to synchronize their dataset with OpenStreetMap, and this MapComplete-instance is set up to help link and import their data into OpenStreetMap.</p> How to use: <ul><li>A velopark-icon on the map (yellow with bicycle silhouette) represents a bicycle known by Velopark but not yet known by OpenStreetMap</li><li>Blue pins are bicycle parkings known by OpenStreetMap</li><li>Light blue pins are bicycle parkings known by OpenStreetMap with a reference to Velopark.be (<span class='literal-code'>ref-velopark=*</span>)</li><li>Click a velopark item, you can either link it with a nearby OSM-bicycle parking or create a new bicycle parking. Note that the geometry of Velopark is often incorrect and can be a few up till 100 meters away from the actual bicycle parking. Use aerial imagery, linked images and streetview to determine the correct location</li><li>Once linked, you can compare the Velopark- and OSM-attributes and apply correct attributes</li><li>If Velopark has an image, you can also link the image</li></ul> That's it! Thanks for helping to import this!",
|
||||
"nl": "<p><a href='https://velopark.be' target='_blank'>Velopark.be</a> is een website die data verzamelt over fietsenstallingen in een semi-crowdsource manier. Hierbij kunnen enkel geautorizeerde gebruikers data bijdragen, in de praktijk de uitbaters van de fietsenstallingen zoals de bevoegde gemeentebesturen, de NMBS of de Lijn. Velopark.be heeft nu beslist om hun data met OpenStreetMap te synchronizeren. Deze website is de tool om van Velopark.be naar OpenStreetMap te gaan en hun data te importeren.</p> Hoe te gebruiken? <ul><li>Een velopark-logo op de kaart (geel met een fietssilhouette) duidt een fietsenstalling aan die gekend is in Velopark maar nog niet gekend (of gelinkt) is aan een fietsenstalling in OpenStreetMap</li><li>Een blauwe pin duidt een fietsenstalling aan die gekend is in OpenStreetMap</li><li>Een licht-blauwe pin duidt een fietsenstalling aan uit OpenStreetMap die een link heeft naar Velopark.be (<span class='literal-code'>ref-velopark=*</span>)</li><li>Als je op een velopark-item klikt op, kan je deze linken met een fietsenstalling in de buurt (<25m) of een nieuwe fietstalling aan OpenStreetMap toevoegen. Let op: de geometrie van Velopark is zelden correct en wijkt makkelijk 10 meter of meer af van de echte locatie - in uitzonderlijke gevallen zelfs tot meer dan 100 meter. Gebruik de meest recente luchtfoto's, de gelinkte foto's en mapillary om de correcte locatie te bepalen</li><li>Eens gelinkt, kan je de Velopark- en OSM-attributen vergelijken en de correcte attributen toepassen in OpenStreetMap</li><li>Indien velopark een foto heeft, kan je die ook nog linken</li></ul> Dat is het! Bedankt om mee te helpen!"
|
||||
},
|
||||
"shortDescription": {
|
||||
"en": "A tool to import data from velopark.be into OpenStreetMap",
|
||||
"nl": "Een hulpmiddel om data van velopark.be in OpenStreetMap in te laden"
|
||||
},
|
||||
"descriptionTail": {
|
||||
"*": "<h3>Maintainer tools</h3><ul><li><a class='link-underline' href='https://maproulette.org/api/v2/challenge/view/43282' download='Velopark_sync_2024-01-15.geojson'>Download the first sync results</a></li></ul>"
|
||||
},
|
||||
"hideFromOverview": true,
|
||||
"icon": "./assets/themes/velopark/velopark.svg",
|
||||
"mustHaveLanguage": [
|
||||
"nl"
|
||||
],
|
||||
"lockLocation": [
|
||||
[
|
||||
2.51357303225,
|
||||
49.5294835476
|
||||
],
|
||||
[
|
||||
6.15665815596,
|
||||
51.4750237087
|
||||
]
|
||||
],
|
||||
"startZoom": 18,
|
||||
"startLat": 51.03753,
|
||||
"startLon": 3.71025,
|
||||
"startZoom": 18,
|
||||
"defaultBackgroundId": "photo",
|
||||
"enableNoteImports": false,
|
||||
"hideFromOverview": true,
|
||||
"layers": [
|
||||
{
|
||||
"id": "velopark_maproulette",
|
||||
"description": "Maproulette challenge containing velopark data",
|
||||
"source": {
|
||||
"osmTags": "mr_taskId~*",
|
||||
"geoJson": "https://maproulette.org/api/v2/challenge/view/43282",
|
||||
"isOsmCache": false
|
||||
"geoJson": "https://maproulette.org/api/v2/challenge/view/43282"
|
||||
},
|
||||
"title": {
|
||||
"render": "Velopark parking <b>{mr_velopark_id}</b>"
|
||||
|
@ -281,6 +269,7 @@
|
|||
"bike_parking"
|
||||
],
|
||||
"override": {
|
||||
"doCount": false,
|
||||
"minzoom": 14
|
||||
}
|
||||
},
|
||||
|
@ -291,6 +280,7 @@
|
|||
"bicycle_rental"
|
||||
],
|
||||
"override": {
|
||||
"doCount": false,
|
||||
"minzoom": 18
|
||||
}
|
||||
}
|
||||
|
@ -341,5 +331,16 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"lockLocation": [
|
||||
[
|
||||
2.51357303225,
|
||||
49.5294835476
|
||||
],
|
||||
[
|
||||
6.15665815596,
|
||||
51.4750237087
|
||||
]
|
||||
],
|
||||
"enableNoteImports": false
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
"override": {
|
||||
"id": "all_vending_machine",
|
||||
"name": null,
|
||||
"doCount": false,
|
||||
"filter": {
|
||||
"sameAs": "vending_machine"
|
||||
},
|
||||
|
@ -49,6 +50,7 @@
|
|||
{
|
||||
"builtin": "parking_ticket_machine",
|
||||
"override": {
|
||||
"doCount": false,
|
||||
"name": null,
|
||||
"minzoom": 18
|
||||
}
|
||||
|
@ -56,6 +58,7 @@
|
|||
{
|
||||
"builtin": "elongated_coin",
|
||||
"override": {
|
||||
"doCount": false,
|
||||
"name": null,
|
||||
"minzoom": 18
|
||||
}
|
||||
|
@ -63,9 +66,10 @@
|
|||
{
|
||||
"builtin": "ticket_machine",
|
||||
"override": {
|
||||
"doCount": false,
|
||||
"name": null,
|
||||
"minzoom": 18
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
"hideFromOverview": true,
|
||||
"layers": [
|
||||
{
|
||||
"id": "node2node",
|
||||
"id": "node2node_hiking",
|
||||
"name": {
|
||||
"en": "Node to node links",
|
||||
"de": "Knotenpunktverbindungen",
|
||||
|
@ -122,7 +122,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"id": "node",
|
||||
"id": "node_hiking",
|
||||
"name": {
|
||||
"en": "Nodes",
|
||||
"de": "Knotenpunkte",
|
||||
|
@ -293,6 +293,7 @@
|
|||
],
|
||||
"override": {
|
||||
"minzoom": 16,
|
||||
"id": "guidepost_hiking",
|
||||
"source": {
|
||||
"osmTags": {
|
||||
"and": [
|
||||
|
|
|
@ -27,8 +27,7 @@
|
|||
"description": "Laag op basis van externe data",
|
||||
"source": {
|
||||
"osmTags": "OBJECTID~*",
|
||||
"geoJson": "https://opendata.arcgis.com/datasets/5b6953ac5a9d4616a7dc75ab0beeac2f_0.geojson",
|
||||
"isOsmCache": false
|
||||
"geoJson": "https://opendata.arcgis.com/datasets/5b6953ac5a9d4616a7dc75ab0beeac2f_0.geojson"
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_closest_osm_waste_basket=closest(feat)('waste_basket')?.properties?.id",
|
||||
|
@ -71,8 +70,7 @@
|
|||
"description": "Laag op basis van externe data",
|
||||
"source": {
|
||||
"osmTags": "OBJECTID~*",
|
||||
"geoJson": "https://opendata.arcgis.com/datasets/edb893998e27461b8ed82aad9854d27d_0.geojson",
|
||||
"isOsmCache": false
|
||||
"geoJson": "https://opendata.arcgis.com/datasets/edb893998e27461b8ed82aad9854d27d_0.geojson"
|
||||
},
|
||||
"calculatedTags": [
|
||||
"_closest_osm_recycling=closest(feat)('recycling')?.properties?.id",
|
||||
|
|
|
@ -275,6 +275,5 @@
|
|||
"enableLayers": false,
|
||||
"enableSearch": false,
|
||||
"enableBackgroundLayerSelection": false,
|
||||
"widenFactor": 0.05,
|
||||
"clustering": false
|
||||
"widenFactor": 0.05
|
||||
}
|
||||
|
|
|
@ -214,6 +214,7 @@
|
|||
"current_view_generic": "Export a PDF off the current view for {paper_size} in {orientation} orientation"
|
||||
},
|
||||
"title": "Download",
|
||||
"toMuch": "There are to much features to download them all",
|
||||
"uploadGpx": "Upload your track to OpenStreetMap"
|
||||
},
|
||||
"enableGeolocationForSafari": "Did you not get the popup to ask for geopermission?",
|
||||
|
@ -784,7 +785,9 @@
|
|||
"feedback": "This is not a valid web address"
|
||||
},
|
||||
"wikidata": {
|
||||
"description": "A Wikidata identifier"
|
||||
"description": "A Wikidata identifier",
|
||||
"empty": "Please, enter some wikidata-entries",
|
||||
"startsWithQ": "A wikidata identifier starts with Q and is followed by a number"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5518,27 +5518,24 @@
|
|||
"then": "Aquesta és una plaça d'aparcament per a minusvàlids."
|
||||
},
|
||||
"3": {
|
||||
"then": "Es tracta d'una plaça d'aparcament privada."
|
||||
},
|
||||
"4": {
|
||||
"then": "Es tracta d'una plaça d'aparcament reservada per a la recàrrega de vehicles."
|
||||
},
|
||||
"5": {
|
||||
"4": {
|
||||
"then": "Es tracta d'una plaça d'aparcament reservada per a repartidors."
|
||||
},
|
||||
"8": {
|
||||
"7": {
|
||||
"then": "Es tracta d'una plaça d'aparcament reservada per a autobusos."
|
||||
},
|
||||
"9": {
|
||||
"8": {
|
||||
"then": "Es tracta d'una plaça d'aparcament reservada per a motos."
|
||||
},
|
||||
"10": {
|
||||
"9": {
|
||||
"then": "Es tracta d'una plaça d'aparcament reservada per a pares amb fills."
|
||||
},
|
||||
"11": {
|
||||
"10": {
|
||||
"then": "Es tracta d'una plaça d'aparcament reservada al personal."
|
||||
},
|
||||
"12": {
|
||||
"11": {
|
||||
"then": "Aquest espai d'aparcament està reservat per a taxi."
|
||||
}
|
||||
},
|
||||
|
@ -7103,6 +7100,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"souvenir_note": {
|
||||
"tagRenderings": {
|
||||
"designs": {
|
||||
"freeform": {
|
||||
"placeholder": "Nombre de dissenys (p. e. 5)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"speed_camera": {
|
||||
"description": "Capa que mostra càmeres de velocitat",
|
||||
"name": "Càmera de velocitat",
|
||||
|
|
|
@ -5732,39 +5732,36 @@
|
|||
"then": "Jedná se o parkovací místo pro osoby se zdravotním postižením."
|
||||
},
|
||||
"3": {
|
||||
"then": "Jedná se o soukromé parkovací místo."
|
||||
},
|
||||
"4": {
|
||||
"then": "Jedná se o parkovací místo vyhrazené pro nabíjení vozidel."
|
||||
},
|
||||
"5": {
|
||||
"4": {
|
||||
"then": "Jedná se o parkovací místo vyhrazené pro dodávky."
|
||||
},
|
||||
"6": {
|
||||
"5": {
|
||||
"then": "Jedná se o parkovací místo vyhrazené pro těžká nákladní vozidla."
|
||||
},
|
||||
"7": {
|
||||
"6": {
|
||||
"then": "Jedná se o parkovací místo vyhrazené pro karavany nebo obytná vozidla."
|
||||
},
|
||||
"8": {
|
||||
"7": {
|
||||
"then": "Jedná se o parkovací místo vyhrazené pro autobusy."
|
||||
},
|
||||
"9": {
|
||||
"8": {
|
||||
"then": "Jedná se o parkovací místo vyhrazené pro motocykly."
|
||||
},
|
||||
"10": {
|
||||
"9": {
|
||||
"then": "Jedná se o parkovací místo vyhrazené pro rodiče s dětmi."
|
||||
},
|
||||
"11": {
|
||||
"10": {
|
||||
"then": "Jedná se o parkovací místo vyhrazené pro zaměstnance."
|
||||
},
|
||||
"12": {
|
||||
"11": {
|
||||
"then": "Jedná se o parkovací místo vyhrazené pro taxíky."
|
||||
},
|
||||
"13": {
|
||||
"12": {
|
||||
"then": "Jedná se o parkovací místo vyhrazené pro vozidla táhnoucí přívěs."
|
||||
},
|
||||
"14": {
|
||||
"13": {
|
||||
"then": "Jedná se o parkovací místo vyhrazené pro sdílení automobilů."
|
||||
}
|
||||
},
|
||||
|
@ -7399,6 +7396,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"souvenir_note": {
|
||||
"tagRenderings": {
|
||||
"designs": {
|
||||
"freeform": {
|
||||
"placeholder": "Počet vzorů (např. 5)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"speed_camera": {
|
||||
"description": "Vrstva zobrazující rychlostní radary",
|
||||
"name": "Rychlostní radar",
|
||||
|
|
|
@ -258,6 +258,17 @@
|
|||
"question": "Wie viele Leute passen in eine Kabine?",
|
||||
"render": "{aerialway:occupancy} Leute passen in eine Kabine"
|
||||
},
|
||||
"oneway": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Diese Seilbahn kann nur nach oben gefahren werden"
|
||||
},
|
||||
"1": {
|
||||
"then": "Diese Seilbahn kann in beide Richtungen befahren werden"
|
||||
}
|
||||
},
|
||||
"question": "In welche Richtung kann diese Seilbahn genutzt werden?"
|
||||
},
|
||||
"type": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
|
@ -5101,6 +5112,11 @@
|
|||
"ghost_bike-start_date": {
|
||||
"question": "Wann wurde dieses Geisterrad aufgestellt?",
|
||||
"render": "Aufgestellt am {start_date}"
|
||||
},
|
||||
"wikidata": {
|
||||
"render": {
|
||||
"before": "<h3>Wikipedia-Seite über die verstorbene Person</h3>"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
|
@ -6108,6 +6124,18 @@
|
|||
"render": "Gedenktafel"
|
||||
}
|
||||
},
|
||||
"mountain_rescue": {
|
||||
"description": "Ein Gebäude, in dem die Ersthelfer Material lagern und möglicherweise Wache halten",
|
||||
"name": "Bergrettungsstationen",
|
||||
"presets": {
|
||||
"0": {
|
||||
"title": "eine Bergrettungsstation"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": "Bergrettungsstation"
|
||||
}
|
||||
},
|
||||
"nature_reserve": {
|
||||
"description": "Ein Naturschutzgebiet ist ein Gebiet, das der Natur überlassen wurde",
|
||||
"filter": {
|
||||
|
@ -6642,39 +6670,36 @@
|
|||
"then": "Dies ist ein Behindertenstellplatz."
|
||||
},
|
||||
"3": {
|
||||
"then": "Dies ist ein privater Stellplatz."
|
||||
},
|
||||
"4": {
|
||||
"then": "Dies ist ein Stellplatz, der für das Laden von Fahrzeugen reserviert ist."
|
||||
},
|
||||
"5": {
|
||||
"4": {
|
||||
"then": "Dies ist ein Stellplatz, der für Lieferfahrzeuge reserviert ist."
|
||||
},
|
||||
"6": {
|
||||
"5": {
|
||||
"then": "Dies ist ein Stellplatz, der für Lastkraftwagen reserviert ist."
|
||||
},
|
||||
"7": {
|
||||
"6": {
|
||||
"then": "Dieser Stellplatz ist für Wohnwagen oder Wohnmobile reserviert."
|
||||
},
|
||||
"8": {
|
||||
"7": {
|
||||
"then": "Dies ist ein Stellplatz, der für Busse reserviert ist."
|
||||
},
|
||||
"9": {
|
||||
"8": {
|
||||
"then": "Dies ist ein Stellplatz, der für Motorräder reserviert ist."
|
||||
},
|
||||
"10": {
|
||||
"9": {
|
||||
"then": "Dies ist ein Stellplatz, der für Eltern mit Kindern reserviert ist."
|
||||
},
|
||||
"11": {
|
||||
"10": {
|
||||
"then": "Dies ist ein Stellplatz, der für das Personal reserviert ist."
|
||||
},
|
||||
"12": {
|
||||
"11": {
|
||||
"then": "Dies ist ein Stellplatz, der für Taxis reserviert ist."
|
||||
},
|
||||
"13": {
|
||||
"12": {
|
||||
"then": "Dies ist ein Stellplatz, der für Fahrzeuge mit Anhänger reserviert ist."
|
||||
},
|
||||
"14": {
|
||||
"13": {
|
||||
"then": "Dies ist ein Stellplatz, der für Carsharing reserviert ist."
|
||||
}
|
||||
},
|
||||
|
@ -6685,6 +6710,9 @@
|
|||
"mappings": {
|
||||
"0": {
|
||||
"then": "Behindertenparkplatz"
|
||||
},
|
||||
"1": {
|
||||
"then": "Parkplatz zum Laden von Elektrofahrzeugen"
|
||||
}
|
||||
},
|
||||
"render": "Stellplatz"
|
||||
|
@ -8535,6 +8563,15 @@
|
|||
"render": "Geschwindigkeitsreduzierte Straße"
|
||||
}
|
||||
},
|
||||
"souvenir_note": {
|
||||
"tagRenderings": {
|
||||
"designs": {
|
||||
"freeform": {
|
||||
"placeholder": "Motivanzahl (z.B. 5)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"speed_camera": {
|
||||
"description": "Ebene mit Blitzern",
|
||||
"name": "Blitzer",
|
||||
|
@ -9327,6 +9364,48 @@
|
|||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"gender_segregated": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Es gibt einen separaten, ausgeschilderten Bereich für Männer und Frauen"
|
||||
},
|
||||
"1": {
|
||||
"then": "Es gibt keinen getrennten, ausgeschilderten Bereich für Männer und Frauen"
|
||||
}
|
||||
},
|
||||
"question": "Sind diese Toiletten geschlechtergetrennt?",
|
||||
"questionHint": "Gibt es getrennte Kabinen oder getrennte Bereiche für Männer und Frauen, und sind sie als solche ausgeschildert?"
|
||||
},
|
||||
"menstrual_products": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Für alle Besucher dieser Toiletten stehen kostenlose Menstruationsprodukte zur Verfügung"
|
||||
},
|
||||
"1": {
|
||||
"then": "Für einige Besucher dieser Toiletten gibt es kostenlose Menstruationsprodukte"
|
||||
},
|
||||
"2": {
|
||||
"then": "Hier gibt es keine kostenlosen Menstruationsprodukte"
|
||||
}
|
||||
},
|
||||
"question": "Werden hier kostenlose Menstruationsprodukte verteilt?",
|
||||
"questionHint": "Hier geht es nur um Menstruationsprodukte, die kostenlos sind. Wenn es z. B. einen Automaten gibt, an dem Menstruationsprodukte kostenpflichtig sind, wird er bei dieser Frage nicht berücksichtigt."
|
||||
},
|
||||
"menstrual_products_location": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Die kostenlosen Menstruationsprodukte befinden sich in der Toilette für Frauen"
|
||||
},
|
||||
"1": {
|
||||
"then": "Die kostenlosen Menstruationsprodukte befinden sich in der Toilette für Männer"
|
||||
},
|
||||
"2": {
|
||||
"then": "Die kostenlosen Menstruationsprodukte befinden sich auf der Toilette für Rollstuhlfahrende"
|
||||
}
|
||||
},
|
||||
"question": "Wo befinden sich die kostenlosen Menstruationsprodukte?",
|
||||
"render": "Die Menstruationsprodukte befinden sich in {toilets:menstrual_products:location}"
|
||||
},
|
||||
"opening_hours_24_7": {
|
||||
"override": {
|
||||
"question": "Wann sind diese Toiletten geöffnet?"
|
||||
|
@ -9925,7 +10004,49 @@
|
|||
}
|
||||
},
|
||||
"trolley_bay": {
|
||||
"description": "Finde Einkaufswagenbuchten für Einkaufswagen.",
|
||||
"name": "Einkaufswagenbuchten",
|
||||
"presets": {
|
||||
"0": {
|
||||
"description": "Ein Platz zum Abstellen von Einkaufswagen.",
|
||||
"title": "eine Einkaufswagenbucht"
|
||||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"cart_types": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Einkaufswagen mit Lupe sind erhältlich"
|
||||
},
|
||||
"1": {
|
||||
"then": "Einkaufswagen für Rollstuhlfahrer sind verfügbar"
|
||||
},
|
||||
"2": {
|
||||
"then": "Einkaufswagen für Kinder sind verfügbar"
|
||||
},
|
||||
"3": {
|
||||
"then": "Einkaufswagen mit Sitzen für Kinder sind verfügbar"
|
||||
},
|
||||
"4": {
|
||||
"then": "Einkaufswagen mit Flachbett sind verfügbar"
|
||||
},
|
||||
"5": {
|
||||
"then": "Vertikale Einkaufswagen für plattenförmige Güter sind verfügbar"
|
||||
}
|
||||
},
|
||||
"question": "Welche Art von speziellen Einkaufswagen gibt es?"
|
||||
},
|
||||
"covered": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Dieser Einkaufswagenplatz ist überdacht"
|
||||
},
|
||||
"1": {
|
||||
"then": "Dieser Einkaufswagenplatz ist nicht überdacht"
|
||||
}
|
||||
},
|
||||
"question": "Ist dieser Einkaufswagenplatz überdacht?"
|
||||
},
|
||||
"denominations": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
|
@ -9937,9 +10058,22 @@
|
|||
"2": {
|
||||
"then": "2-Euro-Münzen werden akzeptiert"
|
||||
}
|
||||
}
|
||||
},
|
||||
"question": "Welche Münzen werden für die Pfandgebühr akzeptiert?"
|
||||
},
|
||||
"deposit": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Für die Einkaufswagen ist ein Pfand zu hinterlegen"
|
||||
},
|
||||
"1": {
|
||||
"then": "Für die Einkaufswagen ist kein Pfand erforderlich"
|
||||
}
|
||||
},
|
||||
"question": "Ist für die Einkaufswagen ein Pfand (z.B. eine Münze) erforderlich?"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "Einkaufswagenbucht"
|
||||
},
|
||||
"unit": {
|
||||
"description": "Bibliotheksebene mit allen gängigen Einrichtungen. Einrichtungen können _nur_ aus dieser Datei importiert werden.",
|
||||
|
@ -10127,6 +10261,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"mangrove-key-import": {
|
||||
"render": {
|
||||
"after": "Wenn du einen privaten Schlüssel hochlädst, wird dein aktueller privater Schlüssel gelöscht. Wenn du damit Rezensionen geschrieben hast, lade zuerst deinen aktuellen privaten Schlüssel hoch",
|
||||
"special": {
|
||||
"text": "Privaten Mangrove-Schlüssel aus Backup importieren"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mangrove-keys": {
|
||||
"render": {
|
||||
"after": "Jeder, der diese Datei besitzt, kann mit Ihrer Identität Rezensionen vornehmen",
|
||||
|
|
|
@ -5658,6 +5658,12 @@
|
|||
"render": "Information board"
|
||||
}
|
||||
},
|
||||
"item_with_image": {
|
||||
"name": "Items with at least one image",
|
||||
"title": {
|
||||
"render": "POI with image"
|
||||
}
|
||||
},
|
||||
"kerbs": {
|
||||
"description": "A layer showing kerbs.",
|
||||
"filter": {
|
||||
|
@ -6670,39 +6676,36 @@
|
|||
"then": "This is a disabled parking space."
|
||||
},
|
||||
"3": {
|
||||
"then": "This is a private parking space."
|
||||
},
|
||||
"4": {
|
||||
"then": "This is parking space reserved for charging vehicles."
|
||||
},
|
||||
"5": {
|
||||
"4": {
|
||||
"then": "This is parking space reserved for deliveries."
|
||||
},
|
||||
"6": {
|
||||
"5": {
|
||||
"then": "This is parking space reserved for heavy goods vehicles."
|
||||
},
|
||||
"7": {
|
||||
"6": {
|
||||
"then": "This is parking space reserved for caravans or RVs."
|
||||
},
|
||||
"8": {
|
||||
"7": {
|
||||
"then": "This is parking space reserved for buses."
|
||||
},
|
||||
"9": {
|
||||
"8": {
|
||||
"then": "This is parking space reserved for motorcycles."
|
||||
},
|
||||
"10": {
|
||||
"9": {
|
||||
"then": "This is a parking space reserved for parents with children."
|
||||
},
|
||||
"11": {
|
||||
"10": {
|
||||
"then": "This is a parking space reserved for staff."
|
||||
},
|
||||
"12": {
|
||||
"11": {
|
||||
"then": "This is a parking space reserved for taxis."
|
||||
},
|
||||
"13": {
|
||||
"12": {
|
||||
"then": "This is a parking space reserved for vehicles towing a trailer."
|
||||
},
|
||||
"14": {
|
||||
"13": {
|
||||
"then": "This is a parking space reserved for car sharing."
|
||||
}
|
||||
},
|
||||
|
@ -6996,6 +6999,101 @@
|
|||
"render": "Playground"
|
||||
}
|
||||
},
|
||||
"playground_equipment": {
|
||||
"description": "Layer showing playground equipment",
|
||||
"name": "Playground equipment",
|
||||
"presets": {
|
||||
"0": {
|
||||
"description": "An exact type is asked later",
|
||||
"title": "a playground device"
|
||||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"type": {
|
||||
"freeform": {
|
||||
"placeholder": "Type of device"
|
||||
},
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "This is a swing"
|
||||
},
|
||||
"1": {
|
||||
"then": "This is a structure consisting of several connected playground devices"
|
||||
},
|
||||
"2": {
|
||||
"then": "This is a slide"
|
||||
},
|
||||
"3": {
|
||||
"then": "This is a sand pit"
|
||||
},
|
||||
"4": {
|
||||
"then": "This is a spring rider"
|
||||
},
|
||||
"5": {
|
||||
"then": "This is a climbing frame"
|
||||
},
|
||||
"6": {
|
||||
"then": "This is a seesaw"
|
||||
},
|
||||
"7": {
|
||||
"then": "This is a playhouse"
|
||||
},
|
||||
"8": {
|
||||
"then": "This is a roundabout"
|
||||
},
|
||||
"9": {
|
||||
"then": "This is a basket swing"
|
||||
},
|
||||
"10": {
|
||||
"then": "This is a zip wire"
|
||||
},
|
||||
"11": {
|
||||
"then": "This is a horizontal bar"
|
||||
},
|
||||
"12": {
|
||||
"then": "This is a hopscotch"
|
||||
},
|
||||
"13": {
|
||||
"then": "This is a splash pad"
|
||||
},
|
||||
"14": {
|
||||
"then": "This is a climbing wall"
|
||||
},
|
||||
"15": {
|
||||
"then": "This is a map"
|
||||
},
|
||||
"16": {
|
||||
"then": "This is a bridge (either as a standalone device or as part of a larger structure)"
|
||||
},
|
||||
"17": {
|
||||
"then": "This is a bouncy cushion"
|
||||
},
|
||||
"18": {
|
||||
"then": "This is an activity panel"
|
||||
},
|
||||
"19": {
|
||||
"then": "This is a teen shelter"
|
||||
},
|
||||
"20": {
|
||||
"then": "This is a funnel used to play with funnel ball"
|
||||
},
|
||||
"21": {
|
||||
"then": "This is a spinning circle"
|
||||
}
|
||||
},
|
||||
"question": "What kind of device is this?",
|
||||
"render": "This is a {playground}"
|
||||
},
|
||||
"wheelchair-access": {
|
||||
"override": {
|
||||
"question": "Is this device accessible by wheelchair?"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": "Playground device"
|
||||
}
|
||||
},
|
||||
"postboxes": {
|
||||
"description": "The layer showing postboxes.",
|
||||
"name": "Postboxes",
|
||||
|
@ -7010,6 +7108,43 @@
|
|||
},
|
||||
"postoffices": {
|
||||
"description": "A layer showing post offices.",
|
||||
"filter": {
|
||||
"1": {
|
||||
"options": {
|
||||
"0": {
|
||||
"question": "Offers letter posting"
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"options": {
|
||||
"0": {
|
||||
"question": "Offers parcel posting"
|
||||
}
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"options": {
|
||||
"0": {
|
||||
"question": "Offers pickup of missed parcels"
|
||||
}
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
"options": {
|
||||
"0": {
|
||||
"question": "Accepts pickup of parcels sent here"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"options": {
|
||||
"0": {
|
||||
"question": "Sells stamps"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Post offices",
|
||||
"presets": {
|
||||
"0": {
|
||||
|
@ -8566,6 +8701,125 @@
|
|||
"render": "Slow road"
|
||||
}
|
||||
},
|
||||
"souvenir_coin": {
|
||||
"description": "Layer showing machines selling souvenir coins",
|
||||
"name": "Souvenir Coin Machines",
|
||||
"presets": {
|
||||
"0": {
|
||||
"description": "Add a machine selling souvenir coins",
|
||||
"title": "a souvenir coin machine"
|
||||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"charge": {
|
||||
"freeform": {
|
||||
"placeholder": "Cost (e.g. 2 EUR)"
|
||||
},
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "A souvenir coin costs 2 euro"
|
||||
}
|
||||
},
|
||||
"question": "How much does a souvenir coin cost?",
|
||||
"render": "A souvenir coins costs {charge}"
|
||||
},
|
||||
"designs": {
|
||||
"override": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "This machine has one design available"
|
||||
},
|
||||
"1": {
|
||||
"then": "This machine has two designs available"
|
||||
},
|
||||
"2": {
|
||||
"then": "This machine has three designs available"
|
||||
},
|
||||
"3": {
|
||||
"then": "This machine has four designs available"
|
||||
}
|
||||
},
|
||||
"render": "This machine has {coin:design_count} designs available"
|
||||
}
|
||||
},
|
||||
"indoor": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "This machine is located indoors."
|
||||
},
|
||||
"1": {
|
||||
"then": "This machine is located outdoors."
|
||||
}
|
||||
},
|
||||
"question": "Is this machine located indoors?"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": "Souvenir Coin Machine"
|
||||
}
|
||||
},
|
||||
"souvenir_note": {
|
||||
"description": "Layer showing machines selling souvenir banknotes",
|
||||
"name": "Souvenir Banknote Machines",
|
||||
"presets": {
|
||||
"0": {
|
||||
"description": "Add a machine selling souvenir banknotes",
|
||||
"title": "a souvenir banknote machine"
|
||||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"charge": {
|
||||
"freeform": {
|
||||
"placeholder": "Cost (e.g. 2 EUR)"
|
||||
},
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "A souvenir note costs 2 euro"
|
||||
},
|
||||
"1": {
|
||||
"then": "A souvenir note costs 3 euro"
|
||||
}
|
||||
},
|
||||
"question": "How much does a souvenir note cost?",
|
||||
"render": "A souvenir note costs {charge}"
|
||||
},
|
||||
"designs": {
|
||||
"freeform": {
|
||||
"placeholder": "Number of designs (e.g. 5)"
|
||||
},
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "This machine has one design available."
|
||||
},
|
||||
"1": {
|
||||
"then": "This machine has two designs available."
|
||||
},
|
||||
"2": {
|
||||
"then": "This machine has three designs available."
|
||||
},
|
||||
"3": {
|
||||
"then": "This machine has four designs available."
|
||||
}
|
||||
},
|
||||
"question": "How many designs are available?",
|
||||
"render": "This machine has {note:design_count} designs available."
|
||||
},
|
||||
"indoor": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "This machine is located indoors."
|
||||
},
|
||||
"1": {
|
||||
"then": "This machine is located outdoors."
|
||||
}
|
||||
},
|
||||
"question": "Is this machine located indoors?"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": "Souvenir Banknote Machine"
|
||||
}
|
||||
},
|
||||
"speed_camera": {
|
||||
"description": "Layer showing speed cameras",
|
||||
"name": "Speed Camera",
|
||||
|
@ -9066,6 +9320,11 @@
|
|||
"render": "Stripclub"
|
||||
}
|
||||
},
|
||||
"summary": {
|
||||
"title": {
|
||||
"render": "Summary"
|
||||
}
|
||||
},
|
||||
"surveillance_camera": {
|
||||
"description": "This layer shows surveillance cameras and allows a contributor to update information and add new cameras",
|
||||
"name": "Surveillance camera's",
|
||||
|
|
|
@ -4023,6 +4023,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"souvenir_note": {
|
||||
"tagRenderings": {
|
||||
"designs": {
|
||||
"freeform": {
|
||||
"placeholder": "Número de diseños (por ejemplo, 5)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"speed_camera": {
|
||||
"description": "Capa con cámaras de velocidad",
|
||||
"name": "Cámara de velocidad",
|
||||
|
|
|
@ -5727,39 +5727,36 @@
|
|||
"then": "Dit is een gehandicaptenparkeerplaats."
|
||||
},
|
||||
"3": {
|
||||
"then": "Dit is een privéparkeerplek."
|
||||
},
|
||||
"4": {
|
||||
"then": "Deze parkeerplek is gereserveerd voor het opladen van voertuigen."
|
||||
},
|
||||
"5": {
|
||||
"4": {
|
||||
"then": "Deze parkeerplek is gereserveerd voor leveringen."
|
||||
},
|
||||
"6": {
|
||||
"5": {
|
||||
"then": "Deze parkeerplek is gereserveerd voor vrachtwagens."
|
||||
},
|
||||
"7": {
|
||||
"6": {
|
||||
"then": "Deze parkeerplek is gereserveerd voor caravans of campers."
|
||||
},
|
||||
"8": {
|
||||
"7": {
|
||||
"then": "Deze parkeerplek is gereserveerd voor bussen."
|
||||
},
|
||||
"9": {
|
||||
"8": {
|
||||
"then": "Deze parkeerplek is gereserveerd voor motoren."
|
||||
},
|
||||
"10": {
|
||||
"9": {
|
||||
"then": "Deze parkeerplek is gereserveerd voor ouders met kinderen."
|
||||
},
|
||||
"11": {
|
||||
"10": {
|
||||
"then": "Deze parkeerplek is gereserveerd voor personeel."
|
||||
},
|
||||
"12": {
|
||||
"11": {
|
||||
"then": "Deze parkeerplek is gereserveerd voor taxis."
|
||||
},
|
||||
"13": {
|
||||
"12": {
|
||||
"then": "Deze parkeerplek is gereserveerd voor voertuigen met een aanhanger."
|
||||
},
|
||||
"14": {
|
||||
"13": {
|
||||
"then": "Deze parkeerplek is gereserveerd voor autodelen."
|
||||
}
|
||||
},
|
||||
|
@ -6068,6 +6065,33 @@
|
|||
"render": "Speeltuin"
|
||||
}
|
||||
},
|
||||
"playground_equipment": {
|
||||
"tagRenderings": {
|
||||
"type": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Dit is een schommel"
|
||||
},
|
||||
"3": {
|
||||
"then": "Dit is een zandbak"
|
||||
},
|
||||
"4": {
|
||||
"then": "Dit is een veertoestel"
|
||||
},
|
||||
"5": {
|
||||
"then": "Dit is een klimrek"
|
||||
},
|
||||
"6": {
|
||||
"then": "Dit is een wipwap"
|
||||
},
|
||||
"11": {
|
||||
"then": "Dit is een rekstok"
|
||||
}
|
||||
},
|
||||
"question": "Wat voor speeltoestel is dit?"
|
||||
}
|
||||
}
|
||||
},
|
||||
"postboxes": {
|
||||
"description": "Deze laag toont brievenbussen.",
|
||||
"name": "Brievenbussen",
|
||||
|
|
|
@ -2200,27 +2200,24 @@
|
|||
"then": "To jest miejsce parkingowe dla niepełnosprawnych."
|
||||
},
|
||||
"3": {
|
||||
"then": "To jest prywatne miejsce parkingowe."
|
||||
},
|
||||
"4": {
|
||||
"then": "To miejsce parkingowe jest zarezerwowane dla ładowania pojazdów."
|
||||
},
|
||||
"5": {
|
||||
"4": {
|
||||
"then": "To miejsce parkingowe jest przeznaczone dla dostaw."
|
||||
},
|
||||
"8": {
|
||||
"7": {
|
||||
"then": "To miejsce parkingowe jest przeznaczone dla busów."
|
||||
},
|
||||
"9": {
|
||||
"8": {
|
||||
"then": "To miejsce parkingowe jest przeznaczone dla motocykli."
|
||||
},
|
||||
"10": {
|
||||
"9": {
|
||||
"then": "To miejsce jest przeznaczone dla rodziców z dziećmi."
|
||||
},
|
||||
"11": {
|
||||
"10": {
|
||||
"then": "To jest miejsce parkingowe przeznaczone dla pracowników."
|
||||
},
|
||||
"12": {
|
||||
"11": {
|
||||
"then": "To miejsce parkingowe jest przeznaczone dla taksówek."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -810,6 +810,71 @@
|
|||
"description": "A <b>ghost bike</b> 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.<br/><br/>On this map, one can see all the ghost bikes which are known by OpenStreetMap. Is a ghost bike missing? Everyone can add or update information here - you only need to have a (free) OpenStreetMap account. <p>There exists an <a href='https://masto.bike/@ghostbikebot' target='_blank'>automated account on Mastodon which posts a monthly overview of ghost bikes worldwide</a></p>",
|
||||
"title": "Ghost bikes"
|
||||
},
|
||||
"ghostsigns": {
|
||||
"description": "A map showing disused signs on buildings",
|
||||
"layers": {
|
||||
"0": {
|
||||
"description": "Layer showing disused signs on buildings",
|
||||
"name": "Ghost Signs",
|
||||
"presets": {
|
||||
"0": {
|
||||
"title": "a ghost sign"
|
||||
}
|
||||
},
|
||||
"tagRenderings": {
|
||||
"brand": {
|
||||
"freeform": {
|
||||
"placeholder": "Business name"
|
||||
},
|
||||
"question": "For what business was this sign made?",
|
||||
"render": "This sign was made for: {brand}"
|
||||
},
|
||||
"historic": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "This is a ghost sign"
|
||||
},
|
||||
"1": {
|
||||
"then": "This is not a ghost sign, answering this will hide the sign from the map"
|
||||
}
|
||||
},
|
||||
"question": "Is this a ghost sign?",
|
||||
"questionHint": "Is this sign for a business that no longer exists or no longer being maintained?"
|
||||
},
|
||||
"inscription": {
|
||||
"freeform": {
|
||||
"placeholder": "Text on the sign"
|
||||
},
|
||||
"question": "What is the text on the sign?",
|
||||
"render": "The text on the sign is: {inscription}"
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"render": "Ghost Sign"
|
||||
}
|
||||
},
|
||||
"1": {
|
||||
"override": {
|
||||
"+tagRenderings": {
|
||||
"0": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "This is a ghost sign"
|
||||
},
|
||||
"1": {
|
||||
"then": "This is not a ghost sign"
|
||||
}
|
||||
},
|
||||
"question": "Is this a ghost sign?",
|
||||
"questionHint": "Is this sign for a business that no longer exists or no longer being maintained?"
|
||||
}
|
||||
},
|
||||
"name": "All advertentie wall paintings"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "Ghost Signs"
|
||||
},
|
||||
"grb": {
|
||||
"description": "This theme is an attempt to help automating the GRB import.",
|
||||
"layers": {
|
||||
|
@ -886,6 +951,10 @@
|
|||
"description": "On this map, publicly accessible indoor places are shown",
|
||||
"title": "Indoors"
|
||||
},
|
||||
"items_with_image": {
|
||||
"description": "A map showing all items on OSM which have an image. This theme is a very bad fit for MapComplete as someone is not able to directly add a picture. However, this theme is mostly here to include this all into the database, which'll allow this to quickly fetch images nearby for other features",
|
||||
"title": "All items with images"
|
||||
},
|
||||
"kerbs_and_crossings": {
|
||||
"description": "A map showing kerbs and crossings.",
|
||||
"layers": {
|
||||
|
@ -1262,6 +1331,32 @@
|
|||
},
|
||||
"postboxes": {
|
||||
"description": "On this map you can find and add data of post offices and post boxes. You can use this map to find where you can mail your next postcard! :)<br/>Spotted an error or is a post box missing? You can edit this map with a free OpenStreetMap account.",
|
||||
"layers": {
|
||||
"3": {
|
||||
"override": {
|
||||
"+tagRenderings": {
|
||||
"0": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "This shop is a post partner"
|
||||
},
|
||||
"1": {
|
||||
"then": "This shop is not a post partner"
|
||||
}
|
||||
},
|
||||
"question": "Is this shop a post partner?"
|
||||
}
|
||||
},
|
||||
"=presets": {
|
||||
"0": {
|
||||
"description": "If a shop is not yet on the map and is a post partner, you can add it here.",
|
||||
"title": "a missing shop that is a post partner"
|
||||
}
|
||||
},
|
||||
"description": "Add a new post partner to the map in an existing shop"
|
||||
}
|
||||
}
|
||||
},
|
||||
"shortDescription": "A map showing postboxes and post offices",
|
||||
"title": "Postbox and Post Office Map"
|
||||
},
|
||||
|
|
|
@ -721,6 +721,15 @@
|
|||
"description": "Een <b>Witte Fiets</b> of <b>Spookfiets</b> is een aandenken aan een fietser die bij een verkeersongeval om het leven kwam. Het gaat om een fiets die volledig wit is geschilderd en in de buurt van het ongeval werd geinstalleerd.<br/><br/>Op deze kaart zie je alle witte fietsen die door OpenStreetMap gekend zijn. Ontbreekt er een Witte Fiets of wens je informatie aan te passen? Meld je dan aan met een (gratis) OpenStreetMap account.",
|
||||
"title": "Witte Fietsen"
|
||||
},
|
||||
"ghostsigns": {
|
||||
"layers": {
|
||||
"1": {
|
||||
"override": {
|
||||
"name": "Alle adverterende muurschilderingen"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"grb": {
|
||||
"description": "Dit thema helpt het GRB importeren.",
|
||||
"layers": {
|
||||
|
|
386
package-lock.json
generated
386
package-lock.json
generated
|
@ -20,6 +20,7 @@
|
|||
"@turf/length": "^6.5.0",
|
||||
"@turf/turf": "^6.5.0",
|
||||
"@types/dompurify": "^3.0.2",
|
||||
"@types/pg": "^8.10.9",
|
||||
"@types/qrcode-generator": "^1.0.6",
|
||||
"@types/showdown": "^2.0.0",
|
||||
"chart.js": "^3.8.0",
|
||||
|
@ -53,6 +54,8 @@
|
|||
"osmtogeojson": "^3.0.0-beta.5",
|
||||
"panzoom": "^9.4.3",
|
||||
"papaparse": "^5.3.1",
|
||||
"pbf": "^3.2.1",
|
||||
"pg": "^8.11.3",
|
||||
"pic4carto": "^2.1.15",
|
||||
"prompt-sync": "^4.2.0",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
|
@ -4267,6 +4270,68 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.3.tgz",
|
||||
"integrity": "sha512-hw6bDMjvm+QTvEC+pRLpnTknQXoPu8Fnf+A+zX9HB7j/7RfYajFSbdukabo3adPwvvEHhIMafQl0R0Tpej7clQ=="
|
||||
},
|
||||
"node_modules/@types/pg": {
|
||||
"version": "8.10.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.9.tgz",
|
||||
"integrity": "sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"pg-protocol": "*",
|
||||
"pg-types": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pg/node_modules/pg-types": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.1.tgz",
|
||||
"integrity": "sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==",
|
||||
"dependencies": {
|
||||
"pg-int8": "1.0.1",
|
||||
"pg-numeric": "1.0.2",
|
||||
"postgres-array": "~3.0.1",
|
||||
"postgres-bytea": "~3.0.0",
|
||||
"postgres-date": "~2.0.1",
|
||||
"postgres-interval": "^3.0.0",
|
||||
"postgres-range": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pg/node_modules/postgres-array": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz",
|
||||
"integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pg/node_modules/postgres-bytea": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz",
|
||||
"integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
|
||||
"dependencies": {
|
||||
"obuf": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pg/node_modules/postgres-date": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.0.1.tgz",
|
||||
"integrity": "sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pg/node_modules/postgres-interval": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz",
|
||||
"integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/prompt-sync": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/prompt-sync/-/prompt-sync-4.2.0.tgz",
|
||||
|
@ -5332,6 +5397,14 @@
|
|||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"node_modules/buffer-writer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
|
||||
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/bytewise": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz",
|
||||
|
@ -9860,6 +9933,11 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/obuf": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
||||
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
|
@ -10099,6 +10177,11 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/packet-reader": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
|
||||
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
|
||||
},
|
||||
"node_modules/panzoom": {
|
||||
"version": "9.4.3",
|
||||
"resolved": "https://registry.npmjs.org/panzoom/-/panzoom-9.4.3.tgz",
|
||||
|
@ -10208,6 +10291,97 @@
|
|||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz",
|
||||
"integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==",
|
||||
"dependencies": {
|
||||
"buffer-writer": "2.0.0",
|
||||
"packet-reader": "1.0.0",
|
||||
"pg-connection-string": "^2.6.2",
|
||||
"pg-pool": "^3.6.1",
|
||||
"pg-protocol": "^1.6.0",
|
||||
"pg-types": "^2.1.0",
|
||||
"pgpass": "1.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"pg-cloudflare": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg-native": ">=3.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pg-cloudflare": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
|
||||
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-connection-string": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
|
||||
"integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA=="
|
||||
},
|
||||
"node_modules/pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-numeric": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz",
|
||||
"integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-pool": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz",
|
||||
"integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==",
|
||||
"peerDependencies": {
|
||||
"pg": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-protocol": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
|
||||
"integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
|
||||
},
|
||||
"node_modules/pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"dependencies": {
|
||||
"pg-int8": "1.0.1",
|
||||
"postgres-array": "~2.0.0",
|
||||
"postgres-bytea": "~1.0.0",
|
||||
"postgres-date": "~1.0.4",
|
||||
"postgres-interval": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"dependencies": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pic4carto": {
|
||||
"version": "2.1.15",
|
||||
"resolved": "https://registry.npmjs.org/pic4carto/-/pic4carto-2.1.15.tgz",
|
||||
|
@ -10392,6 +10566,46 @@
|
|||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-bytea": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-range": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.3.tgz",
|
||||
"integrity": "sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g=="
|
||||
},
|
||||
"node_modules/potpack": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz",
|
||||
|
@ -11748,6 +11962,14 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||
"engines": {
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/sshpk": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
|
||||
|
@ -17177,6 +17399,55 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.3.tgz",
|
||||
"integrity": "sha512-hw6bDMjvm+QTvEC+pRLpnTknQXoPu8Fnf+A+zX9HB7j/7RfYajFSbdukabo3adPwvvEHhIMafQl0R0Tpej7clQ=="
|
||||
},
|
||||
"@types/pg": {
|
||||
"version": "8.10.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.9.tgz",
|
||||
"integrity": "sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==",
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"pg-protocol": "*",
|
||||
"pg-types": "^4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"pg-types": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.1.tgz",
|
||||
"integrity": "sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==",
|
||||
"requires": {
|
||||
"pg-int8": "1.0.1",
|
||||
"pg-numeric": "1.0.2",
|
||||
"postgres-array": "~3.0.1",
|
||||
"postgres-bytea": "~3.0.0",
|
||||
"postgres-date": "~2.0.1",
|
||||
"postgres-interval": "^3.0.0",
|
||||
"postgres-range": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"postgres-array": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz",
|
||||
"integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog=="
|
||||
},
|
||||
"postgres-bytea": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz",
|
||||
"integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
|
||||
"requires": {
|
||||
"obuf": "~1.1.2"
|
||||
}
|
||||
},
|
||||
"postgres-date": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.0.1.tgz",
|
||||
"integrity": "sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw=="
|
||||
},
|
||||
"postgres-interval": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz",
|
||||
"integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/prompt-sync": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/prompt-sync/-/prompt-sync-4.2.0.tgz",
|
||||
|
@ -17958,6 +18229,11 @@
|
|||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"buffer-writer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
|
||||
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw=="
|
||||
},
|
||||
"bytewise": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz",
|
||||
|
@ -21346,6 +21622,11 @@
|
|||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
|
||||
},
|
||||
"obuf": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
|
||||
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
|
@ -21521,6 +21802,11 @@
|
|||
"p-limit": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"packet-reader": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
|
||||
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
|
||||
},
|
||||
"panzoom": {
|
||||
"version": "9.4.3",
|
||||
"resolved": "https://registry.npmjs.org/panzoom/-/panzoom-9.4.3.tgz",
|
||||
|
@ -21606,6 +21892,73 @@
|
|||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
|
||||
},
|
||||
"pg": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz",
|
||||
"integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==",
|
||||
"requires": {
|
||||
"buffer-writer": "2.0.0",
|
||||
"packet-reader": "1.0.0",
|
||||
"pg-cloudflare": "^1.1.1",
|
||||
"pg-connection-string": "^2.6.2",
|
||||
"pg-pool": "^3.6.1",
|
||||
"pg-protocol": "^1.6.0",
|
||||
"pg-types": "^2.1.0",
|
||||
"pgpass": "1.x"
|
||||
}
|
||||
},
|
||||
"pg-cloudflare": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
|
||||
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
|
||||
"optional": true
|
||||
},
|
||||
"pg-connection-string": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
|
||||
"integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA=="
|
||||
},
|
||||
"pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
|
||||
},
|
||||
"pg-numeric": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz",
|
||||
"integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw=="
|
||||
},
|
||||
"pg-pool": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz",
|
||||
"integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==",
|
||||
"requires": {}
|
||||
},
|
||||
"pg-protocol": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
|
||||
"integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
|
||||
},
|
||||
"pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"requires": {
|
||||
"pg-int8": "1.0.1",
|
||||
"postgres-array": "~2.0.0",
|
||||
"postgres-bytea": "~1.0.0",
|
||||
"postgres-date": "~1.0.4",
|
||||
"postgres-interval": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"requires": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"pic4carto": {
|
||||
"version": "2.1.15",
|
||||
"resolved": "https://registry.npmjs.org/pic4carto/-/pic4carto-2.1.15.tgz",
|
||||
|
@ -21710,6 +22063,34 @@
|
|||
"util-deprecate": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
|
||||
},
|
||||
"postgres-bytea": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w=="
|
||||
},
|
||||
"postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="
|
||||
},
|
||||
"postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"requires": {
|
||||
"xtend": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"postgres-range": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.3.tgz",
|
||||
"integrity": "sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g=="
|
||||
},
|
||||
"potpack": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz",
|
||||
|
@ -22697,6 +23078,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
|
||||
|
|
12
package.json
12
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "mapcomplete",
|
||||
"version": "0.38.1",
|
||||
"version": "0.40.0",
|
||||
"repository": "https://github.com/pietervdvn/MapComplete",
|
||||
"description": "A small website to edit OSM easily",
|
||||
"bugs": "https://github.com/pietervdvn/MapComplete/issues",
|
||||
|
@ -21,6 +21,7 @@
|
|||
"oauth_secret": "NBWGhWDrD3QDB35xtVuxv4aExnmIt4FA_WgeLtwxasg",
|
||||
"url": "https://www.openstreetmap.org"
|
||||
},
|
||||
"mvt_layer_server": "https://cache.mapcomplete.org/public.{type}_{layer}/{z}/{x}/{y}.pbf",
|
||||
"disabled:oauth_credentials": {
|
||||
"##": "DEV",
|
||||
"#": "This client-id is registered by 'MapComplete' on https://master.apis.dev.openstreetmap.org/",
|
||||
|
@ -60,8 +61,6 @@
|
|||
"reset:translations": "vite-node scripts/generateTranslations.ts -- --ignore-weblate",
|
||||
"generate:layouts": "vite-node scripts/generateLayouts.ts",
|
||||
"generate:docs": "rm -rf Docs/Themes/* && rm -rf Docs/Layers/* && rm -rf Docs/TagInfo && mkdir Docs/TagInfo && vite-node scripts/generateDocs.ts && vite-node scripts/generateTaginfoProjectFiles.ts",
|
||||
"generate:cache:speelplekken": "npm run generate:layeroverview && vite-node scripts/generateCache.ts -- speelplekken 14 ../MapComplete-data/speelplekken_cache/ 51.20 4.35 51.09 4.56",
|
||||
"generate:cache:natuurpunt": "npm run generate:layeroverview && vite-node scripts/generateCache.ts -- natuurpunt 12 ../MapComplete-data/natuurpunt_cache/ 50.40 2.1 51.54 6.4 --generate-point-overview nature_reserve,visitor_information_centre",
|
||||
"generate:layeroverview": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts",
|
||||
"velopark": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts -- --force --themes=velopark --layers=bike_parking,maproulette_challenge",
|
||||
"generate:mapcomplete-changes-theme": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts -- --generate-change-map",
|
||||
|
@ -96,7 +95,9 @@
|
|||
"reuse-compliance": "reuse lint",
|
||||
"backup:images": "vite-node scripts/generateImageAnalysis.ts -- ~/data/imgur-image-backup/",
|
||||
"dloadVelopark": "vite-node scripts/velopark/veloParkToGeojson.ts ",
|
||||
"scrapeWebsites": "vite-node scripts/importscripts/compareWebsiteData.ts -- ~/Downloads/ShopsWithWebsiteNodes.csv ~/data/scraped_websites/\n"
|
||||
"scrapeWebsites": "vite-node scripts/importscripts/compareWebsiteData.ts -- ~/Downloads/ShopsWithWebsiteNodes.csv ~/data/scraped_websites/",
|
||||
"summary-server": "vite-node scripts/osm2pgsql/tilecountServer.ts",
|
||||
"ldjson-server": "vite-node scripts/serverLdScrape.ts"
|
||||
},
|
||||
"keywords": [
|
||||
"OpenStreetMap",
|
||||
|
@ -122,6 +123,7 @@
|
|||
"@turf/length": "^6.5.0",
|
||||
"@turf/turf": "^6.5.0",
|
||||
"@types/dompurify": "^3.0.2",
|
||||
"@types/pg": "^8.10.9",
|
||||
"@types/qrcode-generator": "^1.0.6",
|
||||
"@types/showdown": "^2.0.0",
|
||||
"chart.js": "^3.8.0",
|
||||
|
@ -155,6 +157,8 @@
|
|||
"osmtogeojson": "^3.0.0-beta.5",
|
||||
"panzoom": "^9.4.3",
|
||||
"papaparse": "^5.3.1",
|
||||
"pbf": "^3.2.1",
|
||||
"pg": "^8.11.3",
|
||||
"pic4carto": "^2.1.15",
|
||||
"prompt-sync": "^4.2.0",
|
||||
"qrcode-generator": "^1.4.4",
|
||||
|
|
|
@ -13,8 +13,15 @@ export default abstract class Script {
|
|||
ScriptUtils.fixUtils()
|
||||
const args = [...process.argv]
|
||||
args.splice(0, 2)
|
||||
const start = new Date()
|
||||
this.main(args)
|
||||
.then((_) => console.log("All done"))
|
||||
.then((_) =>{
|
||||
const end = new Date()
|
||||
const millisNeeded = end.getTime() - start.getTime()
|
||||
|
||||
const green = (s) => "\x1b[92m" + s + "\x1b[0m"
|
||||
console.log(green("All done! (" + millisNeeded + " ms)"))
|
||||
})
|
||||
.catch((e) => console.log("ERROR:", e))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,571 +0,0 @@
|
|||
/**
|
||||
* Generates a collection of geojson files based on an overpass query for a given theme
|
||||
*/
|
||||
import { Utils } from "../src/Utils"
|
||||
import { Overpass } from "../src/Logic/Osm/Overpass"
|
||||
import { existsSync, readFileSync, writeFileSync } from "fs"
|
||||
import { TagsFilter } from "../src/Logic/Tags/TagsFilter"
|
||||
import { Or } from "../src/Logic/Tags/Or"
|
||||
import { AllKnownLayouts } from "../src/Customizations/AllKnownLayouts"
|
||||
import * as OsmToGeoJson from "osmtogeojson"
|
||||
import MetaTagging from "../src/Logic/MetaTagging"
|
||||
import { UIEventSource } from "../src/Logic/UIEventSource"
|
||||
import { TileRange, Tiles } from "../src/Models/TileRange"
|
||||
import LayoutConfig from "../src/Models/ThemeConfig/LayoutConfig"
|
||||
import ScriptUtils from "./ScriptUtils"
|
||||
import PerLayerFeatureSourceSplitter from "../src/Logic/FeatureSource/PerLayerFeatureSourceSplitter"
|
||||
import FilteredLayer from "../src/Models/FilteredLayer"
|
||||
import StaticFeatureSource from "../src/Logic/FeatureSource/Sources/StaticFeatureSource"
|
||||
import Constants from "../src/Models/Constants"
|
||||
import { GeoOperations } from "../src/Logic/GeoOperations"
|
||||
import SimpleMetaTaggers, { ReferencingWaysMetaTagger } from "../src/Logic/SimpleMetaTagger"
|
||||
import FilteringFeatureSource from "../src/Logic/FeatureSource/Sources/FilteringFeatureSource"
|
||||
import { Feature } from "geojson"
|
||||
import { BBox } from "../src/Logic/BBox"
|
||||
import { FeatureSource } from "../src/Logic/FeatureSource/FeatureSource"
|
||||
import OsmObjectDownloader from "../src/Logic/Osm/OsmObjectDownloader"
|
||||
import FeaturePropertiesStore from "../src/Logic/FeatureSource/Actors/FeaturePropertiesStore"
|
||||
|
||||
ScriptUtils.fixUtils()
|
||||
|
||||
function createOverpassObject(theme: LayoutConfig, backend: string) {
|
||||
let filters: TagsFilter[] = []
|
||||
let extraScripts: string[] = []
|
||||
for (const layer of theme.layers) {
|
||||
if (typeof layer === "string") {
|
||||
throw "A layer was not expanded!"
|
||||
}
|
||||
if (layer.doNotDownload) {
|
||||
continue
|
||||
}
|
||||
if (!layer.source) {
|
||||
continue
|
||||
}
|
||||
if (layer.source.geojsonSource) {
|
||||
// This layer defines a geoJson-source
|
||||
// SHould it be cached?
|
||||
if (layer.source.isOsmCacheLayer !== true) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
filters.push(layer.source.osmTags)
|
||||
}
|
||||
filters = Utils.NoNull(filters)
|
||||
extraScripts = Utils.NoNull(extraScripts)
|
||||
if (filters.length + extraScripts.length === 0) {
|
||||
throw "Nothing to download! The theme doesn't declare anything to download"
|
||||
}
|
||||
return new Overpass(new Or(filters), extraScripts, backend, new UIEventSource<number>(60))
|
||||
}
|
||||
|
||||
function rawJsonName(targetDir: string, x: number, y: number, z: number): string {
|
||||
return targetDir + "_" + z + "_" + x + "_" + y + ".json"
|
||||
}
|
||||
|
||||
function geoJsonName(targetDir: string, x: number, y: number, z: number): string {
|
||||
return targetDir + "_" + z + "_" + x + "_" + y + ".geojson"
|
||||
}
|
||||
|
||||
/// Downloads the given tilerange from overpass and saves them to disk
|
||||
async function downloadRaw(
|
||||
targetdir: string,
|
||||
r: TileRange,
|
||||
theme: LayoutConfig
|
||||
): Promise<{ failed: number; skipped: number }> {
|
||||
let downloaded = 0
|
||||
let failed = 0
|
||||
let skipped = 0
|
||||
const startTime = new Date().getTime()
|
||||
for (let x = r.xstart; x <= r.xend; x++) {
|
||||
for (let y = r.ystart; y <= r.yend; y++) {
|
||||
downloaded++
|
||||
const filename = rawJsonName(targetdir, x, y, r.zoomlevel)
|
||||
if (existsSync(filename)) {
|
||||
console.log("Already exists (not downloading again): ", filename)
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
const runningSeconds = (new Date().getTime() - startTime) / 1000
|
||||
const resting = failed + (r.total - downloaded)
|
||||
const perTile = runningSeconds / (downloaded - skipped)
|
||||
const estimated = Math.floor(resting * perTile)
|
||||
console.log(
|
||||
"total: ",
|
||||
downloaded,
|
||||
"/",
|
||||
r.total,
|
||||
"failed: ",
|
||||
failed,
|
||||
"skipped: ",
|
||||
skipped,
|
||||
"running time: ",
|
||||
Utils.toHumanTime(runningSeconds) + "s",
|
||||
"estimated left: ",
|
||||
Utils.toHumanTime(estimated),
|
||||
"(" + Math.floor(perTile) + "s/tile)"
|
||||
)
|
||||
|
||||
const boundsArr = Tiles.tile_bounds(r.zoomlevel, x, y)
|
||||
const bounds = {
|
||||
north: Math.max(boundsArr[0][0], boundsArr[1][0]),
|
||||
south: Math.min(boundsArr[0][0], boundsArr[1][0]),
|
||||
east: Math.max(boundsArr[0][1], boundsArr[1][1]),
|
||||
west: Math.min(boundsArr[0][1], boundsArr[1][1]),
|
||||
}
|
||||
const overpass = createOverpassObject(
|
||||
theme,
|
||||
Constants.defaultOverpassUrls[failed % Constants.defaultOverpassUrls.length]
|
||||
)
|
||||
const url = overpass.buildQuery(
|
||||
"[bbox:" +
|
||||
bounds.south +
|
||||
"," +
|
||||
bounds.west +
|
||||
"," +
|
||||
bounds.north +
|
||||
"," +
|
||||
bounds.east +
|
||||
"]"
|
||||
)
|
||||
|
||||
try {
|
||||
const json = await Utils.downloadJson(url)
|
||||
if ((<string>json.remark ?? "").startsWith("runtime error")) {
|
||||
console.error("Got a runtime error: ", json.remark)
|
||||
failed++
|
||||
} else if (json.elements.length === 0) {
|
||||
console.log("Got an empty response! Writing anyway")
|
||||
}
|
||||
|
||||
console.log(
|
||||
"Got the response - writing ",
|
||||
json.elements.length,
|
||||
" elements to ",
|
||||
filename
|
||||
)
|
||||
writeFileSync(filename, JSON.stringify(json, null, " "))
|
||||
} catch (err) {
|
||||
console.log(url)
|
||||
console.log(
|
||||
"Could not download - probably hit the rate limit; waiting a bit. (" + err + ")"
|
||||
)
|
||||
failed++
|
||||
await ScriptUtils.sleep(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { failed: failed, skipped: skipped }
|
||||
}
|
||||
|
||||
/*
|
||||
* Downloads extra geojson sources and returns the features.
|
||||
* Extra geojson layers should not be tiled
|
||||
*/
|
||||
async function downloadExtraData(theme: LayoutConfig) /* : any[] */ {
|
||||
const allFeatures: any[] = []
|
||||
for (const layer of theme.layers) {
|
||||
if (!layer.source?.geojsonSource) {
|
||||
continue
|
||||
}
|
||||
const source = layer.source.geojsonSource
|
||||
if (layer.source.isOsmCacheLayer !== undefined && layer.source.isOsmCacheLayer !== false) {
|
||||
// Cached layers are not considered here
|
||||
continue
|
||||
}
|
||||
if (source.startsWith("https://api.openstreetmap.org/api/0.6/notes.json")) {
|
||||
// We ignore map notes
|
||||
continue
|
||||
}
|
||||
console.log("Downloading extra data: ", source)
|
||||
await Utils.downloadJson(source).then((json) => allFeatures.push(...json.features))
|
||||
}
|
||||
return allFeatures
|
||||
}
|
||||
|
||||
function loadAllTiles(
|
||||
targetdir: string,
|
||||
r: TileRange,
|
||||
theme: LayoutConfig,
|
||||
extraFeatures: any[]
|
||||
): FeatureSource {
|
||||
let allFeatures = [...extraFeatures]
|
||||
let processed = 0
|
||||
for (let x = r.xstart; x <= r.xend; x++) {
|
||||
for (let y = r.ystart; y <= r.yend; y++) {
|
||||
processed++
|
||||
const filename = rawJsonName(targetdir, x, y, r.zoomlevel)
|
||||
console.log(" Loading and processing", processed, "/", r.total, filename)
|
||||
if (!existsSync(filename)) {
|
||||
console.error("Not found - and not downloaded. Run this script again!: " + filename)
|
||||
continue
|
||||
}
|
||||
|
||||
// We read the raw OSM-file and convert it to a geojson
|
||||
const rawOsm = JSON.parse(readFileSync(filename, { encoding: "utf8" }))
|
||||
|
||||
// Create and save the geojson file - which is the main chunk of the data
|
||||
const geojson = OsmToGeoJson.default(rawOsm)
|
||||
console.log(" which as", geojson.features.length, "features")
|
||||
|
||||
allFeatures.push(...geojson.features)
|
||||
}
|
||||
}
|
||||
return StaticFeatureSource.fromGeojson(allFeatures)
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all the tiles into memory from disk
|
||||
*/
|
||||
async function sliceToTiles(
|
||||
allFeatures: FeatureSource,
|
||||
theme: LayoutConfig,
|
||||
targetdir: string,
|
||||
pointsOnlyLayers: string[],
|
||||
clip: boolean,
|
||||
targetzoomLevel: number = 9
|
||||
) {
|
||||
const skippedLayers = new Set<string>()
|
||||
|
||||
const indexedFeatures: Map<string, any> = new Map<string, any>()
|
||||
let indexisBuilt = false
|
||||
const osmObjectDownloader = new OsmObjectDownloader()
|
||||
|
||||
function buildIndex() {
|
||||
for (const f of allFeatures.features.data) {
|
||||
indexedFeatures.set(f.properties.id, f)
|
||||
}
|
||||
indexisBuilt = true
|
||||
}
|
||||
|
||||
function getFeatureById(id) {
|
||||
if (!indexisBuilt) {
|
||||
buildIndex()
|
||||
}
|
||||
return indexedFeatures.get(id)
|
||||
}
|
||||
|
||||
const flayers: FilteredLayer[] = theme.layers.map((l) => new FilteredLayer(l))
|
||||
const perLayer = new PerLayerFeatureSourceSplitter(flayers, allFeatures)
|
||||
for (const [layerId, source] of perLayer.perLayer) {
|
||||
const layer = flayers.find((flayer) => flayer.layerDef.id === layerId).layerDef
|
||||
const targetZoomLevel = layer.source.geojsonZoomLevel ?? targetzoomLevel
|
||||
|
||||
if (layer.source.geojsonSource && layer.source.isOsmCacheLayer !== true) {
|
||||
console.log("Skipping layer ", layerId, ": not a caching layer")
|
||||
skippedLayers.add(layer.id)
|
||||
continue
|
||||
}
|
||||
const flayer: FilteredLayer = new FilteredLayer(layer)
|
||||
console.log(
|
||||
"Handling layer ",
|
||||
layerId,
|
||||
"which has",
|
||||
source.features.data.length,
|
||||
"features"
|
||||
)
|
||||
if (source.features.data.length === 0) {
|
||||
continue
|
||||
}
|
||||
const featureProperties: FeaturePropertiesStore = new FeaturePropertiesStore(source)
|
||||
|
||||
MetaTagging.addMetatags(
|
||||
source.features.data,
|
||||
{
|
||||
getFeaturesWithin: (_) => {
|
||||
return <any>[allFeatures.features.data]
|
||||
},
|
||||
getFeatureById: getFeatureById,
|
||||
},
|
||||
layer,
|
||||
theme,
|
||||
osmObjectDownloader,
|
||||
featureProperties,
|
||||
{
|
||||
includeDates: false,
|
||||
includeNonDates: true,
|
||||
evaluateStrict: true,
|
||||
}
|
||||
)
|
||||
|
||||
while (SimpleMetaTaggers.country.runningTasks.size > 0) {
|
||||
console.log(
|
||||
"Still waiting for ",
|
||||
SimpleMetaTaggers.country.runningTasks.size,
|
||||
" features which don't have a country yet"
|
||||
)
|
||||
await ScriptUtils.sleep(250)
|
||||
}
|
||||
|
||||
const createdTiles = []
|
||||
// At this point, we have all the features of the entire area.
|
||||
// However, we want to export them per tile of a fixed size, so we use a dynamicTileSOurce to split it up
|
||||
const features = source.features.data
|
||||
const perBbox = GeoOperations.spreadIntoBboxes(features, targetZoomLevel)
|
||||
|
||||
for (let [tileIndex, features] of perBbox) {
|
||||
const bbox = BBox.fromTileIndex(tileIndex).asGeoJson({})
|
||||
console.log("Got tile:", tileIndex, layer.id)
|
||||
if (features.length === 0) {
|
||||
continue
|
||||
}
|
||||
const filteredTile = new FilteringFeatureSource(
|
||||
flayer,
|
||||
new StaticFeatureSource(features)
|
||||
)
|
||||
console.log(
|
||||
"Tile " +
|
||||
layer.id +
|
||||
"." +
|
||||
tileIndex +
|
||||
" contains " +
|
||||
filteredTile.features.data.length +
|
||||
" features after filtering (" +
|
||||
features.length +
|
||||
") features before"
|
||||
)
|
||||
if (filteredTile.features.data.length === 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
let strictlyCalculated = 0
|
||||
let featureCount = 0
|
||||
|
||||
for (const feature of features) {
|
||||
// Some cleanup
|
||||
|
||||
if (layer.calculatedTags !== undefined) {
|
||||
// Evaluate all the calculated tags strictly
|
||||
const calculatedTagKeys = layer.calculatedTags.map((ct) => ct[0])
|
||||
featureCount++
|
||||
const props = feature.properties
|
||||
for (const calculatedTagKey of calculatedTagKeys) {
|
||||
const strict = props[calculatedTagKey]
|
||||
|
||||
if (props.hasOwnProperty(calculatedTagKey)) {
|
||||
delete props[calculatedTagKey]
|
||||
}
|
||||
|
||||
props[calculatedTagKey] = strict
|
||||
strictlyCalculated++
|
||||
if (strictlyCalculated % 100 === 0) {
|
||||
console.log(
|
||||
"Strictly calculated ",
|
||||
strictlyCalculated,
|
||||
"values for tile",
|
||||
tileIndex,
|
||||
": now at ",
|
||||
featureCount,
|
||||
"/",
|
||||
filteredTile.features.data.length,
|
||||
"examle value: ",
|
||||
strict
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
delete feature["bbox"]
|
||||
}
|
||||
|
||||
if (clip) {
|
||||
console.log("Clipping features")
|
||||
features = [].concat(
|
||||
...features.map((f: Feature) => GeoOperations.clipWith(<any>f, bbox))
|
||||
)
|
||||
}
|
||||
// Lets save this tile!
|
||||
const [z, x, y] = Tiles.tile_from_index(tileIndex)
|
||||
// console.log("Writing tile ", z, x, y, layerId)
|
||||
const targetPath = geoJsonName(targetdir + "_" + layerId, x, y, z)
|
||||
createdTiles.push(tileIndex)
|
||||
// This is the geojson file containing all features for this tile
|
||||
writeFileSync(
|
||||
targetPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
type: "FeatureCollection",
|
||||
features,
|
||||
},
|
||||
null,
|
||||
" "
|
||||
)
|
||||
)
|
||||
console.log("Written tile", targetPath, "with", filteredTile.features.data.length)
|
||||
}
|
||||
|
||||
// All the tiles are written at this point
|
||||
// Only thing left to do is to create the index
|
||||
const path = targetdir + "_" + layerId + "_" + targetZoomLevel + "_overview.json"
|
||||
const perX = {}
|
||||
createdTiles
|
||||
.map((i) => Tiles.tile_from_index(i))
|
||||
.forEach(([z, x, y]) => {
|
||||
const key = "" + x
|
||||
if (perX[key] === undefined) {
|
||||
perX[key] = []
|
||||
}
|
||||
perX[key].push(y)
|
||||
})
|
||||
console.log("Written overview: ", path, "with ", createdTiles.length, "tiles")
|
||||
writeFileSync(path, JSON.stringify(perX))
|
||||
|
||||
// And, if needed, to create a points-only layer
|
||||
if (pointsOnlyLayers.indexOf(layer.id) >= 0) {
|
||||
const filtered = new FilteringFeatureSource(flayer, source)
|
||||
const features = filtered.features.data
|
||||
|
||||
const points = features.map((feature) => GeoOperations.centerpoint(feature))
|
||||
console.log("Writing points overview for ", layerId)
|
||||
const targetPath = targetdir + "_" + layerId + "_points.geojson"
|
||||
// This is the geojson file containing all features for this tile
|
||||
writeFileSync(
|
||||
targetPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
type: "FeatureCollection",
|
||||
features: points,
|
||||
},
|
||||
null,
|
||||
" "
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const skipped = Array.from(skippedLayers)
|
||||
if (skipped.length > 0) {
|
||||
console.warn(
|
||||
"Did not save any cache files for layers " +
|
||||
skipped.join(", ") +
|
||||
" as these didn't set the flag `isOsmCache` to true"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function main(args: string[]) {
|
||||
console.log("Cache builder started with args ", args.join(" "))
|
||||
ReferencingWaysMetaTagger.enabled = false
|
||||
if (args.length < 6) {
|
||||
console.error(
|
||||
"Expected arguments are: theme zoomlevel targetdirectory lat0 lon0 lat1 lon1 [--generate-point-overview layer-name,layer-name,...] [--force-zoom-level z] [--clip]" +
|
||||
"--force-zoom-level causes non-cached-layers to be donwnloaded\n" +
|
||||
"--clip will erase parts of the feature falling outside of the bounding box"
|
||||
)
|
||||
return
|
||||
}
|
||||
const themeName = args[0]
|
||||
const zoomlevel = Number(args[1])
|
||||
console.log(
|
||||
"Target zoomlevel for the tiles is",
|
||||
zoomlevel,
|
||||
"; this can be overridden by the individual layers"
|
||||
)
|
||||
|
||||
const targetdir = args[2] + "/" + themeName
|
||||
if (!existsSync(args[2])) {
|
||||
console.log("Directory not found")
|
||||
throw `The directory ${args[2]} does not exist`
|
||||
}
|
||||
|
||||
const lat0 = Number(args[3])
|
||||
const lon0 = Number(args[4])
|
||||
const lat1 = Number(args[5])
|
||||
const lon1 = Number(args[6])
|
||||
const clip = args.indexOf("--clip") >= 0
|
||||
|
||||
if (isNaN(lat0)) {
|
||||
throw "The first number (a latitude) is not a valid number"
|
||||
}
|
||||
|
||||
if (isNaN(lon0)) {
|
||||
throw "The second number (a longitude) is not a valid number"
|
||||
}
|
||||
if (isNaN(lat1)) {
|
||||
throw "The third number (a latitude) is not a valid number"
|
||||
}
|
||||
|
||||
if (isNaN(lon1)) {
|
||||
throw "The fourth number (a longitude) is not a valid number"
|
||||
}
|
||||
|
||||
const tileRange = Tiles.TileRangeBetween(zoomlevel, lat0, lon0, lat1, lon1)
|
||||
|
||||
if (isNaN(tileRange.total)) {
|
||||
throw "Something has gone wrong: tilerange is NAN"
|
||||
}
|
||||
|
||||
if (tileRange.total === 0) {
|
||||
console.log("Tilerange has zero tiles - this is probably an error")
|
||||
return
|
||||
}
|
||||
|
||||
const theme = AllKnownLayouts.allKnownLayouts.get(themeName)
|
||||
if (theme === undefined) {
|
||||
const keys = Array.from(AllKnownLayouts.allKnownLayouts.keys())
|
||||
console.error("The theme " + themeName + " was not found; try one of ", keys)
|
||||
return
|
||||
}
|
||||
|
||||
theme.layers = theme.layers.filter(
|
||||
(l) =>
|
||||
Constants.priviliged_layers.indexOf(<any>l.id) < 0 && !l.id.startsWith("note_import_")
|
||||
)
|
||||
console.log("Layers to download:", theme.layers.map((l) => l.id).join(", "))
|
||||
|
||||
let generatePointLayersFor = []
|
||||
if (args[7] == "--generate-point-overview") {
|
||||
if (args[8] === undefined) {
|
||||
throw "--generate-point-overview needs a list of layers to generate the overview for (or * for all)"
|
||||
} else if (args[8] === "*") {
|
||||
generatePointLayersFor = theme.layers.map((l) => l.id)
|
||||
} else {
|
||||
generatePointLayersFor = args[8].split(",")
|
||||
}
|
||||
console.log(
|
||||
"Also generating a point overview for layers ",
|
||||
generatePointLayersFor.join(",")
|
||||
)
|
||||
}
|
||||
{
|
||||
const index = args.indexOf("--force-zoom-level")
|
||||
if (index >= 0) {
|
||||
const forcedZoomLevel = Number(args[index + 1])
|
||||
for (const layer of theme.layers) {
|
||||
layer.source.geojsonSource = "https://127.0.0.1/cache_{layer}_{z}_{x}_{y}.geojson"
|
||||
layer.source.isOsmCacheLayer = true
|
||||
layer.source.geojsonZoomLevel = forcedZoomLevel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let failed = 0
|
||||
do {
|
||||
try {
|
||||
const cachingResult = await downloadRaw(targetdir, tileRange, theme)
|
||||
failed = cachingResult.failed
|
||||
if (failed > 0) {
|
||||
await ScriptUtils.sleep(30000)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return
|
||||
}
|
||||
} while (failed > 0)
|
||||
|
||||
const extraFeatures = await downloadExtraData(theme)
|
||||
const allFeaturesSource = loadAllTiles(targetdir, tileRange, theme, extraFeatures)
|
||||
await sliceToTiles(allFeaturesSource, theme, targetdir, generatePointLayersFor, clip, zoomlevel)
|
||||
}
|
||||
|
||||
let args = [...process.argv]
|
||||
if (!args[1]?.endsWith("test/TestAll.ts")) {
|
||||
args.splice(0, 2)
|
||||
try {
|
||||
main(args)
|
||||
.then(() => console.log("All done!"))
|
||||
.catch((e) => console.error("Error building cache:", e))
|
||||
} catch (e) {
|
||||
console.error("Error building cache:", e)
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import {
|
|||
PrevalidateTheme,
|
||||
ValidateLayer,
|
||||
ValidateThemeAndLayers,
|
||||
ValidateThemeEnsemble,
|
||||
} from "../src/Models/ThemeConfig/Conversion/Validation"
|
||||
import { Translation } from "../src/UI/i18n/Translation"
|
||||
import { PrepareLayer } from "../src/Models/ThemeConfig/Conversion/PrepareLayer"
|
||||
|
@ -29,6 +30,8 @@ import LayerConfig from "../src/Models/ThemeConfig/LayerConfig"
|
|||
import PointRenderingConfig from "../src/Models/ThemeConfig/PointRenderingConfig"
|
||||
import { ConversionContext } from "../src/Models/ThemeConfig/Conversion/ConversionContext"
|
||||
import { GenerateFavouritesLayer } from "./generateFavouritesLayer"
|
||||
import LayoutConfig from "../src/Models/ThemeConfig/LayoutConfig"
|
||||
import { TagsFilter } from "../src/Logic/Tags/TagsFilter"
|
||||
|
||||
// This scripts scans 'src/assets/layers/*.json' for layer definition files and 'src/assets/themes/*.json' for theme definition files.
|
||||
// It spits out an overview of those to be used to load them
|
||||
|
@ -367,7 +370,6 @@ class LayerOverviewUtils extends Script {
|
|||
?.split(",") ?? []
|
||||
)
|
||||
|
||||
const start = new Date()
|
||||
const forceReload = args.some((a) => a == "--force")
|
||||
|
||||
const licensePaths = new Set<string>()
|
||||
|
@ -397,6 +399,10 @@ class LayerOverviewUtils extends Script {
|
|||
themeWhitelist
|
||||
)
|
||||
|
||||
new ValidateThemeEnsemble().convertStrict(
|
||||
Array.from(sharedThemes.values()).map((th) => new LayoutConfig(th, true))
|
||||
)
|
||||
|
||||
if (recompiledThemes.length > 0) {
|
||||
writeFileSync(
|
||||
"./src/assets/generated/known_layers.json",
|
||||
|
@ -458,17 +464,8 @@ class LayerOverviewUtils extends Script {
|
|||
)
|
||||
}
|
||||
|
||||
const end = new Date()
|
||||
const millisNeeded = end.getTime() - start.getTime()
|
||||
if (AllSharedLayers.getSharedLayersConfigs().size == 0) {
|
||||
console.error(
|
||||
"This was a bootstrapping-run. Run generate layeroverview again!(" +
|
||||
millisNeeded +
|
||||
" ms)"
|
||||
)
|
||||
} else {
|
||||
const green = (s) => "\x1b[92m" + s + "\x1b[0m"
|
||||
console.log(green("All done! (" + millisNeeded + " ms)"))
|
||||
console.error("This was a bootstrapping-run. Run generate layeroverview again!")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -279,6 +279,7 @@ async function generateCsp(
|
|||
"https://www.openstreetmap.org",
|
||||
"https://api.openstreetmap.org",
|
||||
"https://pietervdvn.goatcounter.com",
|
||||
"https://cache.mapcomplete.org",
|
||||
].concat(...(await eliUrls()))
|
||||
|
||||
SpecialVisualizations.specialVisualizations.forEach((sv) => {
|
||||
|
|
|
@ -16,9 +16,9 @@ npm run test &&
|
|||
npm run prepare-deploy &&
|
||||
zip dist.zip -r dist/* &&
|
||||
mv config.json.bu config.json &&
|
||||
scp ./scripts/hetzner/config/* hetzner:/root/ &&
|
||||
scp ./Docs/ServerConfig/hetzner/* hetzner:/root/ &&
|
||||
rsync -rzh --progress dist.zip hetzner:/root/ &&
|
||||
echo "Upload completed, deploying config and booting" &&
|
||||
ssh hetzner -t "unzip dist.zip && rm dist.zip && rm -rf public/ && mv dist public && caddy stop && caddy start" &&
|
||||
rm dist.zip
|
||||
# rm dist.zip
|
||||
npm run clean
|
||||
|
|
306
scripts/osm2pgsql/generateBuildDbScript.ts
Normal file
306
scripts/osm2pgsql/generateBuildDbScript.ts
Normal file
|
@ -0,0 +1,306 @@
|
|||
import { TagsFilter } from "../../src/Logic/Tags/TagsFilter"
|
||||
import { Tag } from "../../src/Logic/Tags/Tag"
|
||||
import { And } from "../../src/Logic/Tags/And"
|
||||
import Script from "../Script"
|
||||
import fs from "fs"
|
||||
import { Or } from "../../src/Logic/Tags/Or"
|
||||
import { RegexTag } from "../../src/Logic/Tags/RegexTag"
|
||||
import { ValidateThemeEnsemble } from "../../src/Models/ThemeConfig/Conversion/Validation"
|
||||
import { AllKnownLayouts } from "../../src/Customizations/AllKnownLayouts"
|
||||
import { OsmObject } from "../../src/Logic/Osm/OsmObject"
|
||||
|
||||
class LuaSnippets {
|
||||
public static helpers = [
|
||||
"function countTbl(tbl)\n" +
|
||||
" local c = 0\n" +
|
||||
" for n in pairs(tbl) do \n" +
|
||||
" c = c + 1 \n" +
|
||||
" end\n" +
|
||||
" return c\n" +
|
||||
"end",
|
||||
].join("\n")
|
||||
|
||||
public static isPolygonFeature(): { blacklist: TagsFilter; whitelisted: TagsFilter } {
|
||||
const dict = OsmObject.polygonFeatures
|
||||
const or: TagsFilter[] = []
|
||||
const blacklisted: TagsFilter[] = []
|
||||
dict.forEach(({ values, blacklist }, k) => {
|
||||
if (blacklist) {
|
||||
if (values === undefined) {
|
||||
blacklisted.push(new RegexTag(k, /.+/is))
|
||||
return
|
||||
}
|
||||
values.forEach((v) => {
|
||||
blacklisted.push(new RegexTag(k, v))
|
||||
})
|
||||
return
|
||||
}
|
||||
if (values === undefined || values === null) {
|
||||
or.push(new RegexTag(k, /.+/is))
|
||||
return
|
||||
}
|
||||
values.forEach((v) => {
|
||||
or.push(new RegexTag(k, v))
|
||||
})
|
||||
})
|
||||
console.log(
|
||||
"Polygon features are:",
|
||||
or.map((t) => t.asHumanString(false, false, {}))
|
||||
)
|
||||
return { blacklist: new Or(blacklisted), whitelisted: new Or(or) }
|
||||
}
|
||||
|
||||
public static toLuaFilter(tag: TagsFilter, useParens: boolean = false): string {
|
||||
if (tag instanceof Tag) {
|
||||
return `object.tags["${tag.key}"] == "${tag.value}"`
|
||||
}
|
||||
if (tag instanceof And) {
|
||||
const expr = tag.and.map((t) => this.toLuaFilter(t, true)).join(" and ")
|
||||
if (useParens) {
|
||||
return "(" + expr + ")"
|
||||
}
|
||||
return expr
|
||||
}
|
||||
if (tag instanceof Or) {
|
||||
const expr = tag.or.map((t) => this.toLuaFilter(t, true)).join(" or ")
|
||||
if (useParens) {
|
||||
return "(" + expr + ")"
|
||||
}
|
||||
return expr
|
||||
}
|
||||
if (tag instanceof RegexTag) {
|
||||
let expr = LuaSnippets.regexTagToLua(tag)
|
||||
if (useParens) {
|
||||
expr = "(" + expr + ")"
|
||||
}
|
||||
return expr
|
||||
}
|
||||
let msg = "Could not handle" + tag.asHumanString(false, false, {})
|
||||
console.error(msg)
|
||||
throw msg
|
||||
}
|
||||
|
||||
private static regexTagToLua(tag: RegexTag) {
|
||||
if (typeof tag.value === "string" && tag.invert) {
|
||||
return `object.tags["${tag.key}"] ~= "${tag.value}"`
|
||||
}
|
||||
|
||||
if (typeof tag.value === "string" && !tag.invert) {
|
||||
return `object.tags["${tag.key}"] == "${tag.value}"`
|
||||
}
|
||||
|
||||
let v: string = (<RegExp>tag.value).source.replace(/\\\//g, "/")
|
||||
|
||||
if ("" + tag.value === "/.+/is" && !tag.invert) {
|
||||
return `object.tags["${tag.key}"] ~= nil`
|
||||
}
|
||||
|
||||
if ("" + tag.value === "/.+/is" && tag.invert) {
|
||||
return `object.tags["${tag.key}"] == nil`
|
||||
}
|
||||
|
||||
if (tag.matchesEmpty && !tag.invert) {
|
||||
return `object.tags["${tag.key}"] == nil or object.tags["${tag.key}"] == ""`
|
||||
}
|
||||
|
||||
if (tag.matchesEmpty && tag.invert) {
|
||||
return `object.tags["${tag.key}"] ~= nil or object.tags["${tag.key}"] ~= ""`
|
||||
}
|
||||
|
||||
let head = "^((.*;)?"
|
||||
let tail = "(;.*)?)$"
|
||||
if (v.startsWith(head)) {
|
||||
v = "(" + v.substring(head.length)
|
||||
}
|
||||
if (v.endsWith(tail)) {
|
||||
v = v.substring(0, v.length - tail.length) + ")"
|
||||
// We basically remove the optional parts at the start and the end, as object.find has this freedom anyway.
|
||||
// This might result in _some_ incorrect values that end up in the database (e.g. when matching 'friture', it might als match "abc;foo_friture_bar;xyz", but the frontend will filter this out
|
||||
}
|
||||
|
||||
if (v.indexOf(")?") > 0) {
|
||||
throw (
|
||||
"LUA regexes have a bad support for (optional) capture groups, as such, " +
|
||||
v +
|
||||
" is not supported"
|
||||
)
|
||||
}
|
||||
|
||||
if (tag.invert) {
|
||||
return `object.tags["${tag.key}"] == nil or not string.find(object.tags["${tag.key}"], "${v}")`
|
||||
}
|
||||
|
||||
return `(object.tags["${tag.key}"] ~= nil and string.find(object.tags["${tag.key}"], "${v}"))`
|
||||
}
|
||||
}
|
||||
|
||||
class GenerateLayerLua {
|
||||
private readonly _id: string
|
||||
private readonly _tags: TagsFilter
|
||||
private readonly _foundInThemes: string[]
|
||||
|
||||
constructor(id: string, tags: TagsFilter, foundInThemes: string[] = []) {
|
||||
this._tags = tags
|
||||
this._id = id
|
||||
this._foundInThemes = foundInThemes
|
||||
}
|
||||
|
||||
public generateTables(): string {
|
||||
if (!this._tags) {
|
||||
return undefined
|
||||
}
|
||||
return [
|
||||
`db_tables.pois_${this._id} = osm2pgsql.define_table({`,
|
||||
this._foundInThemes ? "-- used in themes: " + this._foundInThemes.join(", ") : "",
|
||||
` name = 'pois_${this._id}',`,
|
||||
" ids = { type = 'any', type_column = 'osm_type', id_column = 'osm_id' },",
|
||||
" columns = {",
|
||||
" { column = 'tags', type = 'jsonb' },",
|
||||
" { column = 'geom', type = 'point', projection = 4326, not_null = true },",
|
||||
" }",
|
||||
"})",
|
||||
"",
|
||||
`db_tables.lines_${this._id} = osm2pgsql.define_table({`,
|
||||
this._foundInThemes ? "-- used in themes: " + this._foundInThemes.join(", ") : "",
|
||||
` name = 'lines_${this._id}',`,
|
||||
" ids = { type = 'any', type_column = 'osm_type', id_column = 'osm_id' },",
|
||||
" columns = {",
|
||||
" { column = 'tags', type = 'jsonb' },",
|
||||
" { column = 'geom', type = 'linestring', projection = 4326, not_null = true },",
|
||||
" }",
|
||||
"})",
|
||||
|
||||
`db_tables.polygons_${this._id} = osm2pgsql.define_table({`,
|
||||
this._foundInThemes ? "-- used in themes: " + this._foundInThemes.join(", ") : "",
|
||||
` name = 'polygons_${this._id}',`,
|
||||
" ids = { type = 'any', type_column = 'osm_type', id_column = 'osm_id' },",
|
||||
" columns = {",
|
||||
" { column = 'tags', type = 'jsonb' },",
|
||||
" { column = 'geom', type = 'polygon', projection = 4326, not_null = true },",
|
||||
" }",
|
||||
"})",
|
||||
"",
|
||||
].join("\n")
|
||||
}
|
||||
}
|
||||
|
||||
class GenerateBuildDbScript extends Script {
|
||||
constructor() {
|
||||
super("Generates a .lua-file to use with osm2pgsql")
|
||||
}
|
||||
|
||||
async main(args: string[]) {
|
||||
const allNeededLayers = new ValidateThemeEnsemble().convertStrict(
|
||||
AllKnownLayouts.allKnownLayouts.values()
|
||||
)
|
||||
|
||||
const generators: GenerateLayerLua[] = []
|
||||
|
||||
allNeededLayers.forEach(({ tags, foundInTheme }, layerId) => {
|
||||
generators.push(new GenerateLayerLua(layerId, tags, foundInTheme))
|
||||
})
|
||||
|
||||
const script = [
|
||||
"local db_tables = {}",
|
||||
LuaSnippets.helpers,
|
||||
...generators.map((g) => g.generateTables()),
|
||||
this.generateProcessPoi(allNeededLayers),
|
||||
this.generateProcessWay(allNeededLayers),
|
||||
].join("\n\n\n")
|
||||
const path = "build_db.lua"
|
||||
fs.writeFileSync(path, script, "utf-8")
|
||||
console.log("Written", path)
|
||||
console.log(
|
||||
allNeededLayers.size +
|
||||
" layers will be created with 3 tables each. Make sure to set 'max_connections' to at least " +
|
||||
(10 + 3 * allNeededLayers.size)
|
||||
)
|
||||
}
|
||||
|
||||
private earlyAbort() {
|
||||
return [" if countTbl(object.tags) == 0 then", " return", " end", ""].join("\n")
|
||||
}
|
||||
|
||||
private generateProcessPoi(
|
||||
allNeededLayers: Map<string, { tags: TagsFilter; foundInTheme: string[] }>
|
||||
) {
|
||||
const body: string[] = []
|
||||
allNeededLayers.forEach(({ tags }, layerId) => {
|
||||
body.push(this.insertInto(tags, layerId, "pois_").join("\n"))
|
||||
})
|
||||
|
||||
return [
|
||||
"function osm2pgsql.process_node(object)",
|
||||
this.earlyAbort(),
|
||||
" local geom = object:as_point()",
|
||||
" local matches_filter = false",
|
||||
body.join("\n"),
|
||||
"end",
|
||||
].join("\n")
|
||||
}
|
||||
|
||||
/**
|
||||
* If matches_filter
|
||||
* @param tags
|
||||
* @param layerId
|
||||
* @param tableprefix
|
||||
* @private
|
||||
*/
|
||||
private insertInto(
|
||||
tags: TagsFilter,
|
||||
layerId: string,
|
||||
tableprefix: "pois_" | "lines_" | "polygons_"
|
||||
) {
|
||||
const filter = LuaSnippets.toLuaFilter(tags)
|
||||
return [
|
||||
" matches_filter = " + filter,
|
||||
" if matches_filter then",
|
||||
" db_tables." + tableprefix + layerId + ":insert({",
|
||||
" geom = geom,",
|
||||
" tags = object.tags",
|
||||
" })",
|
||||
" end",
|
||||
]
|
||||
}
|
||||
|
||||
private generateProcessWay(allNeededLayers: Map<string, { tags: TagsFilter }>) {
|
||||
const bodyLines: string[] = []
|
||||
allNeededLayers.forEach(({ tags }, layerId) => {
|
||||
bodyLines.push(this.insertInto(tags, layerId, "lines_").join("\n"))
|
||||
})
|
||||
|
||||
const bodyPolygons: string[] = []
|
||||
allNeededLayers.forEach(({ tags }, layerId) => {
|
||||
bodyPolygons.push(this.insertInto(tags, layerId, "polygons_").join("\n"))
|
||||
})
|
||||
|
||||
const isPolygon = LuaSnippets.isPolygonFeature()
|
||||
return [
|
||||
"function process_polygon(object, geom)",
|
||||
" local matches_filter",
|
||||
...bodyPolygons,
|
||||
"end",
|
||||
"function process_linestring(object, geom)",
|
||||
" local matches_filter",
|
||||
...bodyLines,
|
||||
"end",
|
||||
"",
|
||||
"function osm2pgsql.process_way(object)",
|
||||
this.earlyAbort(),
|
||||
" local object_is_line = not object.is_closed or " +
|
||||
LuaSnippets.toLuaFilter(isPolygon.blacklist),
|
||||
` local object_is_area = object.is_closed and (object.tags["area"] == "yes" or (not object_is_line and ${LuaSnippets.toLuaFilter(
|
||||
isPolygon.whitelisted,
|
||||
true
|
||||
)}))`,
|
||||
" if object_is_area then",
|
||||
" process_polygon(object, object:as_polygon())",
|
||||
" else",
|
||||
" process_linestring(object, object:as_linestring())",
|
||||
" end",
|
||||
"end",
|
||||
].join("\n")
|
||||
}
|
||||
}
|
||||
|
||||
new GenerateBuildDbScript().run()
|
217
scripts/osm2pgsql/tilecountServer.ts
Normal file
217
scripts/osm2pgsql/tilecountServer.ts
Normal file
|
@ -0,0 +1,217 @@
|
|||
import { Client } from "pg"
|
||||
import { Tiles } from "../../src/Models/TileRange"
|
||||
import { Server } from "../server"
|
||||
|
||||
/**
|
||||
* Just the OSM2PGSL default database
|
||||
*/
|
||||
interface PoiDatabaseMeta {
|
||||
attributes
|
||||
current_timestamp
|
||||
db_format
|
||||
flat_node_file
|
||||
import_timestamp
|
||||
output
|
||||
prefix
|
||||
replication_base_url
|
||||
replication_sequence_number
|
||||
replication_timestamp
|
||||
style
|
||||
updatable
|
||||
version
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects with a Postgis database, gives back how much items there are within the given BBOX
|
||||
*/
|
||||
class OsmPoiDatabase {
|
||||
private static readonly prefixes: ReadonlyArray<string> = ["pois", "lines", "polygons"]
|
||||
private readonly _client: Client
|
||||
private isConnected = false
|
||||
private supportedLayers: Set<string> = undefined
|
||||
private supportedLayersDate: Date = undefined
|
||||
private metaCache: PoiDatabaseMeta = undefined
|
||||
private metaCacheDate: Date = undefined
|
||||
|
||||
constructor(connectionString: string) {
|
||||
this._client = new Client(connectionString)
|
||||
}
|
||||
|
||||
async getCount(
|
||||
layer: string,
|
||||
bbox: [[number, number], [number, number]] = undefined
|
||||
): Promise<{ count: number; lat: number; lon: number }> {
|
||||
if (!this.isConnected) {
|
||||
await this._client.connect()
|
||||
this.isConnected = true
|
||||
}
|
||||
|
||||
let total: number = 0
|
||||
let latSum = 0
|
||||
let lonSum = 0
|
||||
for (const prefix of OsmPoiDatabase.prefixes) {
|
||||
let query =
|
||||
"SELECT COUNT(*), ST_AsText(ST_Centroid(ST_Collect(geom))) FROM " +
|
||||
prefix +
|
||||
"_" +
|
||||
layer
|
||||
|
||||
if (bbox) {
|
||||
query += ` WHERE ST_MakeEnvelope (${bbox[0][0]}, ${bbox[0][1]}, ${bbox[1][0]}, ${bbox[1][1]}, 4326) ~ geom`
|
||||
}
|
||||
const result = await this._client.query(query)
|
||||
const count = Number(result.rows[0].count)
|
||||
let point = result.rows[0].st_astext
|
||||
if (count === 0) {
|
||||
continue
|
||||
}
|
||||
total += count
|
||||
if (!point) {
|
||||
continue
|
||||
}
|
||||
point = point.substring(6, point.length - 1)
|
||||
const [lon, lat] = point.split(" ")
|
||||
latSum += lat * count
|
||||
lonSum += lon * count
|
||||
}
|
||||
|
||||
return { count: total, lat: latSum / total, lon: lonSum / total }
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this._client.end()
|
||||
}
|
||||
|
||||
async getLayers(): Promise<Set<string>> {
|
||||
if (
|
||||
this.supportedLayers !== undefined &&
|
||||
new Date().getTime() - this.supportedLayersDate.getTime() < 1000 * 60 * 60 * 24
|
||||
) {
|
||||
return this.supportedLayers
|
||||
}
|
||||
const q =
|
||||
"SELECT table_name \n" +
|
||||
"FROM information_schema.tables \n" +
|
||||
"WHERE table_schema = 'public' AND table_name LIKE 'lines_%';"
|
||||
const result = await this._client.query(q)
|
||||
const layers = result.rows.map((r) => r.table_name.substring("lines_".length))
|
||||
this.supportedLayers = new Set(layers)
|
||||
this.supportedLayersDate = new Date()
|
||||
return this.supportedLayers
|
||||
}
|
||||
|
||||
async getMeta(): Promise<PoiDatabaseMeta> {
|
||||
const now = new Date()
|
||||
if (this.metaCache !== undefined) {
|
||||
const diffSec = (this.metaCacheDate.getTime() - now.getTime()) / 1000
|
||||
if (diffSec < 120) {
|
||||
return this.metaCache
|
||||
}
|
||||
}
|
||||
const result = await this._client.query("SELECT * FROM public.osm2pgsql_properties")
|
||||
const meta = {}
|
||||
for (const { property, value } of result.rows) {
|
||||
meta[property] = value
|
||||
}
|
||||
this.metaCacheDate = now
|
||||
this.metaCache = <any>meta
|
||||
return this.metaCache
|
||||
}
|
||||
}
|
||||
|
||||
class CachedSqlCount {
|
||||
private readonly _cache: Record<
|
||||
string,
|
||||
Record<
|
||||
number,
|
||||
{
|
||||
date: Date
|
||||
entry: { count: number; lat: number; lon: number }
|
||||
}
|
||||
>
|
||||
> = {}
|
||||
|
||||
private readonly _poiDatabase: OsmPoiDatabase
|
||||
private readonly _maxAge: number
|
||||
|
||||
constructor(poiDatabase: OsmPoiDatabase, maxAge: number) {
|
||||
this._poiDatabase = poiDatabase
|
||||
this._maxAge = maxAge
|
||||
}
|
||||
|
||||
public async getCount(
|
||||
layer: string,
|
||||
tileId: number
|
||||
): Promise<{ count: number; lat: number; lon: number }> {
|
||||
const cachedEntry = this._cache[layer]?.[tileId]
|
||||
if (cachedEntry) {
|
||||
const age = (new Date().getTime() - cachedEntry.date.getTime()) / 1000
|
||||
if (age < this._maxAge) {
|
||||
return cachedEntry.entry
|
||||
}
|
||||
}
|
||||
const bbox = Tiles.tile_bounds_lon_lat(...Tiles.tile_from_index(tileId))
|
||||
const count = await this._poiDatabase.getCount(layer, bbox)
|
||||
if (!this._cache[layer]) {
|
||||
this._cache[layer] = {}
|
||||
}
|
||||
this._cache[layer][tileId] = { entry: count, date: new Date() }
|
||||
return count
|
||||
}
|
||||
}
|
||||
|
||||
const connectionString = "postgresql://user:password@localhost:5444/osm-poi"
|
||||
const tcs = new OsmPoiDatabase(connectionString)
|
||||
const withCache = new CachedSqlCount(tcs, 14 * 60 * 60 * 24)
|
||||
new Server(2345, { ignorePathPrefix: ["summary"] }, [
|
||||
{
|
||||
mustMatch: "status.json",
|
||||
mimetype: "application/json",
|
||||
handle: async (path: string) => {
|
||||
const layers = await tcs.getLayers()
|
||||
const meta = await tcs.getMeta()
|
||||
return JSON.stringify({ meta, layers: Array.from(layers) })
|
||||
},
|
||||
},
|
||||
{
|
||||
mustMatch: /[a-zA-Z0-9+_-]+\/[0-9]+\/[0-9]+\/[0-9]+\.json/,
|
||||
mimetype: "application/json", // "application/vnd.geo+json",
|
||||
async handle(path) {
|
||||
const [layers, z, x, y] = path.split(".")[0].split("/")
|
||||
|
||||
let sum = 0
|
||||
let properties: Record<string, number> = {}
|
||||
const availableLayers = await tcs.getLayers()
|
||||
let latSum = 0
|
||||
let lonSum = 0
|
||||
for (const layer of layers.split("+")) {
|
||||
if (!availableLayers.has(layer)) {
|
||||
continue
|
||||
}
|
||||
const count = await withCache.getCount(
|
||||
layer,
|
||||
Tiles.tile_index(Number(z), Number(x), Number(y))
|
||||
)
|
||||
|
||||
properties[layer] = count.count
|
||||
if (count.count !== 0) {
|
||||
latSum += count.lat * count.count
|
||||
lonSum += count.lon * count.count
|
||||
sum += count.count
|
||||
}
|
||||
}
|
||||
|
||||
properties["lon"] = lonSum / sum
|
||||
properties["lat"] = latSum / sum
|
||||
|
||||
return JSON.stringify({ ...properties, total: sum })
|
||||
},
|
||||
},
|
||||
])
|
||||
console.log(
|
||||
">>>",
|
||||
await tcs.getCount("drinking_water", [
|
||||
[3.194358020772171, 51.228073636083394],
|
||||
[3.2839964396059145, 51.172701162680994],
|
||||
])
|
||||
)
|
|
@ -1,5 +0,0 @@
|
|||
#! /bin/bash
|
||||
|
||||
# npm run generate:layeroverview
|
||||
cd ../..
|
||||
ts-node scripts/generateCache.ts postal_codes 8 /home/pietervdvn/Downloads/postal_codes 49.69606181911566 2.373046875 51.754240074033525 6.459960937499999 --generate-point-overview '*' --force-zoom-level 1
|
|
@ -1,5 +1,5 @@
|
|||
import Script from "../scripts/Script"
|
||||
import { Server } from "../scripts/server"
|
||||
import { Server } from "./server"
|
||||
import { Utils } from "../src/Utils"
|
||||
import parse from "node-html-parser"
|
||||
class ServerLdScrape extends Script {
|
||||
|
@ -8,20 +8,24 @@ class ServerLdScrape extends Script {
|
|||
}
|
||||
async main(args: string[]): Promise<void> {
|
||||
const port = Number(args[0] ?? 2346)
|
||||
const cache: Record<string, any> = []
|
||||
const cache: Record<string, { date: Date; contents: any }> = {}
|
||||
new Server(port, {}, [
|
||||
{
|
||||
mustMatch: "extractgraph",
|
||||
mimetype: "application/ld+json",
|
||||
async handle(content, searchParams: URLSearchParams) {
|
||||
const url = searchParams.get("url")
|
||||
console.log("Fetching", url)
|
||||
if (cache[url]) {
|
||||
return JSON.stringify(cache[url])
|
||||
if (cache[url] !== undefined) {
|
||||
const { date, contents } = cache[url]
|
||||
// In seconds
|
||||
const tdiff = (new Date().getTime() - date.getTime()) / 1000
|
||||
if (tdiff < 24 * 60 * 60) {
|
||||
return contents
|
||||
}
|
||||
}
|
||||
const dloaded = await Utils.download(url, {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", // "MapComplete/openstreetmap scraper; pietervdvn@posteo.net; https://github.com/pietervdvn/MapComplete",
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36", // MapComplete/openstreetmap scraper; pietervdvn@posteo.net; https://github.com/pietervdvn/MapComplete",
|
||||
})
|
||||
// return dloaded
|
||||
const parsed = parse(dloaded)
|
||||
|
@ -41,8 +45,6 @@ class ServerLdScrape extends Script {
|
|||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify({})
|
||||
},
|
||||
},
|
||||
])
|
||||
|
|
|
@ -1,26 +1,12 @@
|
|||
/**
|
||||
* This actor will download the latest version of the selected element from OSM and update the tags if necessary.
|
||||
*/
|
||||
import { UIEventSource } from "../UIEventSource"
|
||||
import { Changes } from "../Osm/Changes"
|
||||
import { OsmConnection } from "../Osm/OsmConnection"
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import SimpleMetaTagger from "../SimpleMetaTagger"
|
||||
import { Feature } from "geojson"
|
||||
import { OsmTags } from "../../Models/OsmFeature"
|
||||
import OsmObjectDownloader from "../Osm/OsmObjectDownloader"
|
||||
import { IndexedFeatureSource } from "../FeatureSource/FeatureSource"
|
||||
import { Utils } from "../../Utils"
|
||||
|
||||
interface TagsUpdaterState {
|
||||
selectedElement: UIEventSource<Feature>
|
||||
featureProperties: { getStore: (id: string) => UIEventSource<Record<string, string>> }
|
||||
changes: Changes
|
||||
osmConnection: OsmConnection
|
||||
layout: LayoutConfig
|
||||
osmObjectDownloader: OsmObjectDownloader
|
||||
indexedFeatures: IndexedFeatureSource
|
||||
}
|
||||
import ThemeViewState from "../../Models/ThemeViewState"
|
||||
import { BBox } from "../BBox"
|
||||
import { Feature } from "geojson"
|
||||
|
||||
export default class SelectedElementTagsUpdater {
|
||||
private static readonly metatags = new Set([
|
||||
|
@ -31,19 +17,21 @@ export default class SelectedElementTagsUpdater {
|
|||
"uid",
|
||||
"id",
|
||||
])
|
||||
private readonly state: ThemeViewState
|
||||
|
||||
constructor(state: TagsUpdaterState) {
|
||||
constructor(state: ThemeViewState) {
|
||||
this.state = state
|
||||
state.osmConnection.isLoggedIn.addCallbackAndRun((isLoggedIn) => {
|
||||
if (!isLoggedIn && !Utils.runningFromConsole) {
|
||||
return
|
||||
}
|
||||
this.installCallback(state)
|
||||
this.installCallback()
|
||||
// We only have to do this once...
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
public static applyUpdate(latestTags: OsmTags, id: string, state: TagsUpdaterState) {
|
||||
public static applyUpdate(latestTags: OsmTags, id: string, state: ThemeViewState) {
|
||||
try {
|
||||
const leftRightSensitive = state.layout.isLeftRightSensitive()
|
||||
|
||||
|
@ -120,8 +108,13 @@ export default class SelectedElementTagsUpdater {
|
|||
console.error("Updating the tags of selected element ", id, "failed due to", e)
|
||||
}
|
||||
}
|
||||
|
||||
private installCallback(state: TagsUpdaterState) {
|
||||
private invalidateCache(s: Feature) {
|
||||
const state = this.state
|
||||
const wasPartOfLayer = state.layout.getMatchingLayer(s.properties)
|
||||
state.toCacheSavers.get(wasPartOfLayer.id).invalidateCacheAround(BBox.get(s))
|
||||
}
|
||||
private installCallback() {
|
||||
const state = this.state
|
||||
state.selectedElement.addCallbackAndRunD(async (s) => {
|
||||
let id = s.properties?.id
|
||||
if (!id) {
|
||||
|
@ -146,9 +139,9 @@ export default class SelectedElementTagsUpdater {
|
|||
const osmObject = await state.osmObjectDownloader.DownloadObjectAsync(id)
|
||||
if (osmObject === "deleted") {
|
||||
console.debug("The current selected element has been deleted upstream!", id)
|
||||
this.invalidateCache(s)
|
||||
const currentTagsSource = state.featureProperties.getStore(id)
|
||||
currentTagsSource.data["_deleted"] = "yes"
|
||||
currentTagsSource.addCallbackAndRun((tags) => console.trace("Tags are", tags))
|
||||
currentTagsSource.ping()
|
||||
return
|
||||
}
|
||||
|
@ -158,6 +151,7 @@ export default class SelectedElementTagsUpdater {
|
|||
const oldGeometry = oldFeature?.geometry
|
||||
if (oldGeometry !== undefined && !Utils.SameObject(newGeometry, oldGeometry)) {
|
||||
console.log("Detected a difference in geometry for ", id)
|
||||
this.invalidateCache(s)
|
||||
oldFeature.geometry = newGeometry
|
||||
state.featureProperties.getStore(id)?.ping()
|
||||
}
|
||||
|
|
|
@ -45,15 +45,16 @@ export class BBox {
|
|||
])
|
||||
}
|
||||
|
||||
static get(feature): BBox {
|
||||
if (feature.bbox?.overlapsWith === undefined) {
|
||||
static get(feature: Feature): BBox {
|
||||
const f = <any>feature
|
||||
if (f.bbox?.overlapsWith === undefined) {
|
||||
const turfBbox: number[] = turf.bbox(feature)
|
||||
feature.bbox = new BBox([
|
||||
f["bbox"] = new BBox([
|
||||
[turfBbox[0], turfBbox[1]],
|
||||
[turfBbox[2], turfBbox[3]],
|
||||
])
|
||||
}
|
||||
return feature.bbox
|
||||
return f["bbox"]
|
||||
}
|
||||
|
||||
static bboxAroundAll(bboxes: BBox[]): BBox {
|
||||
|
|
|
@ -5,6 +5,8 @@ import { GeoOperations } from "../../GeoOperations"
|
|||
import FeaturePropertiesStore from "./FeaturePropertiesStore"
|
||||
import { UIEventSource } from "../../UIEventSource"
|
||||
import { Utils } from "../../../Utils"
|
||||
import { Tiles } from "../../../Models/TileRange"
|
||||
import { BBox } from "../../BBox"
|
||||
|
||||
class SingleTileSaver {
|
||||
private readonly _storage: UIEventSource<Feature[]>
|
||||
|
@ -54,6 +56,8 @@ class SingleTileSaver {
|
|||
* Also see the sibling class
|
||||
*/
|
||||
export default class SaveFeatureSourceToLocalStorage {
|
||||
public readonly storage: TileLocalStorage<Feature[]>
|
||||
private zoomlevel: number
|
||||
constructor(
|
||||
backend: string,
|
||||
layername: string,
|
||||
|
@ -62,7 +66,9 @@ export default class SaveFeatureSourceToLocalStorage {
|
|||
featureProperties: FeaturePropertiesStore,
|
||||
maxCacheAge: number
|
||||
) {
|
||||
this.zoomlevel = zoomlevel
|
||||
const storage = TileLocalStorage.construct<Feature[]>(backend, layername, maxCacheAge)
|
||||
this.storage = storage
|
||||
const singleTileSavers: Map<number, SingleTileSaver> = new Map<number, SingleTileSaver>()
|
||||
features.features.addCallbackAndRunD((features) => {
|
||||
const sliced = GeoOperations.slice(zoomlevel, features)
|
||||
|
@ -80,4 +86,12 @@ export default class SaveFeatureSourceToLocalStorage {
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
public invalidateCacheAround(bbox: BBox) {
|
||||
const range = Tiles.tileRangeFrom(bbox, this.zoomlevel)
|
||||
Tiles.MapRange(range, (x, y) => {
|
||||
const index = Tiles.tile_index(this.zoomlevel, x, y)
|
||||
this.storage.invalidate(index)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { IdbLocalStorage } from "../../Web/IdbLocalStorage"
|
||||
import { UIEventSource } from "../../UIEventSource"
|
||||
import { Tiles } from "../../../Models/TileRange"
|
||||
|
||||
/**
|
||||
* A class which allows to read/write a tile to local storage.
|
||||
|
@ -91,9 +92,17 @@ export default class TileLocalStorage<T> {
|
|||
await IdbLocalStorage.GetDirectly(this._layername + "_" + tileIndex + "_date")
|
||||
)
|
||||
const maxAge = this._maxAgeSeconds
|
||||
const timeDiff = Date.now() - date
|
||||
const timeDiff = (Date.now() - date) / 1000
|
||||
if (timeDiff >= maxAge) {
|
||||
console.debug("Dropping cache for", this._layername, tileIndex, "out of date")
|
||||
console.debug(
|
||||
"Dropping cache for",
|
||||
this._layername,
|
||||
tileIndex,
|
||||
"out of date. Max allowed age is",
|
||||
maxAge,
|
||||
"current age is",
|
||||
timeDiff
|
||||
)
|
||||
await IdbLocalStorage.SetDirectly(this._layername + "_" + tileIndex, undefined)
|
||||
|
||||
return undefined
|
||||
|
@ -102,7 +111,8 @@ export default class TileLocalStorage<T> {
|
|||
return <any>data
|
||||
}
|
||||
|
||||
invalidate(zoomlevel: number, tileIndex) {
|
||||
public invalidate(tileIndex: number) {
|
||||
console.log("Invalidated tile", tileIndex)
|
||||
this.getTileSource(tileIndex).setData(undefined)
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue