diff --git a/.env b/.env index 395b1d9..6fafc98 100644 --- a/.env +++ b/.env @@ -1,15 +1,15 @@ # This file defines default environment variables for all images # Layers definition and meta data -TILESET_FILE=vfosnarmaptiles.yaml +TILESET_FILE=openmaptiles.yaml # Use 3-part patch version to ignore patch updates, e.g. 7.0.0 TOOLS_VERSION=7.1 # Make sure these values are in sync with the ones in .env-postgres file -PGDATABASE=vfosnarmaptiles -PGUSER=vfosnarmaptiles -PGPASSWORD=vfosnarmaptiles +PGDATABASE=openmaptiles +PGUSER=openmaptiles +PGPASSWORD=openmaptiles PGHOST=postgres PGPORT=5432 diff --git a/BENCHMARKING.md b/BENCHMARKING.md new file mode 100644 index 0000000..58b5fe9 --- /dev/null +++ b/BENCHMARKING.md @@ -0,0 +1,17 @@ +## To determine whether any changes are a notable loss in performance: + +1. Start with the old approach +2. Use a fresh database +3. Use a large extract, such as France, Germany, Britain, etc. Too small of PBFs may be entirely cached in RAM and not representative of planet performance +4. Time the extract, ensuring you have the desired zoom level as set in .env +5. Switch to the new approach and use a fresh database once again +6. Quickstart.log will have all the time logs. +7. If necessary, run an update as well to see if your approach can keep up with live updates on the chosen interval. Currently, weekly updates are the target. + +## To determine whether your changes in SQL are lossless: + +It is recommended to use the "sqldiff" tool to compare mbtiles. + +`sqldiff --table tiles new.mbtiles old.mbtiles` + +This will compare on just the tiles table, ensuring for each zoom, x, y that the tile data is the same between both mbtiles. If sqldiff returns nothing, it means the results are identical. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..18d75c8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,88 @@ +# Introduction + +Thank you for considering contributing to OpenMapTiles. It's people like you that make OpenMapTiles such a great project. Talk to us at the OSM Slack **#openmaptiles** channel ([join](https://slack.openstreetmap.us/)). + +Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue, assessing changes, and helping you finalize your pull requests. + +OpenMapTiles is an open source project and we love to receive contributions from our community — you! There are many ways to contribute, from writing tutorials or blog posts, improving the documentation, submitting bug reports and feature requests or writing code which can be incorporated into OpenMapTiles itself. + +# Ground Rules + + * Create issues for any major changes and enhancements that you wish to make. Discuss things transparently and get community feedback. + * Keep feature versions as small as possible, preferably one new feature per version. + * Be welcoming to newcomers and encourage diverse new contributors from all backgrounds. See the [Python Community Code of Conduct](https://www.python.org/psf/codeofconduct/). + +# Getting started + +1. Create your own fork of the code +1. Do the changes in your fork +1. Create a pull request + +# Code review process + +We all make mistakes and bad coding decisions. So apart from the obvious fixes, all changes must be reviewed by another 2 members of the project. This also helps with the [bus factor](https://en.wikipedia.org/wiki/Bus_factor) -- there should always be other people in the team who know why a change was made. + +For any non-trivial changes, all pull requests must be approved by at least three members of the OpenMapTiles team. Afterwards you can merge the PR if you have rights, or another person must do it for you. + +Your pull request must: + + * Address a single issue or add a single item of functionality. + * Contain a clean history of small, incremental, logically separate commits, + with no merge commits. + * Use clear commit messages. + * Be possible to merge automatically. + +When you modify import data rules in `mapping.yaml` or `*.sql`, please update: + +1. field description in `[layer].yaml` +2. comments starting with `#etldoc` +3. regenerate documentation graphs with `make generate-devdoc` +4. update layer description on https://openmaptiles.org/schema/ (https://github.com/openmaptiles/www.openmaptiles.org/tree/master/layers) +5. check if OMT styles are affected by the PR and if there is a need for style updates + +When you are making PR that adds new spatial features to OpenMapTiles schema, please make also PR for at least one of our GL styles to show it on the map. Visual check is crucial. + +# SQL unit testing + +It is recommended that you create a [unit test](TESTING.md) when modifying the behavior of the SQL layer. This will ensure that your changes are working as expected when importing or updating OSM data into an OpenMapTiles database. + +# Verifying that updates still work + +When testing a PR, you should also verify that the update process completes without an error. Please modify, if necessary, and run the script below. + +**Note:** + +The verification requires the script to append temporary changes to the `.env` file. Please restore the original version from git using `git checkout .env` or remove these changes before submitting a PR. + +``` +( +set -e + +cat >> .env << EOM + +# temporary changes for verifying that updates still work +# Ensure DIFF_MODE is active +DIFF_MODE=true +# Ensure all zoom levels are tested +MAX_ZOOM=14 +EOM + +# Set the test area to the appropriate geofabrik extract +export area=north-america/us/indiana + +# Build 1-month-old tiles +rm -fr data build cache +make destroy-db +make download-geofabrik area=$area +docker-compose run --rm --user=$(id -u):$(id -g) openmaptiles-tools sh -c "wget -nv -O data/$area.osm.pbf http://download.geofabrik.de/$area-$(date --date="$(date +%Y-%m-15) -1 month" +'%y%m01').osm.pbf" +./quickstart.sh $area +cat << EOM + +# Update with the changes since a month+ ago + +EOM +docker-compose run --rm --user=$(id -u):$(id -g) openmaptiles-tools sh -c "osmupdate --base-url=$(sed -n 's/ *\"replication_url\": //p' data/$area.repl.json) data/$area.osm.pbf data/changes.osc.gz" +make import-diff +make generate-tiles-pg +) < /dev/null +``` diff --git a/Makefile b/Makefile index 39d6873..b39d739 100644 --- a/Makefile +++ b/Makefile @@ -243,7 +243,7 @@ export HELP_MESSAGE # .PHONY: all -all: init-dirs build/openmaptiles.tm2source/data.yml build/mapping.yaml build-sql +all: init-dirs build/openmaptiles.tm2source/data.yml build/mapping.yaml build-sql build-style .PHONY: help help: @@ -596,7 +596,7 @@ psql-list-tables: init-dirs .PHONY: vacuum-db vacuum-db: init-dirs @echo "Start - postgresql: VACUUM ANALYZE VERBOSE;" - $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools psql.sh -v ON_ERROR_STOP=1 -P pager=off -c 'VACUUM ANALYZE VERBOSE;' + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools psql.sh -v ON_ERROR_STOP=1 -P pager=off -c 'VACUUM (ANALYZE, VERBOSE);' .PHONY: analyze-db analyze-db: init-dirs diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..16e4589 --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,477 @@ +## Quickstart - for small extracts + +### Req: +* CPU: AMD64 ( = Intel 64 bit) + * The base docker debian images are x86_64 based, so the ARM, MIPS currently not supported! +* Operating system + * Linux is suggested + * The development and the testing platform is Linux. + * If you are using FreeBSD, Solaris, Windows, ... + * Please give a feedback, share your experience, write a tutorial +* bash +* git +* make +* bc +* md5sum +* docker >=1.12.3 + * https://www.docker.com/products/overview +* docker-compose >=1.7.1 + * https://docs.docker.com/compose/install/ +* disk space ( >= ~15Gb ) + * for small extracts >= ~15Gb + * for big extracts ( continents, planet) 250 Gb + * And depends on + * OpenStreetMap data size + * Zoom level + * Best on SSD for postserve but completely usable on HDD + * Takes 24hrs to import on a reasonable machine, and is immediately available with postserve +* memory ( >= 3Gb ) + * for small extracts 3Gb-8Gb RAM + * for big extracts ( Europe, Planet) > 8-32 Gb +* internet connections + * for downloading docker images + * for downloading OpenStreetMap data from Geofabrik + +Important: The ./quickstart.sh is for small extracts - not optimal for a Planet rendering !! + +### First experiment - with `albania` ( small extracts! ) + +```bash +git clone https://github.com/openmaptiles/openmaptiles.git +cd openmaptiles +./quickstart.sh +``` + +If you have problems with the quickstart +* check the ./quickstart.log! +* doublecheck the system requirements! +* check the current issues: https://github.com/openmaptiles/openmaptiles/issues +* create new issues: + * create a new gist: https://gist.github.com/ from your ./quickstart.log + * doublecheck: don't reveal any sensitive information about your system + * create a new issue: https://github.com/openmaptiles/openmaptiles/issues + * describe the problems + * add any pertinent information about your environment + * link your (quickstart.log) gist! + +### Check other extracts + +IF the previous step is working, +THEN you can test other available quickstart extracts ( based on [Geofabrik extracts](http://download.geofabrik.de/index.html) ) ! + * We are using https://github.com/julien-noblet/download-geofabrik tool + * The current extract list, and more information -> `make list-geofabrik` or `make list-bbbike` + +This is generating `.mbtiles` for your area : [ MIN_ZOOM: "0" - MAX_ZOOM: "7" ] + +```bash +./quickstart.sh africa # Africa +./quickstart.sh alabama # Alabama, US +./quickstart.sh alaska # Alaska, US +./quickstart.sh albania # Albania, Europe +./quickstart.sh alberta # Alberta, Canada +./quickstart.sh alps # Alps, Europe +./quickstart.sh alsace # Alsace, France +./quickstart.sh andorra # Andorra, Europe +./quickstart.sh antarctica # Antarctica +./quickstart.sh aquitaine # Aquitaine, France +./quickstart.sh argentina # Argentina, South-America +./quickstart.sh arizona # Arizona, US +./quickstart.sh arkansas # Arkansas, US +./quickstart.sh arnsberg-regbez # Regierungsbezirk Arnsberg, Nordrhein-Westfalen +./quickstart.sh asia # Asia +./quickstart.sh australia # Australia, Australia-Oceania +./quickstart.sh australia-oceania # Australia and Oceania +./quickstart.sh austria # Austria, Europe +./quickstart.sh auvergne # Auvergne, France +./quickstart.sh azerbaijan # Azerbaijan, Asia +./quickstart.sh azores # Azores, Europe +./quickstart.sh baden-wuerttemberg # Baden-Württemberg, Germany +./quickstart.sh bangladesh # Bangladesh, Asia +./quickstart.sh basse-normandie # Basse-Normandie, France +./quickstart.sh bayern # Bayern, Germany +./quickstart.sh belarus # Belarus, Europe +./quickstart.sh belgium # Belgium, Europe +./quickstart.sh belize # Belize, Central-America +./quickstart.sh berlin # Berlin, Germany +./quickstart.sh bolivia # Bolivia, South-America +./quickstart.sh bosnia-herzegovina # Bosnia-Herzegovina, Europe +./quickstart.sh botswana # Botswana, Africa +./quickstart.sh bourgogne # Bourgogne, France +./quickstart.sh brandenburg # Brandenburg, Germany +./quickstart.sh brazil # Brazil, South-America +./quickstart.sh bremen # Bremen, Germany +./quickstart.sh bretagne # Bretagne, France +./quickstart.sh british-columbia # British Columbia, Canada +./quickstart.sh british-isles # British Isles, Europe +./quickstart.sh buckinghamshire # Buckinghamshire, England +./quickstart.sh bulgaria # Bulgaria, Europe +./quickstart.sh burkina-faso # Burkina Faso, Africa +./quickstart.sh california # California, US +./quickstart.sh cambridgeshire # Cambridgeshire, England +./quickstart.sh cameroon # Cameroon +./quickstart.sh canada # Canada, North-America +./quickstart.sh canary-islands # Canary Islands, Africa +./quickstart.sh central-america # Central America +./quickstart.sh centre # Centre, France +./quickstart.sh champagne-ardenne # Champagne Ardenne, France +./quickstart.sh cheshire # Cheshire, England +./quickstart.sh chile # Chile, South-America +./quickstart.sh china # China, Asia +./quickstart.sh colombia # Colombia, South-America +./quickstart.sh colorado # Colorado, US +./quickstart.sh congo-democratic-republic # Congo (Democratic Republic), Africa +./quickstart.sh connecticut # Connecticut, US +./quickstart.sh cornwall # Cornwall, England +./quickstart.sh corse # Corse, France +./quickstart.sh croatia # Croatia, Europe +./quickstart.sh cuba # Cuba, Central-America +./quickstart.sh cumbria # Cumbria, England +./quickstart.sh cyprus # Cyprus, Europe +./quickstart.sh czech-republic # Czech Republic, Europe +./quickstart.sh dach # Germany, Austria, Switzerland, Europe +./quickstart.sh delaware # Delaware, US +./quickstart.sh denmark # Denmark, Europe +./quickstart.sh derbyshire # Derbyshire, England +./quickstart.sh detmold-regbez # Regierungsbezirk Detmold, Nordrhein-Westfalen +./quickstart.sh devon # Devon, England +./quickstart.sh district-of-columbia # District of Columbia, US +./quickstart.sh dorset # Dorset, England +./quickstart.sh duesseldorf-regbez # Regierungsbezirk Düsseldorf, Nordrhein-Westfalen +./quickstart.sh east-sussex # East Sussex, England +./quickstart.sh east-yorkshire-with-hull # East Yorkshire with Hull, England +./quickstart.sh ecuador # Ecuador, South-America +./quickstart.sh egypt # Egypt, Africa +./quickstart.sh england # England, Great-Britain +./quickstart.sh essex # Essex, England +./quickstart.sh estonia # Estonia, Europe +./quickstart.sh ethiopia # Ethiopia, Africa +./quickstart.sh europe # Europe +./quickstart.sh faroe-islands # Faroe Islands, Europe +./quickstart.sh fiji # Fiji, Australia-Oceania +./quickstart.sh finland # Finland, Europe +./quickstart.sh florida # Florida, US +./quickstart.sh france # France, Europe +./quickstart.sh franche-comte # Franche Comte, France +./quickstart.sh freiburg-regbez # Regierungsbezirk Freiburg, Baden-Wuerttemberg +./quickstart.sh gcc-states # GCC States, Asia +./quickstart.sh georgia-eu # Georgia (Eastern Europe), Europe +./quickstart.sh georgia-us # Georgia (US State), US +./quickstart.sh germany # Germany, Europe +./quickstart.sh gloucestershire # Gloucestershire, England +./quickstart.sh great-britain # Great Britain, Europe +./quickstart.sh greater-london # Greater London, England +./quickstart.sh greater-manchester # Greater Manchester, England +./quickstart.sh greece # Greece, Europe +./quickstart.sh greenland # Greenland, North-America +./quickstart.sh guadeloupe # Guadeloupe, France +./quickstart.sh guatemala # Guatemala, Central-America +./quickstart.sh guinea # Guinea, Africa +./quickstart.sh guinea-bissau # Guinea-Bissau, Africa +./quickstart.sh guyane # Guyane, France +./quickstart.sh haiti-and-domrep # Haiti and Dominican Republic, Central-America +./quickstart.sh hamburg # Hamburg, Germany +./quickstart.sh hampshire # Hampshire, England +./quickstart.sh haute-normandie # Haute-Normandie, France +./quickstart.sh hawaii # Hawaii, US +./quickstart.sh herefordshire # Herefordshire, England +./quickstart.sh hertfordshire # Hertfordshire, England +./quickstart.sh hessen # Hessen, Germany +./quickstart.sh hungary # Hungary, Europe +./quickstart.sh iceland # Iceland, Europe +./quickstart.sh idaho # Idaho, US +./quickstart.sh ile-de-france # Ile-de-France, France +./quickstart.sh illinois # Illinois, US +./quickstart.sh india # India, Asia +./quickstart.sh indiana # Indiana, US +./quickstart.sh indonesia # Indonesia, Asia +./quickstart.sh iowa # Iowa, US +./quickstart.sh irak # Irak, Asia +./quickstart.sh iran # Iran, Asia +./quickstart.sh ireland-and-northern-ireland # Ireland and Northern Ireland, Europe +./quickstart.sh isle-of-man # Isle of Man, Europe +./quickstart.sh isle-of-wight # Isle of Wight, England +./quickstart.sh israel-and-palestine # Israel and Palestine, Asia +./quickstart.sh italy # Italy, Europe +./quickstart.sh ivory-coast # Ivory Coast, Africa +./quickstart.sh japan # Japan, Asia +./quickstart.sh jordan # Jordan, Asia +./quickstart.sh kansas # Kansas, US +./quickstart.sh karlsruhe-regbez # Regierungsbezirk Karlsruhe, Baden-Wuerttemberg +./quickstart.sh kazakhstan # Kazakhstan, Asia +./quickstart.sh kent # Kent, England +./quickstart.sh kentucky # Kentucky, US +./quickstart.sh kenya # Kenya, Africa +./quickstart.sh koeln-regbez # Regierungsbezirk Köln, Nordrhein-Westfalen +./quickstart.sh kosovo # Kosovo, Europe +./quickstart.sh kyrgyzstan # Kyrgyzstan, Asia +./quickstart.sh lancashire # Lancashire, England +./quickstart.sh languedoc-roussillon # Languedoc-Roussillon, France +./quickstart.sh latvia # Latvia, Europe +./quickstart.sh lebanon # Lebanon, Asia +./quickstart.sh leicestershire # Leicestershire, England +./quickstart.sh lesotho # Lesotho, Africa +./quickstart.sh liberia # Liberia, Africa +./quickstart.sh libya # Libya, Africa +./quickstart.sh liechtenstein # Liechtenstein, Europe +./quickstart.sh limousin # Limousin, France +./quickstart.sh lithuania # Lithuania, Europe +./quickstart.sh lorraine # Lorraine, France +./quickstart.sh louisiana # Louisiana, US +./quickstart.sh luxembourg # Luxembourg, Europe +./quickstart.sh macedonia # Macedonia, Europe +./quickstart.sh madagascar # Madagascar, Africa +./quickstart.sh maine # Maine, US +./quickstart.sh malaysia-singapore-brunei # Malaysia, Singapore, and Brunei, Asia +./quickstart.sh malta # Malta, Europe +./quickstart.sh manitoba # Manitoba, Canada +./quickstart.sh martinique # Martinique, France +./quickstart.sh maryland # Maryland, US +./quickstart.sh massachusetts # Massachusetts, US +./quickstart.sh mayotte # Mayotte, France +./quickstart.sh mecklenburg-vorpommern # Mecklenburg-Vorpommern, Germany +./quickstart.sh mexico # Mexico, North-America +./quickstart.sh michigan # Michigan, US +./quickstart.sh midi-pyrenees # Midi-Pyrenees, France +./quickstart.sh minnesota # Minnesota, US +./quickstart.sh mississippi # Mississippi, US +./quickstart.sh missouri # Missouri, US +./quickstart.sh mittelfranken # Mittelfranken, Bayern +./quickstart.sh moldova # Moldova, Europe +./quickstart.sh monaco # Monaco, Europe +./quickstart.sh mongolia # Mongolia, Asia +./quickstart.sh montana # Montana, US +./quickstart.sh montenegro # Montenegro, Europe +./quickstart.sh morocco # Morocco, Africa +./quickstart.sh muenster-regbez # Regierungsbezirk Münster, Nordrhein-Westfalen +./quickstart.sh nebraska # Nebraska, US +./quickstart.sh nepal # Nepal, Asia +./quickstart.sh netherlands # Netherlands, Europe +./quickstart.sh nevada # Nevada, US +./quickstart.sh new-brunswick # New Brunswick, Canada +./quickstart.sh new-caledonia # New Caledonia, Australia-Oceania +./quickstart.sh new-hampshire # New Hampshire, US +./quickstart.sh new-jersey # New Jersey, US +./quickstart.sh new-mexico # New Mexico, US +./quickstart.sh new-york # New York, US +./quickstart.sh new-zealand # New Zealand, Australia-Oceania +./quickstart.sh newfoundland-and-labrador # Newfoundland and Labrador, Canada +./quickstart.sh niederbayern # Niederbayern, Bayern +./quickstart.sh niedersachsen # Niedersachsen, Germany +./quickstart.sh nigeria # Nigeria, Africa +./quickstart.sh nord-pas-de-calais # Nord-Pas-de-Calais, France +./quickstart.sh nordrhein-westfalen # Nordrhein-Westfalen, Germany +./quickstart.sh norfolk # Norfolk, England +./quickstart.sh north-america # North America +./quickstart.sh north-carolina # North Carolina, US +./quickstart.sh north-dakota # North Dakota, US +./quickstart.sh north-korea # North Korea, Asia +./quickstart.sh north-yorkshire # North Yorkshire, England +./quickstart.sh northwest-territories # Northwest Territories, Canada +./quickstart.sh norway # Norway, Europe +./quickstart.sh nottinghamshire # Nottinghamshire, England +./quickstart.sh nova-scotia # Nova Scotia, Canada +./quickstart.sh nunavut # Nunavut, Canada +./quickstart.sh oberbayern # Oberbayern, Bayern +./quickstart.sh oberfranken # Oberfranken, Bayern +./quickstart.sh oberpfalz # Oberpfalz, Bayern +./quickstart.sh ohio # Ohio, US +./quickstart.sh oklahoma # Oklahoma, US +./quickstart.sh ontario # Ontario, Canada +./quickstart.sh oregon # Oregon, US +./quickstart.sh oxfordshire # Oxfordshire, England +./quickstart.sh pakistan # Pakistan, Asia +./quickstart.sh paraguay # Paraguay, South-America +./quickstart.sh pays-de-la-loire # Pays de la Loire, France +./quickstart.sh pennsylvania # Pennsylvania, US +./quickstart.sh peru # Peru, South-America +./quickstart.sh philippines # Philippines, Asia +./quickstart.sh picardie # Picardie, France +./quickstart.sh poitou-charentes # Poitou-Charentes, France +./quickstart.sh poland # Poland, Europe +./quickstart.sh portugal # Portugal, Europe +./quickstart.sh prince-edward-island # Prince Edward Island, Canada +./quickstart.sh provence-alpes-cote-d-azur # Provence Alpes-Cote-d'Azur, France +./quickstart.sh quebec # Quebec, Canada +./quickstart.sh reunion # Reunion, France +./quickstart.sh rheinland-pfalz # Rheinland-Pfalz, Germany +./quickstart.sh rhode-island # Rhode Island, US +./quickstart.sh rhone-alpes # Rhone-Alpes, France +./quickstart.sh romania # Romania, Europe +./quickstart.sh russia-asian-part # Russia (Asian part), Asia +./quickstart.sh russia-european-part # Russia (European part), Europe +./quickstart.sh saarland # Saarland, Germany +./quickstart.sh sachsen # Sachsen, Germany +./quickstart.sh sachsen-anhalt # Sachsen-Anhalt, Germany +./quickstart.sh saskatchewan # Saskatchewan, Canada +./quickstart.sh schleswig-holstein # Schleswig-Holstein, Germany +./quickstart.sh schwaben # Schwaben, Bayern +./quickstart.sh scotland # Scotland, Great-Britain +./quickstart.sh serbia # Serbia, Europe +./quickstart.sh shropshire # Shropshire, England +./quickstart.sh sierra-leone # Sierra Leone, Africa +./quickstart.sh slovakia # Slovakia, Europe +./quickstart.sh slovenia # Slovenia, Europe +./quickstart.sh somalia # Somalia, Africa +./quickstart.sh somerset # Somerset, England +./quickstart.sh south-africa-and-lesotho # South Africa (includes Lesotho), Africa +./quickstart.sh south-america # South America +./quickstart.sh south-carolina # South Carolina, US +./quickstart.sh south-dakota # South Dakota, US +./quickstart.sh south-korea # South Korea, Asia +./quickstart.sh south-yorkshire # South Yorkshire, England +./quickstart.sh spain # Spain, Europe +./quickstart.sh sri-lanka # Sri Lanka, Asia +./quickstart.sh staffordshire # Staffordshire, England +./quickstart.sh stuttgart-regbez # Regierungsbezirk Stuttgart, Baden-Wuerttemberg +./quickstart.sh suffolk # Suffolk, England +./quickstart.sh surrey # Surrey, England +./quickstart.sh sweden # Sweden, Europe +./quickstart.sh switzerland # Switzerland, Europe +./quickstart.sh syria # Syria, Asia +./quickstart.sh taiwan # Taiwan, Asia +./quickstart.sh tajikistan # Tajikistan, Asia +./quickstart.sh tanzania # Tanzania, Africa +./quickstart.sh tennessee # Tennessee, US +./quickstart.sh texas # Texas, US +./quickstart.sh thailand # Thailand, Asia +./quickstart.sh thueringen # Thüringen, Germany +./quickstart.sh tuebingen-regbez # Regierungsbezirk Tübingen, Baden-Wuerttemberg +./quickstart.sh turkey # Turkey, Europe +./quickstart.sh turkmenistan # Turkmenistan, Asia +./quickstart.sh ukraine # Ukraine, Europe +./quickstart.sh unterfranken # Unterfranken, Bayern +./quickstart.sh uruguay # Uruguay, South-America +./quickstart.sh us-midwest # US Midwest, North-America +./quickstart.sh us-northeast # US Northeast, North-America +./quickstart.sh us-pacific # US Pacific, North-America +./quickstart.sh us-south # US South, North-America +./quickstart.sh us-west # US West, North-America +./quickstart.sh utah # Utah, US +./quickstart.sh uzbekistan # Uzbekistan, Asia +./quickstart.sh vermont # Vermont, US +./quickstart.sh vietnam # Vietnam, Asia +./quickstart.sh virginia # Virginia, US +./quickstart.sh wales # Wales, Great-Britain +./quickstart.sh washington # Washington, US +./quickstart.sh west-midlands # West Midlands, England +./quickstart.sh west-sussex # West Sussex, England +./quickstart.sh west-virginia # West Virginia, US +./quickstart.sh west-yorkshire # West Yorkshire, England +./quickstart.sh wiltshire # Wiltshire, England +./quickstart.sh wisconsin # Wisconsin, US +./quickstart.sh wyoming # Wyoming, US +./quickstart.sh yukon # Yukon, Canada +``` +### Using your own OSM data +Mbtiles can be generated from an arbitrary osm.pbf (e.g. for a region that is not covered by an existing extract) by making the `data/` directory and placing an *.osm.pbf (e.g. `mydata.osm.pbf`) inside. + +``` +mkdir -p data +mv mydata.osm.pbf data/ +make generate-bbox-file area=mydata +./quickstart.sh mydata +``` + +### Check postserve +* ` docker-compose up -d postserve` +and the generated maps are going to be available in browser on [localhost:8090/tiles/0/0/0.pbf](http://localhost:8090/tiles/0/0/0.pbf). + +### Check tileserver + +start: +* ` make start-tileserver` +and the generated maps are going to be available in webbrowser on [localhost:8080](http://localhost:8080/). + +This is only a quick preview, because your mbtiles only generated to zoom level 7 ! + + +### Set which zooms to generate + +modify the settings in the `.env` file, the defaults: +* `MIN_ZOOM=0` +* `MAX_ZOOM=7` + +Hints: +* Small increments! Never starts with the `MAX_ZOOM = 14` +* The suggested `MAX_ZOOM = 14` - use only with small extracts + +### Set the bounding box to generate + +By default, tile generation is done for the full extent of the area. +If you want to generate a tiles for a smaller extent, modify the settings in the `.env` file, the default: +* `BBOX=-180.0,-85.0511,180.0,85.0511` + +Delete the `./data/.bbox` file, and re-start `./quickstart.sh ` + +Hint: +* The [boundingbox.klokantech.com](https://boundingbox.klokantech.com/) site can be used to find a bounding box (CSV format) using a map. + +### Check other commands + +`make help` + + +the current output: + +``` +============================================================================== +OpenMapTiles https://github.com/openmaptiles/openmaptiles + +Hints for testing areas + make list-geofabrik # list actual geofabrik OSM extracts for download -> <> + ./quickstart.sh <> # example: ./quickstart.sh madagascar + +Hints for designers: + make start-maputnik # start Maputnik Editor + dynamic tile server [ see http://localhost:8088 ] + make stop-maputnik # stop Maputnik Editor + dynamic tile server + make start-postserve # start dynamic tile server [ see http://localhost:8090 ] + make stop-postserve # stop dynamic tile server + make start-tileserver # start maptiler/tileserver-gl [ see http://localhost:8080 ] + make stop-tileserver # stop maptiler/tileserver-gl + +Hints for developers: + make # build source code + make bash # start openmaptiles-tools /bin/bash terminal + make generate-bbox-file # compute bounding box of a data file and store it in a file + make generate-devdoc # generate devdoc including graphs for all layers [./layers/...] + make generate-qa # statistics for a given layer's field + make generate-tiles-pg # generate vector tiles based on .env settings using PostGIS ST_MVT() + make generate-tiles # generate vector tiles based on .env settings using Mapnik (obsolete) + make generate-changed-tiles # Generate tiles changed by import-diff + make test-sql # run unit tests on the OpenMapTiles SQL schema + cat .env # list PG database and MIN_ZOOM and MAX_ZOOM information + cat quickstart.log # transcript of the last ./quickstart.sh run + make help # help about available commands + +Hints for downloading & importing data: + make list-geofabrik # list actual geofabrik OSM extracts for download + make list-bbbike # list actual BBBike OSM extracts for download + make download area=albania # download OSM data from any source and create config file + make download-geofabrik area=albania # download OSM data from geofabrik.de and create config file + make download-osmfr area=asia/qatar # download OSM data from openstreetmap.fr and create config file + make download-bbbike area=Amsterdam # download OSM data from bbbike.org and create config file + make import-data # Import data from OpenStreetMapData, Natural Earth and OSM Lake Labels. + make import-osm # Import OSM data with the mapping rules from build/mapping.yaml + make import-diff # Import OSM updates from data/changes.osc.gz + make import-wikidata # Import labels from Wikidata + make import-sql # Import layers (run this after modifying layer SQL) + +Hints for database management: + make psql # start PostgreSQL console + make psql-list-tables # list all PostgreSQL tables + make list-views # list PostgreSQL public schema views + make list-tables # list PostgreSQL public schema tables + make vacuum-db # PostgreSQL: VACUUM ANALYZE + make analyze-db # PostgreSQL: ANALYZE + make destroy-db # remove docker containers and PostgreSQL data volume + make start-db # start PostgreSQL, creating it if it doesn't exist + make start-db-preloaded # start PostgreSQL, creating data-prepopulated one if it doesn't exist + make stop-db # stop PostgreSQL database without destroying the data + +Hints for Docker management: + make clean-unnecessary-docker # clean unnecessary docker image(s) and container(s) + make refresh-docker-images # refresh openmaptiles docker images from Docker HUB + make remove-docker-images # remove openmaptiles docker images + make list-docker-images # show a list of available docker images +============================================================================== +``` diff --git a/README.md b/README.md index 0b96128..c4afc40 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,198 @@ -# vfosnar map tiles +## OpenMapTiles [![Build Status](https://github.com/openmaptiles/openmaptiles/workflows/OpenMapTiles%20Integrity%20CI/badge.svg?branch=master)](https://github.com/openmaptiles/openmaptiles/actions) -Fork of OpenMapTiles to generate data missing from standard OMT +OpenMapTiles is an extensible and open tile schema based on the OpenStreetMap. This project is used to generate vector tiles for online zoomable maps. OpenMapTiles is about creating a beautiful basemaps with general layers containing topographic information. More information [openmaptiles.org](https://openmaptiles.org/) and [maptiler.com/data/](https://www.maptiler.com/data/). + +We encourage you to collaborate, reuse and adapt existing layers, or add your own layers. You may use our approach for your own vector tile project. Feel free to fork the repo and experiment. The repository is built on top of the [openmaptiles/openmaptiles-tools](https://github.com/openmaptiles/openmaptiles-tools) to simplify vector tile creation. + +Please keep in mind that OpenMapTiles schema should display general topographic content. If creating a new layer or expanding an existing layer with a specific theme, please create a fork and invite other community members to cooperate on your topic. OpenMapTiles schema is used in many projects all over the world and the size of the final vector tiles needs to be considered in any update. + +- :link: Schema https://openmaptiles.org/schema +- :link: Docs https://openmaptiles.org/docs +- :link: Data for download: https://www.maptiler.com/data/ +- :link: Hosting https://www.maptiler.com/cloud/ +- :link: Create own layer https://github.com/openmaptiles/openmaptiles-skiing +- :link: Practical usage of OpenMapTiles https://github.com/maptiler/foss4g-workshop +- :link: Discuss at the #openmaptiles channel at [OSM Slack](https://slack.openstreetmap.us/) + +## Styles + +You can start from several GL styles supporting the OpenMapTiles vector schema. + +:link: [Learn how to create Mapbox GL styles with Maputnik and OpenMapTiles](http://openmaptiles.org/docs/style/maputnik/). + + +- [OSM OpenMapTiles](./style/README.md) +- [OSM Bright](https://github.com/openmaptiles/osm-bright-gl-style) +- [MapTiler Basic](https://github.com/openmaptiles/maptiler-basic-gl-style) +- [MapTiler 3D](https://github.com/openmaptiles/maptiler-3d-gl-style) +- [Fiord Color](https://github.com/openmaptiles/fiord-color-gl-style) +- [MapTiler Toner](https://github.com/openmaptiles/maptiler-toner-gl-style) +- [OSM Liberty](https://github.com/maputnik/osm-liberty) +- [Positron](https://github.com/openmaptiles/positron-gl-style) +- [Dark Matter](https://github.com/openmaptiles/dark-matter-gl-style) + +We also ported over our favorite old raster styles (TM2). + +:link: [Learn how to create TM2 styles with Mapbox Studio Classic and OpenMapTiles](http://openmaptiles.org/docs/style/mapbox-studio-classic/). + +- [Light](https://github.com/openmaptiles/mapbox-studio-light.tm2/) +- [Dark](https://github.com/openmaptiles/mapbox-studio-dark.tm2/) +- [OSM Bright](https://github.com/openmaptiles/mapbox-studio-osm-bright.tm2/) +- [Pencil](https://github.com/openmaptiles/mapbox-studio-pencil.tm2/) +- [Woodcut](https://github.com/openmaptiles/mapbox-studio-woodcut.tm2/) +- [Pirates](https://github.com/openmaptiles/mapbox-studio-pirates.tm2/) +- [Wheatpaste](https://github.com/openmaptiles/mapbox-studio-wheatpaste.tm2/) + +## Schema + +OpenMapTiles consists out of a collection of documented and self contained layers you can modify and adapt. +Together the layers make up the OpenMapTiles tileset. + +:link: [Study the vector tile schema](http://openmaptiles.org/schema) + +- [aeroway](https://openmaptiles.org/schema/#aeroway) +- [boundary](https://openmaptiles.org/schema/#boundary) +- [building](https://openmaptiles.org/schema/#building) +- [housenumber](https://openmaptiles.org/schema/#housenumber) +- [landcover](https://openmaptiles.org/schema/#landcover) +- [landuse](https://openmaptiles.org/schema/#landuse) +- [mountain_peak](https://openmaptiles.org/schema/#mountain_peak) +- [park](https://openmaptiles.org/schema/#park) +- [place](https://openmaptiles.org/schema/#place) +- [poi](https://openmaptiles.org/schema/#poi) +- [transportation](https://openmaptiles.org/schema/#transportation) +- [transportation_name](https://openmaptiles.org/schema/#transportation_name) +- [water](https://openmaptiles.org/schema/#water) +- [water_name](https://openmaptiles.org/schema/#water_name) +- [waterway](https://openmaptiles.org/schema/#waterway) + +## Develop + +To work on OpenMapTiles you need Docker. + +- Install [Docker](https://docs.docker.com/engine/installation/). Minimum version is 1.12.3+. +- Install [Docker Compose](https://docs.docker.com/compose/install/). Minimum version is 1.7.1+. + +### Microsoft Windows Subsystem for Linux (WSL) + +Please use Linux `/home/user/` directory, not Windows e.g. `/mnt/c` directory. + +### Build + +Build the tileset. + +```bash +git clone https://github.com/openmaptiles/openmaptiles.git +cd openmaptiles +# Build the imposm mapping, the tm2source project and collect all SQL scripts +make +``` + +You can execute the following manual steps (for better understanding) +or use the provided `quickstart.sh` script to automatically download and import given area. If area is not given, Albania will be imported. List of available areas `make list-geofabrik`. + +``` +./quickstart.sh +``` + +### Prepare the Database + +Now start up the database container. + +```bash +make start-db +``` + +Import external data from [OpenStreetMapData](http://osmdata.openstreetmap.de/), [Natural Earth](http://www.naturalearthdata.com/) and [OpenStreetMap Lake Labels](https://github.com/openmaptiles/osm-lakelines). Natural Earth country boundaries are used in the few lowest zoom levels. + +```bash +make import-data +``` + +Download OpenStreetMap data extracts from any source like [Geofabrik](http://download.geofabrik.de/), and store the PBF file in the `./data` directory. To use a specific download source, use `download-geofabrik`, `download-bbbike`, or `download-osmfr`, or use `download` to make it auto-pick the area. You can use `area=planet` for the entire OSM dataset (very large). Note that if you have more than one `data/*.osm.pbf` file, every `make` command will always require `area=...` parameter (or you can just `export area=...` first). + +```bash +make download area=albania +``` + +[Import OpenStreetMap data](https://github.com/openmaptiles/openmaptiles-tools/blob/master/bin/import-osm) with the mapping rules from +`build/mapping.yaml` (which has been created by `make`). Run after any change in layers definition (any change in `mapping.yaml`). + +```bash +make import-osm +``` + +Import labels from Wikidata. If an OSM feature has [Key:wikidata](https://wiki.openstreetmap.org/wiki/Key:wikidata), OpenMapTiles check corresponding item in Wikidata and use its [labels](https://www.wikidata.org/wiki/Help:Label) for languages listed in [openmaptiles.yaml](openmaptiles.yaml). So the generated vector tiles includes multi-languages in name field. + +This step uses [Wikidata Query Service](https://query.wikidata.org) to download just the Wikidata IDs that already exist in the database. + +```bash +make import-wikidata +``` + +### Work on Layers +Each time you modify a layer's `mapping.yaml` file or add new OSM tags, run `make` and `make import-osm` to recreate tables (potentially with additional data) in PostgreSQL. With the new data, there can be new Wikidata records also. +``` +make clean +make +make import-osm +make import-wikidata +``` + +Each time you modify layer SQL code run `make` and `make import-sql`. + +``` +make clean +make +make import-sql +``` + +Each time you make a modification that adds a new feature to vector tiles e.g. adding new OSM tags, modify the layer +style snippet by adding new style layer so the changes are propagated visually into the style. +All new style layers must have the `order` value which determines the order or rendering in the map style. +After the layer style snippet is modified run: +```bash +make build-style +``` + + + +Now you are ready to **generate the vector tiles**. By default, `./.env` specifies the entire planet BBOX for zooms 0-7, but running `generate-bbox-file` will analyze the data file and set the `BBOX` param to limit tile generation. + +``` +make generate-bbox-file # compute data bbox -- not needed for the whole planet or for downloaded area by `make download` +make generate-tiles-pg # generate tiles +``` + +### Workflow to generate tiles +If you go from top to bottom you can be sure that it will generate a .mbtiles file out of a .osm.pbf file +``` +make clean # clean / remove existing build files +make # generate build files +make start-db # start up the database container. +make import-data # Import external data from OpenStreetMapData, Natural Earth and OpenStreetMap Lake Labels. +make download area=albania # download albania .osm.pbf file -- can be skipped if a .osm.pbf file already existing +make import-osm # import data into postgres +make import-wikidata # import Wikidata +make import-sql # create / import sql functions +make generate-bbox-file # compute data bbox -- not needed for the whole planet or for downloaded area by `make download` +make generate-tiles-pg # generate tiles +``` +Instead of calling `make download area=albania` you can add a .osm.pbf file in the `data` folder `openmaptiles/data/your_area_file.osm.pbf` + +To change the name of the output filename, you can modify the variable `MBTILES_FILE` in the `.env` file or set up the environment variable `MBTILES_FILE` before running `./quickstart.sh` or `make generate-tiles-pg` (e.g., `MBTILES_FILENAME=monaco.mbtiles ./quickstart.sh monaco`). + + +## License + +All code in this repository is under the [BSD license](./LICENSE.md). Design and the cartography decisions encoded in the schema and SQL are licensed under [CC-BY](./LICENSE.md). + +Products or services using maps derived from OpenMapTiles schema need to **visibly credit "OpenMapTiles.org"** or **reference "OpenMapTiles"** with a link to https://openmaptiles.org/. Exceptions to attribution requirement can be granted on request. + +For a browsable electronic map based on OpenMapTiles and OpenStreetMap data, the +credit should appear in the corner of the map. For example: + +[© OpenMapTiles](https://openmaptiles.org/) [© OpenStreetMap contributors](https://www.openstreetmap.org/copyright) + +For printed and static maps a similar attribution should be made in a textual +description near the image, in the same fashion as if you cite a photograph. diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..f77c603 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,18 @@ + +# OpenMapTiles SQL Testing + +The OpenMapTiles SQL tests ensure that OSM data is properly imported and updated in the OpenMapTiles data schema. The tests work by injecting test OSM data into the database and checking to ensure that the data is properly reflected in the SQL output. + +Usage: + +`make clean && make test-sql` + +## How it works + +The SQL tests consist of the following parts: + + 1. **Test import data**, located in `tests/import`. This test data is in the [OSM XML](https://wiki.openstreetmap.org/wiki/OSM_XML) format and contains the data that should be initially injected into the database. The files are numbered in order to ensure that each test data file OSM id numbers that are unique from the other files. For example, the file starting with `100` will use node ids from 100000-199999, way ids from 1000-1999, and relation ids from 100-199. + 2. **Test update data**, located in `tests/update`. This test data is in the [osmChange XML](https://wiki.openstreetmap.org/wiki/OsmChange) format, and contains the data that will be used to update the test import data (in order to verify that the update process is working correctly. These files are also numbered using the same scheme as the test import data. + 3. **Import SQL test script**, located at `tests/test-post-import.sql`. This script is executed after the test import data has been injected, and runs SQL-based checks to ensure that the import data was properly imported. If there are failures in the tests, an entry will be added to the table `omt_test_failures`, with one record per error that occurs during the import process. A test failure will also fail the build. To inspect the test failure messages, run `make psql` and issue the comment `SELECT * FROM omt_test_failures`. + 4. **Update SQL test script**, located at `tests/test-post-update.sql`. This script performs the same function as the import test script, except that it occurs after the test update data has been applied to the database. Note that script will only run if the import script passes all tests. + diff --git a/UPDATE.md b/UPDATE.md new file mode 100644 index 0000000..7b29c1b --- /dev/null +++ b/UPDATE.md @@ -0,0 +1,116 @@ +# Keeping the Vector Tiles Updated + +Once you have imported OpenMapTiles you can also keep it up to date by importing the latest OSM changes and +regenerating the tables. + +## Import + +You can either keep the database up to date based on the daily (or minutely) OSM change feed +or import specific change files. + +### Choosing the Download Source + +While GeoFabrik currently provides extracts of basically all countries, they provide only daily updates. +If you need minutely updates you might want to try openstreetmap.fr, for example like this: `make download-osmfr area=africa/eritrea`, which configures minutely updates. + +### Preparations + +If you plan to keep data updated automatically, before importing any data, make sure to set + +``` +DIFF_MODE=true +``` + +in the `.env` + +Now download fresh data: + +``` +make download area=your-area-of-choice +``` + +### Keep Database Updated + +You can use imposm3 to keep the database updated (thanks to the [work by @stirringhalo](https://github.com/openmaptiles/openmaptiles/pull/131)). +This will repeatedly download the OSM change feed and import it into the database. +In order to be able to update the database, the initial download and import of the OSM data must be done when `DIFF_MODE=true` is set in the `.env` file. +In this mode the initial download also sets the update source and the update intervals. + +To start the update process please use +``` +make start-update-osm +``` + +To stop the update process please use +``` +make stop-update-osm +``` + +After each update activation, **imposm3** will store lists of updated tiles in text format in subfolders of the `diffdir`, +named for the date(s) on which the import took place (`YYYYMMDD`). + +See [Generate Changed Tiles](#generate-changed-tiles) below on how this file can be used. + +#### Note +When the update process is actively updating the DB it is impossible to successfully generate tiles, +as there will be conflicts and deadlocks related to the DB access. + +Unfortunately, there is no known way to execute an external command in-between rounds of the `update-osm` process. + +#### Troubleshooting + +The log file for osm update can be viewed using + +``` +docker-compose logs --tail 100 --follow update-osm +``` + +Use `Ctrl-C` to stop following the log. + +The output will be similar to this: + +``` +[info] Importing #4889572 including changes till ....... +0000 UTC (2h10m10s behind) +``` + +It might take some time to catch up with the latest changes, but the "time behind" should decrease until it is a few minutes. +If it doesn't, you need to download a new extract or check that there are enough system resources to keep-up with the changes. + +Finally you will get an output like this - this indicates, that some 6 objects were changed: + +``` +[progress] 3s C: 0/s (0) N: 0/s (0) W: 0/s (6) R: 0/s (0) +``` + +The process will keep running foreverprint something like this - which just means that no changes were in the latest changeset: + +``` +[progress] 0s C: 0/s (0) N: 0/s (0) W: 0/s (0) R: 0/s (0) +``` + +### Import Change File + +You may perform a one-time import of OSM changes from the `changes.osc.gz` file in your import folder using + +``` +make import-diff +``` + +Similar to[Keep Database Updated](#keep_database_updated) above, **imposm3** will store the list of updated tiles in text file in subfolders of the `diffdir`, +named for the date on which the import took place (`YYYYMMDD`). + +See [Generate Changed Tiles](#generate-changed-tiles) below. + +#### Note +There is no `make` command for downloading OSM changes into `changes.osc.gz`. +You may perform this task using [`osmupdate`](https://wiki.openstreetmap.org/wiki/Osmupdate), +[pyosmium-get-changes](https://docs.osmcode.org/pyosmium/latest/tools_get_changes.html), +or downloading the changefile directly from the replication server. + +## Generate Changed Tiles + +To generate all changed tiles, based on the lists of all updated tiles, and update the existing MBtiles file, please use + +``` +make generate-changed-tiles +``` diff --git a/docker-compose.yml b/docker-compose.yml index 49c27b5..58ca15c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,5 @@ # This version must match the MAKE_DC_VERSION value below -version: "3" +# version: "3" volumes: pgdata: @@ -9,7 +9,6 @@ networks: driver: bridge services: - postgres: image: "${POSTGIS_IMAGE:-openmaptiles/postgis}:${TOOLS_VERSION}" # Use "command: postgres -c jit=off" for PostgreSQL 11+ because of slow large MVT query processing diff --git a/layers/aerodrome_label/README.md b/layers/aerodrome_label/README.md new file mode 100644 index 0000000..b9f7903 --- /dev/null +++ b/layers/aerodrome_label/README.md @@ -0,0 +1,7 @@ +## Aerodrome Labels + +### Mapping Diagram +![Mapping diagram for aerodrome labels](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for aerodrome labels](etl_diagram.png?raw=true) diff --git a/layers/aerodrome_label/aerodrome_label.sql b/layers/aerodrome_label/aerodrome_label.sql new file mode 100644 index 0000000..b3c3625 --- /dev/null +++ b/layers/aerodrome_label/aerodrome_label.sql @@ -0,0 +1,62 @@ + +-- etldoc: layer_aerodrome_label[shape=record fillcolor=lightpink, style="rounded,filled", label="layer_aerodrome_label | z8 | z9 | z10+" ] ; + +CREATE OR REPLACE FUNCTION layer_aerodrome_label(bbox geometry, + zoom_level integer) + RETURNS TABLE + ( + id bigint, + geometry geometry, + name text, + name_en text, + name_de text, + tags hstore, + class text, + iata text, + icao text, + ele int, + ele_ft int + ) +AS +$$ +SELECT + -- etldoc: osm_aerodrome_label_point -> layer_aerodrome_label:z8 + -- etldoc: osm_aerodrome_label_point -> layer_aerodrome_label:z9 + ABS(osm_id) AS id, -- mvt feature IDs can't be negative + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + aerodrome_type AS class, + NULLIF(iata, '') AS iata, + NULLIF(icao, '') AS icao, + substring(ele FROM E'^(-?\\d+)(\\D|$)')::int AS ele, + round(substring(ele FROM E'^(-?\\d+)(\\D|$)')::int * 3.2808399)::int AS ele_ft +FROM osm_aerodrome_label_point +WHERE geometry && bbox + AND aerodrome_type = 'international' + AND iata <> '' + AND zoom_level BETWEEN 8 AND 9 + +UNION ALL + +SELECT + -- etldoc: osm_aerodrome_label_point -> layer_aerodrome_label:z10_ + ABS(osm_id) AS id, -- mvt feature IDs can't be negative + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + aerodrome_type AS class, + NULLIF(iata, '') AS iata, + NULLIF(icao, '') AS icao, + substring(ele FROM E'^(-?\\d+)(\\D|$)')::int AS ele, + round(substring(ele FROM E'^(-?\\d+)(\\D|$)')::int * 3.2808399)::int AS ele_ft +FROM osm_aerodrome_label_point +WHERE geometry && bbox + AND zoom_level >= 10; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/aerodrome_label/aerodrome_label.yaml b/layers/aerodrome_label/aerodrome_label.yaml new file mode 100644 index 0000000..47893d4 --- /dev/null +++ b/layers/aerodrome_label/aerodrome_label.yaml @@ -0,0 +1,50 @@ +layer: + id: aerodrome_label + description: | + [Aerodrome labels](http://wiki.openstreetmap.org/wiki/Tag:aeroway%3Daerodrome) + buffer_size: 64 + srs: +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over + fields: + name: The OSM [`name`](http://wiki.openstreetmap.org/wiki/Key:name) value of the aerodrome. Language-specific values are in `name:xx`. + name_en: English name `name:en` if available, otherwise `name`. This is deprecated and will be removed in a future release in favor of `name:en`. + name_de: German name `name:de` if available, otherwise `name` or `name:en`. This is deprecated and will be removed in a future release in favor of `name:de`. + class: + description: | + Distinguish between more and less important aerodromes. + Class is derived from the value of + [`aerodrome`](http://wiki.openstreetmap.org/wiki/Proposed_features/Aerodrome) + and `aerodrome:type` tags. + values: + international: + aerodrome: 'international' + aerodrome_type: 'international' + public: + aerodrome: 'public' + aerodrome_type: ['%public%', 'civil'] + regional: + aerodrome: 'regional' + aerodrome_type: 'regional' + military: + aerodrome: 'military' + aerodrome_type: '%military%' + military: 'airfield' + private: + aerodrome: 'private' + aerodrome_type: 'private' + other: + iata: 3-character code issued by the IATA. + icao: 4-letter code issued by the ICAO. + ele: Elevation (`ele`) in meters. + ele_ft: Elevation (`ele`) in feets. + datasource: + geometry_field: geometry + key_field: id + key_field_as_attribute: no + srid: 900913 + query: (SELECT id, geometry, name, name_en, name_de, {name_languages}, class, iata, icao, ele, ele_ft FROM layer_aerodrome_label(!bbox!, z(!scale_denominator!))) AS t +schema: + - ./update_aerodrome_label_point.sql + - ./aerodrome_label.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/aerodrome_label/etl_diagram.png b/layers/aerodrome_label/etl_diagram.png new file mode 100644 index 0000000..c659ab4 Binary files /dev/null and b/layers/aerodrome_label/etl_diagram.png differ diff --git a/layers/aerodrome_label/mapping.yaml b/layers/aerodrome_label/mapping.yaml new file mode 100644 index 0000000..ff0f22c --- /dev/null +++ b/layers/aerodrome_label/mapping.yaml @@ -0,0 +1,46 @@ +tables: + + # etldoc: imposm3 -> osm_aerodrome_label_point + aerodrome_label_point: + type: geometry + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - name: name + key: name + type: string + - name: name_en + key: name:en + type: string + - name: name_de + key: name:de + type: string + - name: tags + type: hstore_tags + - name: aerodrome_type + key: aerodrome:type + type: string + - name: aerodrome + key: aerodrome + type: string + - name: military + key: military + type: string + - name: iata + key: iata + type: string + - name: icao + key: icao + type: string + - name: ele + key: ele + type: string + type_mappings: + points: + aeroway: + - aerodrome + polygons: + aeroway: + - aerodrome diff --git a/layers/aerodrome_label/mapping_diagram.png b/layers/aerodrome_label/mapping_diagram.png new file mode 100644 index 0000000..7596a7c Binary files /dev/null and b/layers/aerodrome_label/mapping_diagram.png differ diff --git a/layers/aerodrome_label/style.json b/layers/aerodrome_label/style.json new file mode 100644 index 0000000..f75f84a --- /dev/null +++ b/layers/aerodrome_label/style.json @@ -0,0 +1,69 @@ +{ + "layers": [ + { + "id": "airport-label-major", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "aerodrome_label", + "minzoom": 8, + "maxzoom": 17, + "layout": { + "icon-size": 1, + "text-font": [ + "Noto Sans Italic" + ], + "text-size": { + "stops": [ + [ + 8, + 10 + ], + [ + 14, + 12 + ] + ] + }, + "icon-image": "aerodrome.12", + "text-field": { + "stops": [ + [ + 8, + " " + ], + [ + 11, + "{name:latin}\n{name:nonlatin}" + ] + ] + }, + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 0.6 + ], + "text-padding": 2, + "text-optional": true, + "symbol-z-order": "auto", + "text-max-width": 9, + "icon-allow-overlap": false, + "text-allow-overlap": false + }, + "paint": { + "text-color": "#5e3b9e", + "text-halo-blur": 0.5, + "text-halo-color": "rgba(255, 255, 255, 0.8)", + "text-halo-width": 1 + }, + "filter": [ + "all", + [ + "has", + "iata" + ] + ], + "order": 190 + } + ] +} \ No newline at end of file diff --git a/layers/aerodrome_label/update_aerodrome_label_point.sql b/layers/aerodrome_label/update_aerodrome_label_point.sql new file mode 100644 index 0000000..c5d675a --- /dev/null +++ b/layers/aerodrome_label/update_aerodrome_label_point.sql @@ -0,0 +1,109 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_aerodrome_label_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_aerodrome_label_point; +DROP TRIGGER IF EXISTS trigger_refresh ON aerodrome_label.updates; + +-- Partial index for zoom 8/9 queries +CREATE INDEX IF NOT EXISTS osm_aerodrome_label_point_type_partial_idx + ON osm_aerodrome_label_point USING gist (geometry) + WHERE aerodrome_type = 'international' + AND iata <> ''; + +CREATE SCHEMA IF NOT EXISTS aerodrome_label; + +CREATE TABLE IF NOT EXISTS aerodrome_label.osm_ids +( + osm_id bigint PRIMARY KEY +); + +-- etldoc: osm_aerodrome_label_point -> osm_aerodrome_label_point +CREATE OR REPLACE FUNCTION update_aerodrome_label_point(full_update boolean) RETURNS void AS +$$ + UPDATE osm_aerodrome_label_point + SET geometry = ST_Centroid(geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM aerodrome_label.osm_ids)) + AND ST_GeometryType(geometry) <> 'ST_Point'; + + UPDATE osm_aerodrome_label_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM aerodrome_label.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); + + UPDATE osm_aerodrome_label_point + SET aerodrome_type= + CASE + %%FIELD_MAPPING: class %% + ELSE 'other' END + WHERE (full_update OR osm_id IN (SELECT osm_id FROM aerodrome_label.osm_ids)) + AND aerodrome_type != + CASE + %%FIELD_MAPPING: class %% + ELSE 'other' END; +$$ LANGUAGE SQL; + +SELECT update_aerodrome_label_point(true); + +-- Handle updates + +CREATE OR REPLACE FUNCTION aerodrome_label.store() RETURNS trigger AS +$$ +BEGIN + INSERT INTO aerodrome_label.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS aerodrome_label.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION aerodrome_label.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO aerodrome_label.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION aerodrome_label.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh aerodrome_label'; + + -- Analyze tracking and source tables before performing update + ANALYZE aerodrome_label.osm_ids; + ANALYZE osm_aerodrome_label_point; + + PERFORM update_aerodrome_label_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM aerodrome_label.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM aerodrome_label.updates; + + RAISE LOG 'Refresh aerodrome_label done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE + ON osm_aerodrome_label_point + FOR EACH ROW +EXECUTE PROCEDURE aerodrome_label.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE + ON osm_aerodrome_label_point + FOR EACH STATEMENT +EXECUTE PROCEDURE aerodrome_label.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON aerodrome_label.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE aerodrome_label.refresh(); diff --git a/layers/aeroway/README.md b/layers/aeroway/README.md new file mode 100644 index 0000000..9dd0536 --- /dev/null +++ b/layers/aeroway/README.md @@ -0,0 +1,10 @@ +## aeroway + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#aeroway** + +### Mapping Diagram +![Mapping diagram for aeroway](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for aeroway](etl_diagram.png?raw=true) diff --git a/layers/aeroway/aeroway.sql b/layers/aeroway/aeroway.sql new file mode 100644 index 0000000..0c3952a --- /dev/null +++ b/layers/aeroway/aeroway.sql @@ -0,0 +1,70 @@ +-- etldoc: layer_aeroway[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_aeroway | z10| z11| z12| z13| z14+" ]; + +CREATE OR REPLACE FUNCTION layer_aeroway(bbox geometry, zoom_level int) + RETURNS TABLE + ( + geometry geometry, + class text, + ref text + ) +AS +$$ +SELECT geometry, aeroway AS class, ref +FROM ( + -- etldoc: osm_aeroway_linestring_gen_z10 -> layer_aeroway:z10 + SELECT geometry, aeroway, ref + FROM osm_aeroway_linestring_gen_z10 + WHERE zoom_level = 10 + UNION ALL + -- etldoc: osm_aeroway_linestring_gen_z11 -> layer_aeroway:z11 + SELECT geometry, aeroway, ref + FROM osm_aeroway_linestring_gen_z11 + WHERE zoom_level = 11 + UNION ALL + -- etldoc: osm_aeroway_linestring_gen_z12 -> layer_aeroway:z12 + SELECT geometry, aeroway, ref + FROM osm_aeroway_linestring_gen_z12 + WHERE zoom_level = 12 + UNION ALL + -- etldoc: osm_aeroway_linestring -> layer_aeroway:z13 + -- etldoc: osm_aeroway_linestring -> layer_aeroway:z14_ + SELECT geometry, aeroway, ref + FROM osm_aeroway_linestring + WHERE zoom_level >= 13 + UNION ALL + -- etldoc: osm_aeroway_polygon_gen_z10 -> layer_aeroway:z10 + SELECT geometry, aeroway, ref + FROM osm_aeroway_polygon_gen_z10 + WHERE zoom_level = 10 + UNION ALL + -- etldoc: osm_aeroway_polygon_gen_z11 -> layer_aeroway:z11 + SELECT geometry, aeroway, ref + FROM osm_aeroway_polygon_gen_z11 + WHERE zoom_level = 11 + UNION ALL + -- etldoc: osm_aeroway_polygon_gen_z12 -> layer_aeroway:z12 + SELECT geometry, aeroway, ref + FROM osm_aeroway_polygon_gen_z12 + WHERE zoom_level = 12 + UNION ALL + -- etldoc: osm_aeroway_polygon_gen_z13 -> layer_aeroway:z13 + SELECT geometry, aeroway, ref + FROM osm_aeroway_polygon_gen_z13 + WHERE zoom_level = 13 + UNION ALL + -- etldoc: osm_aeroway_polygon -> layer_aeroway:z14_ + SELECT geometry, aeroway, ref + FROM osm_aeroway_polygon + WHERE zoom_level >= 14 + UNION ALL + + -- etldoc: osm_aeroway_point -> layer_aeroway:z14_ + SELECT geometry, aeroway, ref + FROM osm_aeroway_point + WHERE zoom_level >= 14 + ) AS zoom_levels +WHERE geometry && bbox; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/aeroway/aeroway.yaml b/layers/aeroway/aeroway.yaml new file mode 100644 index 0000000..51cc2a7 --- /dev/null +++ b/layers/aeroway/aeroway.yaml @@ -0,0 +1,30 @@ +layer: + id: "aeroway" + description: | + Aeroway polygons based of OpenStreetMap [aeroways](http://wiki.openstreetmap.org/wiki/Aeroways). + Airport buildings are contained in the **building** layer but all + other airport related polygons can be found in the **aeroway** layer. + buffer_size: 4 + fields: + ref: The OSM [`ref`](http://wiki.openstreetmap.org/wiki/Key:ref) tag of the runway/taxiway. + class: + description: | + The original value of + [`aeroway`](http://wiki.openstreetmap.org/wiki/Key:aeroway) or + `area:aeroway` tag. + values: + - aerodrome + - heliport + - runway + - helipad + - taxiway + - apron + - gate + datasource: + geometry_field: geometry + query: (SELECT geometry, ref, class FROM layer_aeroway(!bbox!, z(!scale_denominator!))) AS t +schema: + - ./aeroway.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/aeroway/etl_diagram.png b/layers/aeroway/etl_diagram.png new file mode 100644 index 0000000..20e7622 Binary files /dev/null and b/layers/aeroway/etl_diagram.png differ diff --git a/layers/aeroway/mapping.yaml b/layers/aeroway/mapping.yaml new file mode 100644 index 0000000..6e97819 --- /dev/null +++ b/layers/aeroway/mapping.yaml @@ -0,0 +1,104 @@ +generalized_tables: + # etldoc: osm_aeroway_linestring_gen_z11 -> osm_aeroway_linestring_gen_z10 + aeroway_linestring_gen_z10: + source: aeroway_linestring_gen_z11 + tolerance: ZRES11 + + # etldoc: osm_aeroway_linestring_gen_z12 -> osm_aeroway_linestring_gen_z11 + aeroway_linestring_gen_z11: + source: aeroway_linestring_gen_z12 + tolerance: ZRES12 + + # etldoc: osm_aeroway_linestring -> osm_aeroway_linestring_gen_z12 + aeroway_linestring_gen_z12: + source: aeroway_linestring + sql_filter: ST_IsValid(geometry) + tolerance: ZRES13 + + # etldoc: osm_aeroway_polygon_gen_z11 -> osm_aeroway_polygon_gen_z10 + aeroway_polygon_gen_z10: + source: aeroway_polygon_gen_z11 + sql_filter: area>power(ZRES9,2) + tolerance: ZRES10 + + # etldoc: osm_aeroway_polygon_gen_z12 -> osm_aeroway_polygon_gen_z11 + aeroway_polygon_gen_z11: + source: aeroway_polygon_gen_z12 + sql_filter: area>power(ZRES10,2) + tolerance: ZRES11 + + # etldoc: osm_aeroway_polygon_gen_z13 -> osm_aeroway_polygon_gen_z12 + aeroway_polygon_gen_z12: + source: aeroway_polygon_gen_z13 + sql_filter: area>power(ZRES11,2) + tolerance: ZRES12 + + # etldoc: osm_aeroway_polygon -> osm_aeroway_polygon_gen_z13 + aeroway_polygon_gen_z13: + source: aeroway_polygon + sql_filter: area>power(ZRES12,2) AND ST_IsValid(geometry) + tolerance: ZRES13 + +ref_field: &ref + key: ref + name: ref + type: string + +def_aeroway_polygon_mapping: &aeroway_polygon_mapping + - aerodrome + - heliport + - runway + - helipad + - taxiway + - apron + +tables: + # etldoc: imposm3 -> osm_aeroway_polygon + aeroway_polygon: + type: polygon + columns: + - *ref + - name: osm_id + type: id + - name: geometry + type: geometry + - name: aeroway + type: mapping_value + - name: area + type: area + mapping: + aeroway: *aeroway_polygon_mapping + "area:aeroway": *aeroway_polygon_mapping + + # etldoc: imposm3 -> osm_aeroway_linestring + aeroway_linestring: + type: linestring + columns: + - *ref + - name: osm_id + type: id + - name: geometry + type: geometry + - name: aeroway + key: aeroway + type: string + mapping: + aeroway: + - runway + - taxiway + + # etldoc: imposm3 -> osm_aeroway_point + aeroway_point: + type: point + columns: + - *ref + - name: osm_id + type: id + - name: geometry + type: geometry + - name: aeroway + key: aeroway + type: string + mapping: + aeroway: + - gate \ No newline at end of file diff --git a/layers/aeroway/mapping_diagram.png b/layers/aeroway/mapping_diagram.png new file mode 100644 index 0000000..7536f47 Binary files /dev/null and b/layers/aeroway/mapping_diagram.png differ diff --git a/layers/aeroway/style.json b/layers/aeroway/style.json new file mode 100644 index 0000000..c42ca49 --- /dev/null +++ b/layers/aeroway/style.json @@ -0,0 +1,203 @@ +{ + "layers": [ + { + "id": "aeroway_fill", + "type": "fill", + "source": "openmaptiles", + "source-layer": "aeroway", + "minzoom": 11, + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": { + "stops": [ + [ + 6, + "rgba(223, 223, 228, 1)" + ], + [ + 12, + "rgba(232, 231, 223, 1)" + ] + ] + }, + "fill-opacity": 1 + }, + "metadata": {}, + "filter": [ + "==", + "$type", + "Polygon" + ], + "order": 3 + }, + { + "id": "aeroway_runway", + "type": "line", + "source": "openmaptiles", + "source-layer": "aeroway", + "minzoom": 11, + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(178, 181, 209, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 3 + ], + [ + 20, + 48 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "class", + "runway" + ] + ], + "order": 22 + }, + { + "id": "aeroway_taxiway", + "type": "line", + "source": "openmaptiles", + "source-layer": "aeroway", + "minzoom": 11, + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(178, 181, 209, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 1 + ], + [ + 20, + 24 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "class", + "taxiway" + ] + ], + "order": 23 + }, + { + "id": "airport_label", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "aeroway", + "minzoom": 14, + "layout": { + "text-font": [ + "Noto Sans Italic", + "Noto Sans Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 9 + ], + [ + 19, + 15 + ] + ] + }, + "text-field": "{ref}", + "visibility": "visible", + "symbol-placement": "line" + }, + "paint": { + "text-color": "#333333", + "text-halo-color": "rgba(255, 255, 255, 0.8)", + "text-halo-width": 1 + }, + "filter": [ + "all", + [ + "in", + "class", + "runway", + "taxiway" + ] + ], + "order": 191 + }, + { + "id": "airport_gate", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "aeroway", + "minzoom": 16.5, + "layout": { + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 9 + ], + [ + 19, + 15 + ] + ] + }, + "text-field": "{ref}", + "visibility": "visible" + }, + "paint": { + "text-color": "rgba(135, 135, 135, 1)", + "text-halo-color": "rgba(255, 255, 255, 1)", + "text-halo-width": 1 + }, + "filter": [ + "all", + [ + "==", + "class", + "gate" + ] + ], + "order": 192 + } + ] +} \ No newline at end of file diff --git a/layers/barrier/barrier.sql b/layers/barrier/barrier.sql new file mode 100644 index 0000000..1f732fc --- /dev/null +++ b/layers/barrier/barrier.sql @@ -0,0 +1,6 @@ +CREATE OR REPLACE FUNCTION layer_barrier(bbox geometry, zoom_level int) +RETURNS TABLE(geometry geometry, subclass text) AS $$ + SELECT geometry, subclass + FROM osm_barrier_linestring + WHERE zoom_level >= 14 AND geometry && bbox; +$$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/kct/kct.yaml b/layers/barrier/barrier.yaml similarity index 63% rename from layers/kct/kct.yaml rename to layers/barrier/barrier.yaml index efed453..c9071ae 100644 --- a/layers/kct/kct.yaml +++ b/layers/barrier/barrier.yaml @@ -1,17 +1,17 @@ layer: - id: "kct" - description: kct + id: "barrier" + description: | + Barriers buffer_size: 4 srs: +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over fields: - name: - waycolor: + subclass: datasource: geometry_field: geometry srid: 900913 - query: (SELECT geometry, name, waycolor FROM layer_kct(!bbox!, z(!scale_denominator!))) AS t + query: (SELECT geometry, subclass FROM layer_barrier(!bbox!, z(!scale_denominator!))) AS t schema: - - ./layer.sql + - ./barrier.sql datasources: - type: imposm3 mapping_file: ./mapping.yaml diff --git a/layers/barrier/mapping.yaml b/layers/barrier/mapping.yaml new file mode 100644 index 0000000..309df14 --- /dev/null +++ b/layers/barrier/mapping.yaml @@ -0,0 +1,13 @@ +tables: + # etldoc: imposm3 -> osm_barrier_linestring + barrier_linestring: + type: linestring + columns: + - name: geometry + type: geometry + - name: subclass + type: mapping_value + mapping: + barrier: + - fence + - hedge diff --git a/layers/barrier/style.json b/layers/barrier/style.json new file mode 100644 index 0000000..fd37b08 --- /dev/null +++ b/layers/barrier/style.json @@ -0,0 +1,3 @@ +{ + "layers": [] +} \ No newline at end of file diff --git a/layers/boundary/README.md b/layers/boundary/README.md new file mode 100644 index 0000000..9fd7902 --- /dev/null +++ b/layers/boundary/README.md @@ -0,0 +1,7 @@ +## boundary + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#boundary** + +### ETL diagram +![ETL diagram for boundary](etl_diagram.png?raw=true) diff --git a/layers/boundary/boundary.sql b/layers/boundary/boundary.sql new file mode 100644 index 0000000..c477a99 --- /dev/null +++ b/layers/boundary/boundary.sql @@ -0,0 +1,858 @@ +-- etldoc: osm_border_linestring -> osm_border_linestring_gen_z13 +-- etldoc: osm_border_linestring_adm -> osm_border_linestring_gen_z13 +-- etldoc: osm_border_disp_linestring -> osm_border_linestring_gen_z13 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z13 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z13 AS +( +SELECT ST_Simplify(ST_Collect(geometry), ZRes(14)) AS geometry, + MAX(adm0_l) AS adm0_l, + MAX(adm0_r) AS adm0_r, + MIN(admin_level) AS admin_level, + BOOL_OR(disputed) AS disputed, + MAX(name) AS name, + MAX(claimed_by) AS claimed_by, + BOOL_OR(maritime) AS maritime +FROM ( + -- All admin 3-10 boundaries + SELECT osm_id, + geometry, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + MIN(admin_level) AS admin_level, + BOOL_OR(disputed) + OR BOOL_OR(dispute) + OR BOOL_OR(border_status = 'disputed') + OR BOOL_OR(disputed_by <> '') AS disputed, + NULLIF(name, '') AS name, + NULLIF(claimed_by, '') AS claimed_by, + BOOL_OR(maritime) AS maritime + FROM osm_border_linestring + WHERE admin_level BETWEEN 3 AND 10 + AND type = 1 -- ways only + GROUP BY osm_id, geometry, name, claimed_by + + UNION ALL + + -- All non-disputed admin 2 boundaries + SELECT osm_id, + geometry, + adm0_l, + adm0_r, + admin_level, + FALSE AS disputed, + NULL::text AS name, + NULL::text AS claimed_by, + maritime + FROM osm_border_linestring_adm + + UNION ALL + + -- All disputed admin 2 boundaries + SELECT osm_id, + geometry, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + 2::int AS admin_level, + TRUE AS disputed, + NULLIF(name, '') AS name, + NULLIF(claimed_by, '') AS claimed_by, + maritime + FROM osm_border_disp_linestring + GROUP BY osm_id, geometry, name, claimed_by, maritime + ) AS merged_boundary +GROUP by osm_id +)/* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z13_idx ON osm_border_linestring_gen_z13 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z13 -> osm_border_linestring_gen_z12 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z12 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z12 AS +( +SELECT ST_Simplify(geometry, ZRes(13)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z13 +WHERE admin_level BETWEEN 2 AND 10 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z12_idx ON osm_border_linestring_gen_z12 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z12 -> osm_border_linestring_gen_z11 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z11 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z11 AS +( +SELECT ST_Simplify(geometry, ZRes(12)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z12 +WHERE admin_level BETWEEN 2 AND 8 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z11_idx ON osm_border_linestring_gen_z11 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z11 -> osm_border_linestring_gen_z10 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z10 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z10 AS +( +SELECT ST_Simplify(geometry, ZRes(11)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z11 +WHERE admin_level BETWEEN 2 AND 6 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z10_idx ON osm_border_linestring_gen_z10 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z10 -> osm_border_linestring_gen_z9 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z9 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z9 AS +( +SELECT ST_Simplify(geometry, ZRes(10)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z10 +-- WHERE admin_level BETWEEN 2 AND 6 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z9_idx ON osm_border_linestring_gen_z9 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z9 -> osm_border_linestring_gen_z8 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z8 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z8 AS +( +SELECT ST_Simplify(geometry, ZRes(9)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z9 +WHERE admin_level BETWEEN 2 AND 4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z8_idx ON osm_border_linestring_gen_z8 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z8 -> osm_border_linestring_gen_z7 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z7 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z7 AS +( +SELECT ST_Simplify(geometry, ZRes(8)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z8 +-- WHERE admin_level BETWEEN 2 AND 4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z7_idx ON osm_border_linestring_gen_z7 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z7 -> osm_border_linestring_gen_z6 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z6 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z6 AS +( +SELECT ST_Simplify(geometry, ZRes(7)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z7 +-- WHERE admin_level BETWEEN 2 AND 4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z6_idx ON osm_border_linestring_gen_z6 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z6 -> osm_border_linestring_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z5 AS +( +SELECT ST_Simplify(geometry, ZRes(6)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z6 +-- WHERE admin_level BETWEEN 2 AND 4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z5_idx ON osm_border_linestring_gen_z5 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z5 -> osm_border_linestring_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z4 AS +( +SELECT ST_Simplify(geometry, ZRes(5)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z5 +WHERE admin_level = 2 AND maritime + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z4_idx ON osm_border_linestring_gen_z4 USING gist (geometry); + +-- ne_10m_admin_0_boundary_lines_land +-- etldoc: ne_10m_admin_0_boundary_lines_land -> ne_10m_admin_0_boundary_lines_land_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_admin_0_boundary_lines_land_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_admin_0_boundary_lines_land_gen_z4 AS +( +SELECT ST_Simplify(geometry, ZRes(6)) as geometry, + 2 AS admin_level, + (CASE WHEN featurecla LIKE 'Disputed%' THEN TRUE ELSE FALSE END) AS disputed, + NULL::text AS disputed_name, + NULL::text AS claimed_by, + FALSE AS maritime +FROM ne_10m_admin_0_boundary_lines_land +WHERE featurecla <> 'Lease limit' + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_admin_0_boundary_lines_land_gen_z4_idx ON ne_10m_admin_0_boundary_lines_land_gen_z4 USING gist (geometry); + +-- etldoc: ne_10m_admin_0_boundary_lines_land -> ne_10m_admin_0_boundary_lines_land_disputed +DROP MATERIALIZED VIEW IF EXISTS ne_10m_admin_0_boundary_lines_land_disputed CASCADE; +CREATE MATERIALIZED VIEW ne_10m_admin_0_boundary_lines_land_disputed AS +( +SELECT geometry, + 2 AS admin_level, + (CASE WHEN featurecla LIKE 'Disputed%' THEN TRUE ELSE FALSE END) AS disputed, + NULL::text AS disputed_name, + NULL::text AS claimed_by, + FALSE AS maritime +FROM ne_10m_admin_0_boundary_lines_land +WHERE featurecla LIKE 'Disputed%' AND adm0_left = 'South Sudan' AND adm0_right = 'Kenya' + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_admin_0_boundary_lines_land_disputed_idx ON ne_10m_admin_0_boundary_lines_land_disputed USING gist (geometry); + +-- ne_10m_admin_1_states_provinces_lines +-- etldoc: ne_10m_admin_1_states_provinces_lines -> ne_10m_admin_1_states_provinces_lines_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_admin_1_states_provinces_lines_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_admin_1_states_provinces_lines_gen_z4 AS +( +SELECT ST_Simplify(geometry, ZRes(6)) as geometry, + 4 AS admin_level, + FALSE AS disputed, + NULL::text AS disputed_name, + NULL::text AS claimed_by, + FALSE AS maritime, + min_zoom +FROM ne_10m_admin_1_states_provinces_lines +WHERE min_zoom <= 7.7 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_admin_1_states_provinces_lines_gen_z4_idx ON ne_10m_admin_1_states_provinces_lines_gen_z4 USING gist (geometry); + + +-- etldoc: ne_10m_admin_1_states_provinces_lines_gen_z4 -> ne_10m_admin_1_states_provinces_lines_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_admin_1_states_provinces_lines_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_admin_1_states_provinces_lines_gen_z3 AS +( +SELECT ST_Simplify(geometry, ZRes(5)) as geometry, + admin_level, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z4 +WHERE min_zoom <= 7 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_admin_1_states_provinces_lines_gen_z3_idx ON ne_10m_admin_1_states_provinces_lines_gen_z3 USING gist (geometry); + +-- etldoc: ne_10m_admin_1_states_provinces_lines_gen_z3 -> ne_10m_admin_1_states_provinces_lines_gen_z2 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_admin_1_states_provinces_lines_gen_z2 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_admin_1_states_provinces_lines_gen_z2 AS +( +SELECT ST_Simplify(geometry, ZRes(4)) as geometry, + admin_level, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z3 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_admin_1_states_provinces_lines_gen_z2_idx ON ne_10m_admin_1_states_provinces_lines_gen_z2 USING gist (geometry); + +-- etldoc: ne_10m_admin_1_states_provinces_lines_gen_z2 -> ne_10m_admin_1_states_provinces_lines_gen_z1 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_admin_1_states_provinces_lines_gen_z1 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_admin_1_states_provinces_lines_gen_z1 AS +( +SELECT ST_Simplify(geometry, ZRes(3)) as geometry, + admin_level, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z2 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_admin_1_states_provinces_lines_gen_z1_idx ON ne_10m_admin_1_states_provinces_lines_gen_z1 USING gist (geometry); + +-- ne_50m_admin_0_boundary_lines_land +-- etldoc: ne_50m_admin_0_boundary_lines_land -> ne_50m_admin_0_boundary_lines_land_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_admin_0_boundary_lines_land_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_admin_0_boundary_lines_land_gen_z3 AS +( +SELECT ST_Simplify(geometry, ZRes(5)) as geometry, + 2 AS admin_level, + (CASE WHEN featurecla LIKE 'Disputed%' THEN TRUE ELSE FALSE END) AS disputed, + NULL::text AS disputed_name, + NULL::text AS claimed_by, + FALSE AS maritime +FROM ne_50m_admin_0_boundary_lines_land + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_admin_0_boundary_lines_land_gen_z3_idx ON ne_50m_admin_0_boundary_lines_land_gen_z3 USING gist (geometry); + +-- etldoc: ne_50m_admin_0_boundary_lines_land_gen_z3 -> ne_50m_admin_0_boundary_lines_land_gen_z2 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_admin_0_boundary_lines_land_gen_z2 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_admin_0_boundary_lines_land_gen_z2 AS +( +SELECT ST_Simplify(geometry, ZRes(4)) as geometry, + admin_level, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_50m_admin_0_boundary_lines_land_gen_z3 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_admin_0_boundary_lines_land_gen_z2_idx ON ne_50m_admin_0_boundary_lines_land_gen_z2 USING gist (geometry); + +-- etldoc: ne_50m_admin_0_boundary_lines_land_gen_z2 -> ne_50m_admin_0_boundary_lines_land_gen_z1 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_admin_0_boundary_lines_land_gen_z1 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_admin_0_boundary_lines_land_gen_z1 AS +( +SELECT ST_Simplify(geometry, ZRes(3)) as geometry, + admin_level, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_50m_admin_0_boundary_lines_land_gen_z2 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_admin_0_boundary_lines_land_gen_z1_idx ON ne_50m_admin_0_boundary_lines_land_gen_z1 USING gist (geometry); + +-- ne_110m_admin_0_boundary_lines_land +-- etldoc: ne_110m_admin_0_boundary_lines_land -> ne_110m_admin_0_boundary_lines_land_gen_z0 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_admin_0_boundary_lines_land_gen_z0 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_admin_0_boundary_lines_land_gen_z0 AS +( +SELECT ST_Simplify(geometry, ZRes(2)) as geometry, + 2 AS admin_level, + (CASE WHEN featurecla LIKE 'Disputed%' THEN TRUE ELSE FALSE END) AS disputed, + NULL::text AS disputed_name, + NULL::text AS claimed_by, + FALSE AS maritime +FROM ne_110m_admin_0_boundary_lines_land + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_admin_0_boundary_lines_land_gen_z0_idx ON ne_110m_admin_0_boundary_lines_land_gen_z0 USING gist (geometry); + + +CREATE OR REPLACE FUNCTION edit_name(name varchar) RETURNS text AS +$$ +SELECT CASE + WHEN POSITION(' at ' IN name) > 0 + THEN replace(SUBSTRING(name, POSITION(' at ' IN name) + 4), ' ', '') + ELSE replace(replace(name, ' ', ''), 'Extentof', '') + END; +$$ LANGUAGE SQL IMMUTABLE + -- STRICT + PARALLEL SAFE + ; + + +-- etldoc: ne_110m_admin_0_boundary_lines_land_gen_z0 -> boundary_z0 +CREATE OR REPLACE VIEW boundary_z0 AS +( +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_110m_admin_0_boundary_lines_land_gen_z0 + ); + +-- etldoc: ne_50m_admin_0_boundary_lines_land_gen_z1 -> boundary_z1 +-- etldoc: ne_10m_admin_1_states_provinces_lines_gen_z1 -> boundary_z1 +-- etldoc: ne_10m_admin_0_boundary_lines_land_disputed -> boundary_z1 +-- etldoc: osm_border_disp_linestring_gen_z1 -> boundary_z1 +DROP MATERIALIZED VIEW IF EXISTS boundary_z1 CASCADE; +CREATE MATERIALIZED VIEW boundary_z1 AS +( +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_50m_admin_0_boundary_lines_land_gen_z1 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z1 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_0_boundary_lines_land_disputed + ); +CREATE INDEX IF NOT EXISTS boundary_z1_idx ON boundary_z1 USING gist (geometry); + + +-- etldoc: ne_50m_admin_0_boundary_lines_land_gen_z2 -> boundary_z2 +-- etldoc: ne_10m_admin_1_states_provinces_lines_gen_z2 -> boundary_z2 +-- etldoc: ne_10m_admin_0_boundary_lines_land_disputed -> boundary_z2 +-- etldoc: osm_border_disp_linestring_gen_z2 -> boundary_z2 +DROP MATERIALIZED VIEW IF EXISTS boundary_z2 CASCADE; +CREATE MATERIALIZED VIEW boundary_z2 AS +( +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_50m_admin_0_boundary_lines_land_gen_z2 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z2 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_0_boundary_lines_land_disputed + ); +CREATE INDEX IF NOT EXISTS boundary_z2_idx ON boundary_z2 USING gist (geometry); + +-- etldoc: ne_50m_admin_0_boundary_lines_land_gen_z3 -> boundary_z3 +-- etldoc: ne_10m_admin_1_states_provinces_lines_gen_z3 -> boundary_z3 +-- etldoc: ne_10m_admin_0_boundary_lines_land_disputed -> boundary_z3 +-- etldoc: osm_border_disp_linestring_gen_z3 -> boundary_z3 +DROP MATERIALIZED VIEW IF EXISTS boundary_z3 CASCADE; +CREATE MATERIALIZED VIEW boundary_z3 AS +( +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_50m_admin_0_boundary_lines_land_gen_z3 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z3 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_0_boundary_lines_land_disputed + ); +CREATE INDEX IF NOT EXISTS boundary_z3_idx ON boundary_z3 USING gist (geometry); + +-- etldoc: ne_10m_admin_0_boundary_lines_land_gen_z4 -> boundary_z4 +-- etldoc: ne_10m_admin_1_states_provinces_lines_gen_z4 -> boundary_z4 +-- etldoc: osm_border_linestring_gen_z4 -> boundary_z4 +DROP MATERIALIZED VIEW IF EXISTS boundary_z4 CASCADE; +CREATE MATERIALIZED VIEW boundary_z4 AS +( +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_0_boundary_lines_land_gen_z4 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z4 +UNION ALL +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z4 + ); +CREATE INDEX IF NOT EXISTS boundary_z4_idx ON boundary_z4 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z5 -> boundary_z5 +CREATE OR REPLACE VIEW boundary_z5 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z5 +WHERE admin_level <= 4 + ); + +-- etldoc: osm_border_linestring_gen_z6 -> boundary_z6 +CREATE OR REPLACE VIEW boundary_z6 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z6 +WHERE admin_level <= 4 + ); + +-- etldoc: osm_border_linestring_gen_z7 -> boundary_z7 +CREATE OR REPLACE VIEW boundary_z7 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z7 +WHERE admin_level <= 6 + ); + +-- etldoc: osm_border_linestring_gen_z8 -> boundary_z8 +CREATE OR REPLACE VIEW boundary_z8 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z8 +WHERE admin_level <= 6 + ); + +-- etldoc: osm_border_linestring_gen_z9 -> boundary_z9 +CREATE OR REPLACE VIEW boundary_z9 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z9 +WHERE admin_level <= 6 + ); + +-- etldoc: osm_border_linestring_gen_z10 -> boundary_z10 +CREATE OR REPLACE VIEW boundary_z10 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z10 +WHERE admin_level <= 6 + ); + +-- etldoc: osm_border_linestring_gen_z11 -> boundary_z11 +CREATE OR REPLACE VIEW boundary_z11 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z11 +WHERE admin_level <= 8 + ); + +-- etldoc: osm_border_linestring_gen_z12 -> boundary_z12 +CREATE OR REPLACE VIEW boundary_z12 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z12 + ); + +-- etldoc: osm_border_linestring_gen_z13 -> boundary_z13 +CREATE OR REPLACE VIEW boundary_z13 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z13 + ); + +-- etldoc: layer_boundary[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label=" layer_boundary | z0 | z1 | z2 | z3 | z4 | z5 | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+"] +CREATE OR REPLACE FUNCTION layer_boundary(bbox geometry, zoom_level int) + RETURNS TABLE + ( + geometry geometry, + admin_level int, + adm0_l text, + adm0_r text, + disputed int, + disputed_name text, + claimed_by text, + maritime int, + class text, + name text, + tags hstore + ) +AS +$$ +SELECT geometry, admin_level, adm0_l, adm0_r, disputed::int, disputed_name, claimed_by, maritime::int, NULL::text, NULL::text, NULL::hstore +FROM ( + -- etldoc: boundary_z0 -> layer_boundary:z0 + SELECT * + FROM boundary_z0 + WHERE geometry && bbox + AND zoom_level = 0 + UNION ALL + -- etldoc: boundary_z1 -> layer_boundary:z1 + SELECT * + FROM boundary_z1 + WHERE geometry && bbox + AND zoom_level = 1 + UNION ALL + -- etldoc: boundary_z2 -> layer_boundary:z2 + SELECT * + FROM boundary_z2 + WHERE geometry && bbox + AND zoom_level = 2 + UNION ALL + -- etldoc: boundary_z3 -> layer_boundary:z3 + SELECT * + FROM boundary_z3 + WHERE geometry && bbox + AND zoom_level = 3 + UNION ALL + -- etldoc: boundary_z4 -> layer_boundary:z4 + SELECT * + FROM boundary_z4 + WHERE geometry && bbox + AND zoom_level = 4 + UNION ALL + -- etldoc: boundary_z5 -> layer_boundary:z5 + SELECT * + FROM boundary_z5 + WHERE geometry && bbox + AND zoom_level = 5 + UNION ALL + -- etldoc: boundary_z6 -> layer_boundary:z6 + SELECT * + FROM boundary_z6 + WHERE geometry && bbox + AND zoom_level = 6 + UNION ALL + -- etldoc: boundary_z7 -> layer_boundary:z7 + SELECT * + FROM boundary_z7 + WHERE geometry && bbox + AND zoom_level = 7 + UNION ALL + -- etldoc: boundary_z8 -> layer_boundary:z8 + SELECT * + FROM boundary_z8 + WHERE geometry && bbox + AND zoom_level = 8 + UNION ALL + -- etldoc: boundary_z9 -> layer_boundary:z9 + SELECT * + FROM boundary_z9 + WHERE geometry && bbox + AND zoom_level = 9 + UNION ALL + -- etldoc: boundary_z10 -> layer_boundary:z10 + SELECT * + FROM boundary_z10 + WHERE geometry && bbox + AND zoom_level = 10 + UNION ALL + -- etldoc: boundary_z11 -> layer_boundary:z11 + SELECT * + FROM boundary_z11 + WHERE geometry && bbox + AND zoom_level = 11 + UNION ALL + -- etldoc: boundary_z12 -> layer_boundary:z12 + SELECT * + FROM boundary_z12 + WHERE geometry && bbox + AND zoom_level = 12 + UNION ALL + -- etldoc: boundary_z13 -> layer_boundary:z13 + SELECT * + FROM boundary_z13 + WHERE geometry && bbox + AND zoom_level >= 13 + ) AS segment_zoom_levels + +UNION ALL + +SELECT geometry, NULL::int, NULL::text, NULL::text, NULL::int, NULL::text, NULL::text, NULL::int, class, name, tags +FROM ( + + -- etldoc: osm_boundary_polygon_gen_z4 -> layer_boundary:z4 + SELECT geometry, + boundary AS class, + name, + tags + FROM osm_boundary_polygon_gen_z4 + WHERE zoom_level = 4 + AND geometry && bbox + + UNION ALL + + -- etldoc: osm_boundary_polygon_gen_z5 -> layer_boundary:z5 + SELECT geometry, + boundary AS class, + name, + tags + FROM osm_boundary_polygon_gen_z5 + WHERE zoom_level = 5 + AND geometry && bbox + + UNION ALL + + -- etldoc: osm_boundary_polygon_gen_z6 -> layer_boundary:z6 + SELECT geometry, + boundary AS class, + name, + tags + FROM osm_boundary_polygon_gen_z6 + WHERE zoom_level = 6 + AND geometry && bbox + + UNION ALL + + -- etldoc: osm_boundary_polygon_gen_z7 -> layer_boundary:z7 + SELECT geometry, + boundary AS class, + name, + tags + FROM osm_boundary_polygon_gen_z7 + WHERE zoom_level = 7 + AND geometry && bbox + + UNION ALL + + -- etldoc: osm_boundary_polygon_gen_z8 -> layer_boundary:z8 + SELECT geometry, + boundary AS class, + name, + tags + FROM osm_boundary_polygon_gen_z8 + WHERE zoom_level = 8 + AND geometry && bbox + + UNION ALL + + -- etldoc: osm_boundary_polygon_gen_z9 -> layer_boundary:z9 + SELECT geometry, + boundary AS class, + name, + tags + FROM osm_boundary_polygon_gen_z9 + WHERE zoom_level = 9 + AND geometry && bbox + + UNION ALL + + -- etldoc: osm_boundary_polygon_gen_z10 -> layer_boundary:z10 + SELECT geometry, + boundary AS class, + name, + tags + FROM osm_boundary_polygon_gen_z10 + WHERE zoom_level = 10 + AND geometry && bbox + + UNION ALL + + -- etldoc: osm_boundary_polygon_gen_z11 -> layer_boundary:z11 + SELECT geometry, + boundary AS class, + name, + tags + FROM osm_boundary_polygon_gen_z11 + WHERE zoom_level = 11 + AND geometry && bbox + + UNION ALL + + -- etldoc: osm_boundary_polygon_gen_z12 -> layer_boundary:z12 + SELECT geometry, + boundary AS class, + name, + tags + FROM osm_boundary_polygon_gen_z12 + WHERE zoom_level = 12 + AND geometry && bbox + + UNION ALL + + -- etldoc: osm_boundary_polygon_gen_z13 -> layer_boundary:z13 + SELECT geometry, + boundary AS class, + name, + tags + FROM osm_boundary_polygon_gen_z13 + WHERE zoom_level = 13 + AND geometry && bbox + + UNION ALL + + -- etldoc: osm_boundary_polygon -> layer_boundary:z14 + SELECT geometry, + boundary AS class, + name, + tags + FROM osm_boundary_polygon + WHERE zoom_level = 14 + AND geometry && bbox + + ) AS area_zoom_levels + +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/boundary/boundary.yaml b/layers/boundary/boundary.yaml new file mode 100644 index 0000000..636cb9e --- /dev/null +++ b/layers/boundary/boundary.yaml @@ -0,0 +1,72 @@ +layer: + id: "boundary" + requires: + tables: + - osm_border_linestring + - ne_10m_admin_0_countries + - ne_10m_admin_0_boundary_lines_land + - ne_10m_admin_1_states_provinces_lines + - ne_50m_admin_0_boundary_lines_land + - ne_110m_admin_0_boundary_lines_land + description: | + Contains administrative boundaries as linestrings and aboriginal lands as polygons. + Until z4 [Natural Earth data](http://www.naturalearthdata.com/downloads/) is used after which + OSM boundaries ([`boundary=administrative`](http://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative)) + are present from z5 to z14 (also for maritime boundaries with `admin_level <= 2` at z4). + OSM data contains several [`admin_level`](http://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative#admin_level) + but for most styles it makes sense to just style `admin_level=2` and `admin_level=4`. + fields: + class: + description: | + Use the **class** to differentiate between different kinds of boundaries. The class for `boundary=aboriginal_lands` is `aboriginal_lands`. + name: The OSM [`name`](http://wiki.openstreetmap.org/wiki/Key:name) value (area features only). + admin_level: | + OSM [admin_level](http://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative#admin_level) + indicating the level of importance of this boundary. + The `admin_level` corresponds to the lowest `admin_level` + the line participates in. + At low zoom levels the Natural Earth boundaries are mapped to the equivalent admin levels. + adm0_l: | + State name on the left of the border. For country boundaries only (`admin_level = 2`). + adm0_r: | + State name on the right of the border. For country boundaries only (`admin_level = 2`). + disputed: + description: | + Mark with `1` if the border is disputed. + values: [0, 1] + disputed_name: + description: | + Field containing name of the disputed area (extracted from border relation in OSM, without spaces). + For country boundaries only (`admin_level = 2`). + Value examples from Asian OSM pbf extract + values: + - AbuMusaIsland + - BaraHotiiValleys + - ChineseClaim + - Crimea + - Demchok + - Dokdo + - IndianClaim-North + - IndianClaimwesternKashmir + - PakistaniClaim + - SamduValleys + - TirpaniValleys + claimed_by: + description: | + ISO2 code of country, which wants to see the boundary line. + For country boundaries only (`admin_level = 2`). + maritime: + description: | + Mark with `1` if it is a maritime border. + values: [0, 1] + buffer_size: 4 + datasource: + geometry_field: geometry + query: (SELECT geometry, admin_level, adm0_l, adm0_r, disputed, disputed_name, claimed_by, maritime, class, name, {name_languages} FROM layer_boundary(!bbox!, z(!scale_denominator!))) AS t +schema: + - ./update_boundary_polygon.sql + - ./boundary_name.sql + - ./boundary.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/boundary/boundary_name.sql b/layers/boundary/boundary_name.sql new file mode 100644 index 0000000..4be7dac --- /dev/null +++ b/layers/boundary/boundary_name.sql @@ -0,0 +1,105 @@ +DROP TABLE IF EXISTS osm_border_linestring_adm CASCADE; + +-- etldoc: osm_border_linestring -> osm_border_linestring_adm +-- etldoc: osm_border_disp_linestring -> osm_border_linestring_adm +-- etldoc: ne_10m_admin_0_countries -> osm_border_linestring_adm +CREATE TABLE IF NOT EXISTS osm_border_linestring_adm AS ( + WITH + -- Prepare lines from osm to be merged + multiline AS ( + SELECT osm_id, + ST_Node(ST_Collect(geometry)) AS geometry, + BOOL_OR(maritime) AS maritime, + FALSE AS disputed + FROM osm_border_linestring + WHERE admin_level = 2 AND ST_Dimension(geometry) = 1 + AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring) + GROUP BY osm_id + ), + + mergedline AS ( + SELECT osm_id, + (ST_Dump(ST_LineMerge(geometry))).geom AS geometry, + maritime, + disputed + FROM multiline + ), + -- Create polygons from all boundaries to preserve real shape of country + polyg AS ( + SELECT (ST_Dump( + ST_Polygonize(geometry))).geom AS geometry + FROM ( + SELECT (ST_Dump( + ST_LineMerge(geometry))).geom AS geometry + FROM (SELECT ST_Node( + ST_Collect(geometry)) AS geometry + FROM osm_border_linestring + WHERE admin_level = 2 AND ST_Dimension(geometry) = 1 + ) nodes + ) linemerge + ), + + centroids AS ( + SELECT polyg.geometry, + ne.adm0_a3 + FROM polyg, + ne_10m_admin_0_countries AS ne + WHERE ST_Within( + ST_PointOnSurface(polyg.geometry), ne.geometry) + ), + + country_osm_polyg AS ( + SELECT country.adm0_a3, + border.geometry + FROM polyg border, + centroids country + WHERE ST_Within(country.geometry, border.geometry) + ), + + rights AS ( + SELECT osm_id, + adm0_r, + geometry, + maritime, + disputed + FROM ( + SELECT a.osm_id AS osm_id, + b.adm0_a3 AS adm0_r, + a.geometry, + a.maritime, + a.disputed + FROM mergedline AS a + LEFT JOIN country_osm_polyg AS b + -- Create short line on the right of the boundary (mergedline) and find state where line lies. + ON ST_Within( + ST_OffsetCurve( + (ST_LineSubString(a.geometry, 0.3,0.3004)), 70, 'quad_segs=4 join=mitre'), b.geometry) + ) line_rights + ) + + SELECT osm_id, + adm0_l, + adm0_r, + geometry, + maritime, + 2::integer AS admin_level, + disputed + FROM ( + SELECT r.osm_id AS osm_id, + b.adm0_a3 AS adm0_l, + r.adm0_r AS adm0_r, + r.geometry, + r.maritime, + r.disputed + FROM rights AS r + LEFT JOIN country_osm_polyg AS b + -- Create short line on the left of the boundary (mergedline) and find state where line lies. + ON ST_Within( + ST_OffsetCurve( + (ST_LineSubString(r.geometry, 0.4,0.4004)), -70, 'quad_segs=4 join=mitre'), b.geometry) + ) both_lines +); + +CREATE INDEX IF NOT EXISTS osm_border_linestring_adm_geom_idx + ON osm_border_linestring_adm + USING GIST (geometry); diff --git a/layers/boundary/etl_diagram.png b/layers/boundary/etl_diagram.png new file mode 100644 index 0000000..f07c2ec Binary files /dev/null and b/layers/boundary/etl_diagram.png differ diff --git a/layers/boundary/mapping.yaml b/layers/boundary/mapping.yaml new file mode 100644 index 0000000..bb2de21 --- /dev/null +++ b/layers/boundary/mapping.yaml @@ -0,0 +1,160 @@ +generalized_tables: + # etldoc: osm_border_linestring -> osm_border_disp_linestring + border_disp_linestring: + source: border_linestring + sql_filter: ST_GeometryType(geometry) = 'ST_LineString' AND (disputed OR dispute OR border_status = 'disputed' OR disputed_by <> '') AND admin_level = 2 + + # etldoc: osm_boundary_polygon_gen_z5 -> osm_boundary_polygon_gen_z4 + boundary_polygon_gen_z4: + source: boundary_polygon_gen_z5 + sql_filter: area>power(ZRES3,2) + tolerance: ZRES4 + + # etldoc: osm_boundary_polygon_gen_z6 -> osm_boundary_polygon_gen_z5 + boundary_polygon_gen_z5: + source: boundary_polygon_gen_z6 + sql_filter: area>power(ZRES4,2) + tolerance: ZRES5 + + # etldoc: osm_boundary_polygon_gen_z7 -> osm_boundary_polygon_gen_z6 + boundary_polygon_gen_z6: + source: boundary_polygon_gen_z7 + sql_filter: area>power(ZRES5,2) + tolerance: ZRES6 + + # etldoc: osm_boundary_polygon_gen_z8 -> osm_boundary_polygon_gen_z7 + boundary_polygon_gen_z7: + source: boundary_polygon_gen_z8 + sql_filter: area>power(ZRES6,2) + tolerance: ZRES7 + + # etldoc: osm_boundary_polygon_gen_z9 -> osm_boundary_polygon_gen_z8 + boundary_polygon_gen_z8: + source: boundary_polygon_gen_z9 + sql_filter: area>power(ZRES7,2) + tolerance: ZRES8 + + # etldoc: osm_boundary_polygon_gen_z10 -> osm_boundary_polygon_gen_z9 + boundary_polygon_gen_z9: + source: boundary_polygon_gen_z10 + sql_filter: area>power(ZRES8,2) + tolerance: ZRES9 + + # etldoc: osm_boundary_polygon_gen_z11 -> osm_boundary_polygon_gen_z10 + boundary_polygon_gen_z10: + source: boundary_polygon_gen_z11 + sql_filter: area>power(ZRES9,2) + tolerance: ZRES10 + + # etldoc: osm_boundary_polygon_gen_z12 -> osm_boundary_polygon_gen_z11 + boundary_polygon_gen_z11: + source: boundary_polygon_gen_z12 + sql_filter: area>power(ZRES10,2) + tolerance: ZRES11 + + # etldoc: osm_boundary_polygon_gen_z13 -> osm_boundary_polygon_gen_z12 + boundary_polygon_gen_z12: + source: boundary_polygon_gen_z13 + sql_filter: area>power(ZRES11,2) + tolerance: ZRES12 + + # etldoc: osm_boundary_polygon -> osm_boundary_polygon_gen_z13 + boundary_polygon_gen_z13: + source: boundary_polygon + sql_filter: area>power(ZRES12,2) AND ST_IsValid(geometry) + tolerance: ZRES13 + + +tables: + # etldoc: imposm3 -> osm_border_linestring + border_linestring: + type: relation_member + filters: + require: + admin_level: [__any__] + boundary: [administrative] + columns: + - name: relation_id + type: id + - name: osm_id + type: id + from_member: true + - name: member + type: member_id + - name: type + type: member_type + - name: geometry + type: geometry + - key: name + name: name + type: string + # Used for disputed boundary, e.g. "Line of actual control" + from_member: true + - key: admin_level + name: admin_level + type: integer + - key: claimed_by + name: claimed_by + type: string + - key: disputed_by + name: disputed_by + type: string + from_member: true + - key: dispute + name: dispute + type: bool + from_member: true + - key: disputed + name: disputed + type: bool + from_member: true + - key: border_status + name: border_status + type: string + from_member: true + - key: maritime + name: maritime + type: bool + from_member: true + - key: boundary_type + name: boundary_type + type: string + from_member: true + - key: natural + name: natural + type: string + from_member: true + relation_types: [boundary] + mapping: + boundary: + - administrative + border_status: + - dispute + boundary_type: + - maritime + + # etldoc: imposm3 -> osm_boundary_polygon + boundary_polygon: + type: polygon + filters: + require: + type: [boundary] + boundary: [aboriginal_lands] + columns: + - name: osm_id + type: id + - name: geometry + type: validated_geometry + - name: name + key: name + type: string + - name: tags + type: hstore_tags + - name: boundary + key: boundary + type: string + - name: area + type: area + mapping: + boundary: + - aboriginal_lands diff --git a/layers/boundary/mapping_diagram.png b/layers/boundary/mapping_diagram.png new file mode 100644 index 0000000..8ec79b6 Binary files /dev/null and b/layers/boundary/mapping_diagram.png differ diff --git a/layers/boundary/style.json b/layers/boundary/style.json new file mode 100644 index 0000000..068be95 --- /dev/null +++ b/layers/boundary/style.json @@ -0,0 +1,287 @@ +{ + "layers": [ + { + "id": "boundary_3", + "type": "line", + "source": "openmaptiles", + "source-layer": "boundary", + "minzoom": 3, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#845283", + "line-width": { + "base": 1, + "stops": [ + [ + 4, + 0.4 + ], + [ + 5, + 0.7 + ], + [ + 12, + 1.6 + ] + ] + }, + "line-opacity": { + "stops": [ + [ + 3, + 0.5 + ], + [ + 10, + 1 + ] + ] + }, + "line-dasharray": [ + 5, + 3 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "admin_level", + 3, + 4 + ], + [ + "==", + "maritime", + 0 + ] + ], + "order": 146 + }, + { + "id": "boundary_2", + "type": "line", + "source": "openmaptiles", + "source-layer": "boundary", + "minzoom": 0, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#a37da1", + "line-width": { + "base": 1, + "stops": [ + [ + 3, + 0.5 + ], + [ + 5, + 1.2 + ], + [ + 12, + 3 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "admin_level", + 2 + ], + [ + "==", + "maritime", + 0 + ], + [ + "==", + "disputed", + 0 + ] + ], + "order": 147 + }, + { + "id": "boundary_2_disputed", + "type": "line", + "source": "openmaptiles", + "source-layer": "boundary", + "minzoom": 0, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#a37da1", + "line-width": { + "base": 1, + "stops": [ + [ + 3, + 0.3 + ], + [ + 5, + 1.2 + ], + [ + 12, + 3 + ] + ] + }, + "line-opacity": 1, + "line-dasharray": [ + 4, + 3 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "admin_level", + 2 + ], + [ + "==", + "disputed", + 1 + ], + [ + "==", + "maritime", + 0 + ] + ], + "order": 148 + }, + { + "id": "boundary_2_disputed_maritime", + "type": "line", + "source": "openmaptiles", + "source-layer": "boundary", + "minzoom": 0, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(129, 125, 163, 1)", + "line-width": { + "base": 1, + "stops": [ + [ + 3, + 0.5 + ], + [ + 5, + 1.2 + ], + [ + 12, + 3 + ] + ] + }, + "line-opacity": 1, + "line-dasharray": [ + 4, + 3 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "admin_level", + 2 + ], + [ + "==", + "disputed", + 1 + ], + [ + "==", + "maritime", + 1 + ] + ], + "order": 149 + }, + { + "id": "boundary_2_maritime", + "type": "line", + "source": "openmaptiles", + "source-layer": "boundary", + "minzoom": 4, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#a37da1", + "line-width": { + "base": 1, + "stops": [ + [ + 3, + 0.5 + ], + [ + 5, + 1.2 + ], + [ + 12, + 3 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "admin_level", + 2 + ], + [ + "==", + "disputed", + 0 + ], + [ + "==", + "maritime", + 1 + ] + ], + "order": 150 + } + ] +} \ No newline at end of file diff --git a/layers/boundary/update_boundary_polygon.sql b/layers/boundary/update_boundary_polygon.sql new file mode 100644 index 0000000..ee0ab1a --- /dev/null +++ b/layers/boundary/update_boundary_polygon.sql @@ -0,0 +1,170 @@ +ALTER TABLE osm_boundary_polygon + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_boundary_polygon_gen_z13 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_boundary_polygon_gen_z12 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_boundary_polygon_gen_z11 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_boundary_polygon_gen_z10 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_boundary_polygon_gen_z9 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_boundary_polygon_gen_z8 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_boundary_polygon_gen_z7 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_boundary_polygon_gen_z6 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_boundary_polygon_gen_z5 + ADD COLUMN IF NOT EXISTS geometry_point geometry; + +DROP TRIGGER IF EXISTS update_row ON osm_boundary_polygon; +DROP TRIGGER IF EXISTS update_row ON osm_boundary_polygon_gen_z13; +DROP TRIGGER IF EXISTS update_row ON osm_boundary_polygon_gen_z12; +DROP TRIGGER IF EXISTS update_row ON osm_boundary_polygon_gen_z11; +DROP TRIGGER IF EXISTS update_row ON osm_boundary_polygon_gen_z10; +DROP TRIGGER IF EXISTS update_row ON osm_boundary_polygon_gen_z9; +DROP TRIGGER IF EXISTS update_row ON osm_boundary_polygon_gen_z8; +DROP TRIGGER IF EXISTS update_row ON osm_boundary_polygon_gen_z7; +DROP TRIGGER IF EXISTS update_row ON osm_boundary_polygon_gen_z6; +DROP TRIGGER IF EXISTS update_row ON osm_boundary_polygon_gen_z5; + +-- etldoc: osm_boundary_polygon -> osm_boundary_polygon +-- etldoc: osm_boundary_polygon_gen_z13 -> osm_boundary_polygon_gen_z13 +-- etldoc: osm_boundary_polygon_gen_z12 -> osm_boundary_polygon_gen_z12 +-- etldoc: osm_boundary_polygon_gen_z11 -> osm_boundary_polygon_gen_z11 +-- etldoc: osm_boundary_polygon_gen_z10 -> osm_boundary_polygon_gen_z10 +-- etldoc: osm_boundary_polygon_gen_z9 -> osm_boundary_polygon_gen_z9 +-- etldoc: osm_boundary_polygon_gen_z8 -> osm_boundary_polygon_gen_z8 +-- etldoc: osm_boundary_polygon_gen_z7 -> osm_boundary_polygon_gen_z7 +-- etldoc: osm_boundary_polygon_gen_z6 -> osm_boundary_polygon_gen_z6 +-- etldoc: osm_boundary_polygon_gen_z5 -> osm_boundary_polygon_gen_z5 +CREATE OR REPLACE FUNCTION update_osm_boundary_polygon() RETURNS void AS +$$ +BEGIN + UPDATE osm_boundary_polygon + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_boundary_polygon_gen_z13 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_boundary_polygon_gen_z12 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_boundary_polygon_gen_z11 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_boundary_polygon_gen_z10 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_boundary_polygon_gen_z9 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_boundary_polygon_gen_z8 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_boundary_polygon_gen_z7 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_boundary_polygon_gen_z6 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_boundary_polygon_gen_z5 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + +END; +$$ LANGUAGE plpgsql; + +SELECT update_osm_boundary_polygon(); +CREATE INDEX IF NOT EXISTS osm_boundary_polygon_point_geom_idx ON osm_boundary_polygon USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_boundary_polygon_gen_z13_point_geom_idx ON osm_boundary_polygon_gen_z13 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_boundary_polygon_gen_z12_point_geom_idx ON osm_boundary_polygon_gen_z12 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_boundary_polygon_gen_z11_point_geom_idx ON osm_boundary_polygon_gen_z11 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_boundary_polygon_gen_z10_point_geom_idx ON osm_boundary_polygon_gen_z10 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_boundary_polygon_gen_z9_point_geom_idx ON osm_boundary_polygon_gen_z9 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_boundary_polygon_gen_z8_point_geom_idx ON osm_boundary_polygon_gen_z8 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_boundary_polygon_gen_z7_point_geom_idx ON osm_boundary_polygon_gen_z7 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_boundary_polygon_gen_z6_point_geom_idx ON osm_boundary_polygon_gen_z6 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_boundary_polygon_gen_z5_point_geom_idx ON osm_boundary_polygon_gen_z5 USING gist (geometry_point); + +CREATE OR REPLACE FUNCTION update_osm_boundary_polygon_row() + RETURNS trigger +AS +$$ +BEGIN + NEW.tags = update_tags(NEW.tags, NEW.geometry); + NEW.geometry_point = ST_PointOnSurface(NEW.geometry); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_boundary_polygon + FOR EACH ROW +EXECUTE PROCEDURE update_osm_boundary_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_boundary_polygon_gen_z13 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_boundary_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_boundary_polygon_gen_z12 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_boundary_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_boundary_polygon_gen_z11 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_boundary_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_boundary_polygon_gen_z10 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_boundary_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_boundary_polygon_gen_z9 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_boundary_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_boundary_polygon_gen_z8 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_boundary_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_boundary_polygon_gen_z7 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_boundary_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_boundary_polygon_gen_z6 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_boundary_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_boundary_polygon_gen_z5 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_boundary_polygon_row(); diff --git a/layers/building/README.md b/layers/building/README.md new file mode 100644 index 0000000..fc89103 --- /dev/null +++ b/layers/building/README.md @@ -0,0 +1,10 @@ +## building + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#building** + +### Mapping Diagram +![Mapping diagram for building](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for building](etl_diagram.png?raw=true) diff --git a/layers/building/building.sql b/layers/building/building.sql new file mode 100644 index 0000000..271f622 --- /dev/null +++ b/layers/building/building.sql @@ -0,0 +1,126 @@ +-- etldoc: layer_building[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_building | z13 | z14+ " ] ; + +CREATE INDEX IF NOT EXISTS osm_building_relation_building_idx ON osm_building_relation (building) WHERE building = '' AND ST_GeometryType(geometry) = 'ST_Polygon'; +CREATE INDEX IF NOT EXISTS osm_building_relation_member_idx ON osm_building_relation (member) WHERE role = 'outline'; + +CREATE OR REPLACE VIEW osm_all_buildings AS +( +SELECT + -- etldoc: osm_building_relation -> layer_building:z14_ + -- Buildings built from relations + member AS osm_id, + geometry, + COALESCE(CleanNumeric(height), CleanNumeric(buildingheight)) AS height, + COALESCE(CleanNumeric(min_height), CleanNumeric(buildingmin_height)) AS min_height, + COALESCE(CleanNumeric(levels), CleanNumeric(buildinglevels)) AS levels, + COALESCE(CleanNumeric(min_level), CleanNumeric(buildingmin_level)) AS min_level, + nullif(material, '') AS material, + nullif(colour, '') AS colour, + FALSE AS hide_3d, + building +FROM osm_building_relation +WHERE building = '' + AND ST_GeometryType(geometry) = 'ST_Polygon' +UNION ALL + +SELECT + -- etldoc: osm_building_polygon -> layer_building:z14_ + -- Standalone buildings + obp.osm_id, + obp.geometry, + COALESCE(CleanNumeric(obp.height), CleanNumeric(obp.buildingheight)) AS height, + COALESCE(CleanNumeric(obp.min_height), CleanNumeric(obp.buildingmin_height)) AS min_height, + COALESCE(CleanNumeric(obp.levels), CleanNumeric(obp.buildinglevels)) AS levels, + COALESCE(CleanNumeric(obp.min_level), CleanNumeric(obp.buildingmin_level)) AS min_level, + nullif(obp.material, '') AS material, + nullif(obp.colour, '') AS colour, + obr.role IS NOT NULL AS hide_3d, + obp.building AS building +FROM osm_building_polygon obp + LEFT JOIN osm_building_relation obr ON + obp.osm_id >= 0 AND + obr.member = obp.osm_id AND + obr.role = 'outline' +WHERE ST_GeometryType(obp.geometry) IN ('ST_Polygon', 'ST_MultiPolygon') + ); + +CREATE OR REPLACE FUNCTION layer_building(bbox geometry, zoom_level int) + RETURNS TABLE + ( + geometry geometry, + osm_id bigint, + render_height int, + render_min_height int, + colour text, + hide_3d boolean, + building text + ) +AS +$$ +SELECT geometry, + osm_id, + render_height, + render_min_height, + COALESCE(colour, CASE material + -- Ordered by count from taginfo + WHEN 'cement_block' THEN '#6a7880' + WHEN 'brick' THEN '#bd8161' + WHEN 'plaster' THEN '#dadbdb' + WHEN 'wood' THEN '#d48741' + WHEN 'concrete' THEN '#d3c2b0' + WHEN 'metal' THEN '#b7b1a6' + WHEN 'stone' THEN '#b4a995' + WHEN 'mud' THEN '#9d8b75' + WHEN 'steel' THEN '#b7b1a6' -- same as metal + WHEN 'glass' THEN '#5a81a0' + WHEN 'traditional' THEN '#bd8161' -- same as brick + WHEN 'masonry' THEN '#bd8161' -- same as brick + WHEN 'Brick' THEN '#bd8161' -- same as brick + WHEN 'tin' THEN '#b7b1a6' -- same as metal + WHEN 'timber_framing' THEN '#b3b0a9' + WHEN 'sandstone' THEN '#b4a995' -- same as stone + WHEN 'clay' THEN '#9d8b75' -- same as mud + END) AS colour, + CASE WHEN hide_3d THEN TRUE END AS hide_3d, + building +FROM ( + SELECT + -- etldoc: osm_building_block_gen_z13 -> layer_building:z13 + osm_id, + geometry, + NULL::int AS render_height, + NULL::int AS render_min_height, + NULL::text AS material, + NULL::text AS colour, + FALSE AS hide_3d, + NULL::text AS building + FROM osm_building_block_gen_z13 + WHERE zoom_level = 13 + AND geometry && bbox + UNION ALL + SELECT + -- etldoc: osm_building_polygon -> layer_building:z14_ + DISTINCT ON (osm_id) osm_id, + geometry, + ceil(COALESCE(height, levels * 3.66, 5))::int AS render_height, + floor(COALESCE(min_height, min_level * 3.66, 0))::int AS render_min_height, + material, + colour, + hide_3d, + building + FROM osm_all_buildings + WHERE (levels IS NULL OR levels < 1000) + AND (min_level IS NULL OR min_level < 1000) + AND (height IS NULL OR height < 3000) + AND (min_height IS NULL OR min_height < 3000) + AND zoom_level >= 14 + AND geometry && bbox + ) AS zoom_levels +ORDER BY render_height ASC, ST_YMin(geometry) DESC; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE + ; + +-- not handled: where a building outline covers building parts diff --git a/layers/building/building.yaml b/layers/building/building.yaml new file mode 100644 index 0000000..bb1c8e7 --- /dev/null +++ b/layers/building/building.yaml @@ -0,0 +1,28 @@ +layer: + id: "building" + description: | + All [OSM Buildings](http://wiki.openstreetmap.org/wiki/Buildings). All building tags are imported ([`building=*`](http://wiki.openstreetmap.org/wiki/Key:building)). + Only buildings with tag location:underground are excluded. + buffer_size: 4 + datasource: + geometry_field: geometry + key_field: osm_id + key_field_as_attribute: no + srid: 900913 + query: (SELECT osm_id, geometry, render_height, render_min_height, colour, hide_3d, building FROM layer_building(!bbox!, z(!scale_denominator!))) AS t + fields: + render_height: | + An approximated height from levels and height of the building or building:part. + render_min_height: | + An approximated height from minimum levels or minimum height of the bottom of the building or building:part. + colour: | + Colour + hide_3d: | + If True, building (part) should not be rendered in 3D. Currently, [building outlines](https://wiki.openstreetmap.org/wiki/Simple_3D_buildings) are marked as hide_3d. + building: +schema: + - ./update_building.sql + - ./building.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/building/etl_diagram.png b/layers/building/etl_diagram.png new file mode 100644 index 0000000..097a964 Binary files /dev/null and b/layers/building/etl_diagram.png differ diff --git a/layers/building/mapping.yaml b/layers/building/mapping.yaml new file mode 100644 index 0000000..e32e48e --- /dev/null +++ b/layers/building/mapping.yaml @@ -0,0 +1,163 @@ +#generalized_tables: +# # etldoc: imposm3 -> osm_building_polygon_gen1 +# building_polygon_gen1: +# source: building_polygon +# sql_filter: area>power(ZRES12,2) AND ST_IsValid(geometry) +# tolerance: ZRES14 + +tables: + # etldoc: imposm3 -> osm_building_polygon + building_polygon: + columns: + - name: osm_id + type: id + - name: geometry + type: validated_geometry + - name: area + type: area + - name: material + key: building:material + type: string + - name: colour + key: building:colour + type: string + - name: building + key: building + type: string + - name: buildingpart + key: building:part + type: string + - name: buildingheight + key: building:height + type: string + - name: buildingmin_height + key: building:min_height + type: string + - name: buildinglevels + key: building:levels + type: string + - name: buildingmin_level + key: building:min_level + type: string + - name: height + key: height + type: string + - name: min_height + key: min_height + type: string + - name: levels + key: levels + type: string + - name: min_level + key: min_level + type: string + mapping: + building:part: + - __any__ + building: + - __any__ + # these aeroway polygons all imply building=yes + aeroway: + - terminal + - hangar + location: + - underground + filters: + reject: + building: ["no","none","No"] + building:part: ["no","none","No"] + man_made: ["bridge"] + location: ["underground"] + type: polygon + + # etldoc: imposm3 -> osm_building_relation + building_relation: + columns: + - name: osm_id + type: id + - name: geometry + type: validated_geometry + - name: area + type: area + - name: building + key: building + type: string + from_member: true + - name: material + key: building:material + type: string + - name: colour + key: building:colour + type: string + - name: buildingpart + key: building:part + type: string + from_member: true + - name: buildingheight + key: building:height + type: string + from_member: true + - name: height + key: height + type: string + from_member: true + - name: buildingmin_height + key: building:min_height + type: string + from_member: true + - name: min_height + key: min_height + type: string + from_member: true + - name: buildinglevels + key: building:levels + type: string + from_member: true + - name: levels + key: levels + type: string + from_member: true + - name: buildingmin_level + key: building:min_level + type: string + from_member: true + - name: min_level + key: min_level + type: string + from_member: true + - name: relbuildingheight + key: building:height + type: string + - name: relheight + key: height + type: string + - name: relbuildingmin_height + key: building:min_height + type: string + - name: relmin_height + key: min_height + type: string + - name: relbuildinglevels + key: building:levels + type: string + - name: rellevels + key: levels + type: string + - name: relbuildingmin_level + key: building:min_level + type: string + - name: relmin_level + key: min_level + type: string + - name: member + type: member_id + - name: index + type: member_index + - name: role + type: member_role + from_member: true + - name: type + type: member_type + mapping: + type: [building] + type: relation_member diff --git a/layers/building/mapping_diagram.png b/layers/building/mapping_diagram.png new file mode 100644 index 0000000..767b00e Binary files /dev/null and b/layers/building/mapping_diagram.png differ diff --git a/layers/building/style.json b/layers/building/style.json new file mode 100644 index 0000000..4834f68 --- /dev/null +++ b/layers/building/style.json @@ -0,0 +1,44 @@ +{ + "layers": [ + { + "id": "building", + "type": "fill", + "source": "openmaptiles", + "source-layer": "building", + "minzoom": 12, + "maxzoom": 24, + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": { + "stops": [ + [ + 13, + "rgba(222, 213, 207, 1)" + ], + [ + 16, + "#d9d0c9" + ] + ] + }, + "fill-outline-color": { + "base": 1, + "stops": [ + [ + 13, + "#9A918A" + ], + [ + 16, + "rgba(166, 157, 150, 1)" + ] + ] + } + }, + "metadata": {}, + "order": 19 + } + ] +} \ No newline at end of file diff --git a/layers/building/update_building.sql b/layers/building/update_building.sql new file mode 100644 index 0000000..d363eac --- /dev/null +++ b/layers/building/update_building.sql @@ -0,0 +1,185 @@ +DROP TRIGGER IF EXISTS trigger_refresh ON buildings.updates; +DROP TRIGGER IF EXISTS trigger_flag ON osm_building_polygon; + +-- Creating aggregated building blocks with removed small polygons and small +-- holes. Aggregated polygons are simplified by Visvalingam-Whyatt algorithm. +-- Aggregating is made block by block using country_osm_grid polygon table. + +-- Function returning recordset for matview. +-- Returning recordset of buildings aggregates by zres 14, with removed small +-- holes and with removed small buildings/blocks. + +CREATE OR REPLACE FUNCTION osm_building_block_gen1() + RETURNS table + ( + osm_id bigint, + geometry geometry + ) +AS +$$ +DECLARE + zres14 float := Zres(14); + zres12 float := Zres(12); + zres14vw float := Zres(14) * Zres(14); + polyg_world record; + +BEGIN + FOR polyg_world IN + SELECT ST_Transform(country.geometry, 3857) AS geometry + FROM country_osm_grid country + + LOOP + FOR osm_id, geometry IN + WITH dta AS ( -- CTE is used because of optimization + SELECT o.osm_id, + o.geometry, + ST_ClusterDBSCAN(o.geometry, eps := zres14, minpoints := 1) OVER () cid + FROM osm_building_polygon o + WHERE ST_Intersects(o.geometry, polyg_world.geometry) + ) + SELECT (array_agg(dta.osm_id))[1] AS osm_id, + ST_Buffer( + ST_Union( + ST_Buffer( + ST_SnapToGrid(dta.geometry, 0.000001) + , zres14, 'join=mitre') + ) + , -zres14, 'join=mitre') AS geometry + FROM dta + GROUP BY cid + + LOOP + -- removing holes smaller than + IF ST_NumInteriorRings(geometry) > 0 THEN -- only from geometries wih holes + geometry := ( + -- there are some multi-geometries in this layer + SELECT ST_Collect(gn) + FROM ( + -- in some cases are "holes" NULL, because all holes are smaller than + SELECT COALESCE( + -- exterior ring + ST_MakePolygon(ST_ExteriorRing(dmp.geom), holes), + ST_MakePolygon(ST_ExteriorRing(dmp.geom)) + ) gn + + FROM ST_Dump(geometry) dmp, -- 1 dump polygons + LATERAL ( + SELECT array_agg(ST_Boundary(rg.geom)) holes -- 2 create array + FROM ST_DumpRings(dmp.geom) rg -- 3 from rings + WHERE rg.path[1] > 0 -- 5 except inner ring + AND ST_Area(rg.geom) >= power(zres12, 2) -- 4 bigger than + ) holes + ) new_geom + ); + END IF; + + IF ST_Area(geometry) < power(zres12, 2) THEN + CONTINUE; + END IF; + + -- simplify + geometry := ST_SimplifyVW(geometry, zres14vw); + + RETURN NEXT; + END LOOP; + END LOOP; +END; +$$ LANGUAGE plpgsql STABLE + STRICT + PARALLEL SAFE; + + +DROP MATERIALIZED VIEW IF EXISTS osm_building_block_gen1_dup CASCADE; + +CREATE MATERIALIZED VIEW osm_building_block_gen1_dup AS +SELECT * +FROM osm_building_block_gen1(); + +CREATE INDEX ON osm_building_block_gen1_dup USING gist (geometry); + +-- etldoc: osm_building_polygon -> osm_building_block_gen_z13 +DROP MATERIALIZED VIEW IF EXISTS osm_building_block_gen_z13; +CREATE MATERIALIZED VIEW osm_building_block_gen_z13 AS +( +WITH + counts AS ( + SELECT count(osm_id) AS counts, + osm_id + FROM osm_building_block_gen1_dup + GROUP BY osm_id + ), + + duplicates AS ( + SELECT counts.osm_id + FROM counts + WHERE counts.counts > 1 + ) + +SELECT osm.osm_id, + ST_Union( + ST_MakeValid(osm.geometry)) AS geometry + FROM osm_building_block_gen1_dup osm, + duplicates + WHERE osm.osm_id = duplicates.osm_id + GROUP BY osm.osm_id + + UNION ALL + + SELECT osm.osm_id, + osm.geometry + FROM osm_building_block_gen1_dup osm, + counts + WHERE counts.counts = 1 + AND osm.osm_id = counts.osm_id +); + +CREATE INDEX ON osm_building_block_gen_z13 USING gist (geometry); +CREATE UNIQUE INDEX ON osm_building_block_gen_z13 USING btree (osm_id); + +-- Handle updates + +CREATE SCHEMA IF NOT EXISTS buildings; + +CREATE TABLE IF NOT EXISTS buildings.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); + +CREATE OR REPLACE FUNCTION buildings.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO buildings.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION buildings.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh buildings block'; + REFRESH MATERIALIZED VIEW osm_building_block_gen1_dup; + REFRESH MATERIALIZED VIEW osm_building_block_gen_z13; + -- noinspection SqlWithoutWhere + DELETE FROM buildings.updates; + + RAISE LOG 'Update buildings block done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE + ON osm_building_polygon + FOR EACH STATEMENT +EXECUTE PROCEDURE buildings.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON buildings.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE buildings.refresh(); \ No newline at end of file diff --git a/layers/housenumber/README.md b/layers/housenumber/README.md new file mode 100644 index 0000000..b0e03c5 --- /dev/null +++ b/layers/housenumber/README.md @@ -0,0 +1,10 @@ +## housenumber + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#housenumber** + +### Mapping Diagram +![Mapping diagram for housenumber](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for housenumber](etl_diagram.png?raw=true) diff --git a/layers/housenumber/etl_diagram.png b/layers/housenumber/etl_diagram.png new file mode 100644 index 0000000..d93f971 Binary files /dev/null and b/layers/housenumber/etl_diagram.png differ diff --git a/layers/housenumber/housenumber.sql b/layers/housenumber/housenumber.sql new file mode 100644 index 0000000..22dc438 --- /dev/null +++ b/layers/housenumber/housenumber.sql @@ -0,0 +1,33 @@ +-- etldoc: layer_housenumber[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_housenumber | z14+" ] ; + +CREATE OR REPLACE FUNCTION layer_housenumber(bbox geometry, zoom_level integer) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + housenumber text + ) +AS +$$ +SELECT + -- etldoc: osm_housenumber_point -> layer_housenumber:z14_ + osm_id, + geometry, + display_housenumber(housenumber) +FROM ( + SELECT + osm_id, + geometry, + housenumber, + row_number() OVER(PARTITION BY concat(street, block_number, housenumber) ORDER BY has_name ASC) as rn + FROM osm_housenumber_point + WHERE 1=1 + AND zoom_level >= 14 + AND geometry && bbox +) t +WHERE rn = 1; + +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/housenumber/housenumber.yaml b/layers/housenumber/housenumber.yaml new file mode 100644 index 0000000..99a8a7c --- /dev/null +++ b/layers/housenumber/housenumber.yaml @@ -0,0 +1,22 @@ +layer: + id: "housenumber" + description: | + Everything in OpenStreetMap which contains a `addr:housenumber` tag useful for labelling housenumbers on a map. + This adds significant size to *z14*. For buildings the centroid of the building is used as housenumber. + Duplicates within a tile are dropped if they have the same street/block_number (records without name tag are prioritized for preservation). + buffer_size: 8 + srs: +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over + fields: + housenumber: Value of the [`addr:housenumber`](http://wiki.openstreetmap.org/wiki/Key:addr) tag. + If there are multiple values separated by semi-colons, the first and last value separated by a dash. + datasource: + geometry_field: geometry + srid: 900913 + query: (SELECT geometry, housenumber FROM layer_housenumber(!bbox!, z(!scale_denominator!))) AS t +schema: + - ./housenumber_display.sql + - ./housenumber_centroid.sql + - ./housenumber.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/housenumber/housenumber_centroid.sql b/layers/housenumber/housenumber_centroid.sql new file mode 100644 index 0000000..425cd90 --- /dev/null +++ b/layers/housenumber/housenumber_centroid.sql @@ -0,0 +1,102 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_housenumber_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_housenumber_point; +DROP TRIGGER IF EXISTS trigger_refresh ON housenumber.updates; + +CREATE SCHEMA IF NOT EXISTS housenumber; + +CREATE TABLE IF NOT EXISTS housenumber.osm_ids +( + osm_id bigint PRIMARY KEY +); + +-- etldoc: osm_housenumber_point -> osm_housenumber_point +CREATE OR REPLACE FUNCTION convert_housenumber_point(full_update boolean) RETURNS void AS +$$ + UPDATE osm_housenumber_point + SET geometry = + CASE + WHEN ST_NPoints(ST_ConvexHull(geometry)) = ST_NPoints(geometry) + THEN ST_Centroid(geometry) + ELSE ST_PointOnSurface(geometry) + END + WHERE (full_update OR osm_id IN (SELECT osm_id FROM housenumber.osm_ids)) + AND ST_GeometryType(geometry) <> 'ST_Point' + AND ST_IsValid(geometry); + + -- we don't need exact name just to know if it's present + UPDATE osm_housenumber_point + SET has_name = + CASE + WHEN has_name = '' THEN '0' + ELSE '1' + END + WHERE (full_update OR osm_id IN (SELECT osm_id FROM housenumber.osm_ids)); + +$$ LANGUAGE SQL; + +SELECT convert_housenumber_point(true); + +-- Handle updates + +CREATE OR REPLACE FUNCTION housenumber.store() RETURNS trigger AS +$$ +BEGIN + INSERT INTO housenumber.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS housenumber.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION housenumber.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO housenumber.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION housenumber.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh housenumber'; + + -- Analyze tracking and source tables before performing update + ANALYZE housenumber.osm_ids; + ANALYZE osm_housenumber_point; + + PERFORM convert_housenumber_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM housenumber.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM housenumber.updates; + + RAISE LOG 'Refresh housenumber done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE + ON osm_housenumber_point + FOR EACH ROW +EXECUTE PROCEDURE housenumber.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE + ON osm_housenumber_point + FOR EACH STATEMENT +EXECUTE PROCEDURE housenumber.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON housenumber.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE housenumber.refresh(); diff --git a/layers/housenumber/housenumber_display.sql b/layers/housenumber/housenumber_display.sql new file mode 100644 index 0000000..a924c3b --- /dev/null +++ b/layers/housenumber/housenumber_display.sql @@ -0,0 +1,20 @@ +CREATE OR REPLACE FUNCTION display_housenumber_nonnumeric(raw_housenumber text) +RETURNS text AS $$ + -- Find the position of the semicolon in the input string + -- and extract the first and last value + SELECT substring(raw_housenumber from 1 for position(';' in raw_housenumber) - 1) + || '–' + || substring(raw_housenumber from position(';' in raw_housenumber) + 1); +$$ LANGUAGE SQL IMMUTABLE; + + +CREATE OR REPLACE FUNCTION display_housenumber(raw_housenumber text) +RETURNS text AS $$ + SELECT CASE + WHEN raw_housenumber !~ ';' THEN raw_housenumber + WHEN raw_housenumber ~ '[^0-9;]' THEN display_housenumber_nonnumeric(raw_housenumber) + ELSE + (SELECT min(value)::text || '–' || max(value)::text + FROM unnest(array_remove(string_to_array(raw_housenumber, ';'), '')::bigint[]) AS value) + END +$$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/housenumber/mapping.yaml b/layers/housenumber/mapping.yaml new file mode 100644 index 0000000..4bb0fbe --- /dev/null +++ b/layers/housenumber/mapping.yaml @@ -0,0 +1,30 @@ + +tables: + + # etldoc: imposm3 -> osm_housenumber_point + housenumber_point: + type: geometry + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - name: housenumber + key: addr:housenumber + type: string + - name: street + key: addr:street + type: string + - name: block_number + key: addr:block_number + type: string + - name: has_name + key: name + type: string + type_mappings: + points: + addr:housenumber: + - __any__ + polygons: + addr:housenumber: + - __any__ diff --git a/layers/housenumber/mapping_diagram.png b/layers/housenumber/mapping_diagram.png new file mode 100644 index 0000000..0892169 Binary files /dev/null and b/layers/housenumber/mapping_diagram.png differ diff --git a/layers/housenumber/style.json b/layers/housenumber/style.json new file mode 100644 index 0000000..108fd04 --- /dev/null +++ b/layers/housenumber/style.json @@ -0,0 +1,40 @@ +{ + "layers": [ + { + "id": "housenumber", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "housenumber", + "minzoom": 17, + "layout": { + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "stops": [ + [ + 17, + 9 + ], + [ + 22, + 11 + ] + ] + }, + "text-field": "{housenumber}", + "text-padding": 3, + "text-line-height": -0.15, + "symbol-avoid-edges": false, + "text-allow-overlap": false, + "text-ignore-placement": false + }, + "paint": { + "text-color": "rgba(102, 102, 102, 1)", + "text-halo-color": "rgba(255,255,255,0.8)", + "text-halo-width": 1 + }, + "order": 154 + } + ] +} \ No newline at end of file diff --git a/layers/kct/layer.sql b/layers/kct/layer.sql deleted file mode 100644 index 1461e9e..0000000 --- a/layers/kct/layer.sql +++ /dev/null @@ -1,6 +0,0 @@ -CREATE OR REPLACE FUNCTION layer_kct(bbox geometry, zoom_level int) -RETURNS TABLE(geometry geometry, name text, waycolor text) AS $$ - SELECT geometry, NULLIF(name, '') AS name, SPLIT_PART(osmc_symbol, ':', 1) as waycolor - FROM osm_kct_relation - WHERE geometry && bbox; -$$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/kct/mapping.yaml b/layers/kct/mapping.yaml deleted file mode 100644 index 8c27c16..0000000 --- a/layers/kct/mapping.yaml +++ /dev/null @@ -1,22 +0,0 @@ -tables: - # etldoc: imposm3 -> osm_kct_relation - kct_relation: - columns: - - name: geometry - type: geometry - - key: name - name: name - type: string - - key: osmc:symbol - name: osmc_symbol - type: string - mapping: - kct_blue: - - major - kct_green: - - major - kct_red: - - major - kct_yellow: - - major - type: relation_member diff --git a/layers/landcover/README.md b/layers/landcover/README.md new file mode 100644 index 0000000..94be4e3 --- /dev/null +++ b/layers/landcover/README.md @@ -0,0 +1,10 @@ +## landcover + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#landcover** + +### Mapping Diagram +![Mapping diagram for landcover](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for landcover](etl_diagram.png?raw=true) diff --git a/layers/landcover/etl_diagram.png b/layers/landcover/etl_diagram.png new file mode 100644 index 0000000..03b3e19 Binary files /dev/null and b/layers/landcover/etl_diagram.png differ diff --git a/layers/landcover/generalized.sql b/layers/landcover/generalized.sql new file mode 100644 index 0000000..1dc52f0 --- /dev/null +++ b/layers/landcover/generalized.sql @@ -0,0 +1,287 @@ +DROP TABLE IF EXISTS osm_landcover_gen_z7; +DROP TABLE IF EXISTS osm_landcover_gen_z8; +DROP TABLE IF EXISTS osm_landcover_gen_z9; +DROP TABLE IF EXISTS osm_landcover_gen_z10; +DROP TABLE IF EXISTS osm_landcover_gen_z11; +DROP TABLE IF EXISTS osm_landcover_gen_z12; +DROP TABLE IF EXISTS osm_landcover_gen_z13; +DROP TABLE IF EXISTS simplify_vw_z7 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z8 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z9 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z10 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z11 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z12 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z13 CASCADE; + +-- etldoc: osm_landcover_polygon -> simplify_vw_z13 +CREATE TABLE simplify_vw_z13 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(13),2)), + 0.001)) AS geometry + FROM osm_landcover_polygon + WHERE ST_Area(geometry) > power(zres(12),2) +); +CREATE INDEX ON simplify_vw_z13 USING GIST (geometry); + +-- etldoc: simplify_vw_z13 -> osm_landcover_gen_z13 +CREATE TABLE osm_landcover_gen_z13 AS +( + SELECT subclass, ST_MakeValid((ST_dump(ST_Union(geometry))).geom) AS geometry + FROM ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) over () AS cid, geometry + FROM simplify_vw_z13 + WHERE ST_NPoints(geometry) < 300 + AND subclass IN ('wood', 'forest')) union_geom300 + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + geometry + FROM simplify_vw_z13 + WHERE (ST_NPoints(geometry) >= 300 AND subclass IN ('wood', 'forest')) + OR (subclass NOT IN ('wood', 'forest')) + ); + +CREATE INDEX ON osm_landcover_gen_z13 USING GIST (geometry); + + +-- etldoc: simplify_vw_z13 -> simplify_vw_z12 +CREATE TABLE simplify_vw_z12 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(12),2)), + 0.001)) AS geometry + FROM simplify_vw_z13 + WHERE ST_Area(geometry) > power(zres(11),2) +); +CREATE INDEX ON simplify_vw_z12 USING GIST (geometry); + +-- etldoc: simplify_vw_z12 -> osm_landcover_gen_z12 +CREATE TABLE osm_landcover_gen_z12 AS +( + SELECT subclass, ST_MakeValid((ST_dump(ST_Union(geometry))).geom) AS geometry + FROM ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) over () AS cid, geometry + FROM simplify_vw_z12 + WHERE ST_NPoints(geometry) < 300 + AND subclass IN ('wood', 'forest')) union_geom300 + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + geometry + FROM simplify_vw_z12 + WHERE (ST_NPoints(geometry) >= 300 AND subclass IN ('wood', 'forest')) + OR (subclass NOT IN ('wood', 'forest')) + ); + +CREATE INDEX ON osm_landcover_gen_z12 USING GIST (geometry); + + +-- etldoc: simplify_vw_z12 -> simplify_vw_z11 +CREATE TABLE simplify_vw_z11 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(11),2)), + 0.001)) AS geometry + FROM simplify_vw_z12 + WHERE ST_Area(geometry) > power(zres(10),2) +); +CREATE INDEX ON simplify_vw_z11 USING GIST (geometry); + +-- etldoc: simplify_vw_z11 -> osm_landcover_gen_z11 +CREATE TABLE osm_landcover_gen_z11 AS +( + SELECT subclass, ST_MakeValid((ST_dump(ST_Union(geometry))).geom) AS geometry + FROM ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) over () AS cid, geometry + FROM simplify_vw_z11 + WHERE ST_NPoints(geometry) < 300 + AND subclass IN ('wood', 'forest')) union_geom300 + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + geometry + FROM simplify_vw_z11 + WHERE (ST_NPoints(geometry) >= 300 AND subclass IN ('wood', 'forest')) + OR (subclass NOT IN ('wood', 'forest')) + ); + +CREATE INDEX ON osm_landcover_gen_z11 USING GIST (geometry); + + +-- etldoc: simplify_vw_z11 -> simplify_vw_z10 +CREATE TABLE simplify_vw_z10 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(10),2)), + 0.001)) AS geometry + FROM simplify_vw_z11 + WHERE ST_Area(geometry) > power(zres(9),2) +); +CREATE INDEX ON simplify_vw_z10 USING GIST (geometry); + +-- etldoc: simplify_vw_z10 -> osm_landcover_gen_z10 +CREATE TABLE osm_landcover_gen_z10 AS +( + SELECT subclass, ST_MakeValid((ST_dump(ST_Union(geometry))).geom) AS geometry + FROM ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) over () AS cid, geometry + FROM simplify_vw_z10 + WHERE ST_NPoints(geometry) < 300 + AND subclass IN ('wood', 'forest')) union_geom300 + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + geometry + FROM simplify_vw_z10 + WHERE (ST_NPoints(geometry) >= 300 AND subclass IN ('wood', 'forest')) + OR (subclass NOT IN ('wood', 'forest')) + ); + +CREATE INDEX ON osm_landcover_gen_z10 USING GIST (geometry); + + +-- etldoc: simplify_vw_z10 -> simplify_vw_z9 +CREATE TABLE simplify_vw_z9 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(9),2)), + 0.001)) AS geometry + FROM simplify_vw_z10 + WHERE ST_Area(geometry) > power(zres(8),2) +); +CREATE INDEX ON simplify_vw_z9 USING GIST (geometry); + +-- etldoc: simplify_vw_z9 -> osm_landcover_gen_z9 +CREATE TABLE osm_landcover_gen_z9 AS +( + SELECT subclass, ST_MakeValid((ST_dump(ST_Union(geometry))).geom) AS geometry + FROM ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) over () AS cid, geometry + FROM simplify_vw_z9 + WHERE ST_NPoints(geometry) < 300 + AND subclass IN ('wood', 'forest')) union_geom300 + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + ST_MakeValid( + (ST_Dump( + ST_Union(geometry))).geom) AS geometry + FROM ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) over () AS cid, geometry + FROM simplify_vw_z9 + WHERE ST_NPoints(geometry) >= 300 + AND subclass IN ('wood', 'forest')) union_geom_rest + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + geometry + FROM simplify_vw_z9 + WHERE subclass NOT IN ('wood', 'forest') + ); + +CREATE INDEX ON osm_landcover_gen_z9 USING GIST (geometry); + + +-- etldoc: simplify_vw_z9 -> simplify_vw_z8 +CREATE TABLE simplify_vw_z8 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(8),2)), + 0.001)) AS geometry + FROM simplify_vw_z9 + WHERE ST_Area(geometry) > power(zres(7),2) + ); +CREATE INDEX ON simplify_vw_z8 USING GIST (geometry); + +-- etldoc: simplify_vw_z8 -> osm_landcover_gen_z8 +CREATE TABLE osm_landcover_gen_z8 AS +( +SELECT subclass, + ST_MakeValid( + (ST_Dump( + ST_Union(geometry))).geom) AS geometry + FROM + ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) OVER () AS cid, + geometry + FROM simplify_vw_z8 + WHERE subclass IN ('wood', 'forest') + ) union_geom + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + geometry + FROM simplify_vw_z8 + WHERE subclass NOT IN ('wood', 'forest') + ); + +CREATE INDEX ON osm_landcover_gen_z8 USING GIST (geometry); + + +-- etldoc: simplify_vw_z8 -> simplify_vw_z7 +CREATE TABLE simplify_vw_z7 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(7),2)), + 0.001)) AS geometry + FROM simplify_vw_z8 + WHERE ST_Area(geometry) > power(zres(6),2) +); +CREATE INDEX ON simplify_vw_z7 USING GIST (geometry); + +-- etldoc: simplify_vw_z7 -> osm_landcover_gen_z7 +CREATE TABLE osm_landcover_gen_z7 AS +( +SELECT subclass, + ST_MakeValid( + (ST_Dump( + ST_Union(geometry))).geom) AS geometry + FROM + ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) OVER () AS cid, + geometry + FROM simplify_vw_z7 + ) union_geom +GROUP BY subclass, + cid + ); + +CREATE INDEX ON osm_landcover_gen_z7 USING GIST (geometry); + +DROP TABLE IF EXISTS simplify_vw_z7 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z8 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z9 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z10 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z11 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z12 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z13 CASCADE; diff --git a/layers/landcover/landcover.sql b/layers/landcover/landcover.sql new file mode 100644 index 0000000..4cffdb4 --- /dev/null +++ b/layers/landcover/landcover.sql @@ -0,0 +1,373 @@ +--TODO: Find a way to nicely generalize landcover +--CREATE TABLE IF NOT EXISTS landcover_grouped_gen2 AS ( +-- SELECT osm_id, ST_Simplify((ST_Dump(geometry)).geom, 600) AS geometry, landuse, "natural", wetland +-- FROM ( +-- SELECT max(osm_id) AS osm_id, ST_Union(ST_Buffer(geometry, 600)) AS geometry, landuse, "natural", wetland +-- FROM osm_landcover_polygon_gen1 +-- GROUP BY LabelGrid(geometry, 15000000), landuse, "natural", wetland +-- ) AS grouped_measurements +--); +--CREATE INDEX IF NOT EXISTS landcover_grouped_gen2_geometry_idx ON landcover_grouped_gen2 USING gist(geometry); + +CREATE OR REPLACE FUNCTION landcover_class(subclass varchar) RETURNS text AS +$$ +SELECT CASE + %%FIELD_MAPPING: class %% + END; +$$ LANGUAGE SQL IMMUTABLE + -- STRICT + PARALLEL SAFE; + +-- ne_50m_antarctic_ice_shelves_polys +-- etldoc: ne_50m_antarctic_ice_shelves_polys -> ne_50m_antarctic_ice_shelves_polys_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_antarctic_ice_shelves_polys_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_antarctic_ice_shelves_polys_gen_z4 AS +( +SELECT + ST_Simplify(geometry, ZRes(6)) as geometry, + 'ice_shelf'::text AS subclass +FROM ne_50m_antarctic_ice_shelves_polys + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_antarctic_ice_shelves_polys_gen_z4_idx ON ne_50m_antarctic_ice_shelves_polys_gen_z4 USING gist (geometry); + +-- ne_110m_glaciated_areas +-- etldoc: ne_110m_glaciated_areas -> ne_110m_glaciated_areas_gen_z1 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_glaciated_areas_gen_z1 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_glaciated_areas_gen_z1 AS +( +SELECT + ST_Simplify(geometry, ZRes(3)) as geometry, + 'glacier'::text AS subclass +FROM ne_110m_glaciated_areas + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_glaciated_areas_gen_z1_idx ON ne_110m_glaciated_areas_gen_z1 USING gist (geometry); + +-- etldoc: ne_110m_glaciated_areas_gen_z1 -> ne_110m_glaciated_areas_gen_z0 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_glaciated_areas_gen_z0 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_glaciated_areas_gen_z0 AS +( +SELECT + ST_Simplify(geometry, ZRes(2)) as geometry, + subclass +FROM ne_110m_glaciated_areas_gen_z1 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_glaciated_areas_gen_z0_idx ON ne_110m_glaciated_areas_gen_z0 USING gist (geometry); + +-- etldoc: ne_50m_antarctic_ice_shelves_polys_gen_z4 -> ne_50m_antarctic_ice_shelves_polys_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_antarctic_ice_shelves_polys_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_antarctic_ice_shelves_polys_gen_z3 AS +( +SELECT + ST_Simplify(geometry, ZRes(5)) as geometry, + subclass +FROM ne_50m_antarctic_ice_shelves_polys_gen_z4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_antarctic_ice_shelves_polys_gen_z3_idx ON ne_50m_antarctic_ice_shelves_polys_gen_z3 USING gist (geometry); + +-- etldoc: ne_50m_antarctic_ice_shelves_polys_gen_z3 -> ne_50m_antarctic_ice_shelves_polys_gen_z2 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_antarctic_ice_shelves_polys_gen_z2 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_antarctic_ice_shelves_polys_gen_z2 AS +( +SELECT + ST_Simplify(geometry, ZRes(4)) as geometry, + subclass +FROM ne_50m_antarctic_ice_shelves_polys_gen_z3 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_antarctic_ice_shelves_polys_gen_z2_idx ON ne_50m_antarctic_ice_shelves_polys_gen_z2 USING gist (geometry); + +-- ne_50m_glaciated_areas +-- etldoc: ne_50m_glaciated_areas -> ne_50m_glaciated_areas_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_glaciated_areas_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_glaciated_areas_gen_z4 AS +( +SELECT + ST_Simplify(geometry, ZRes(6)) as geometry, + 'glacier'::text AS subclass +FROM ne_50m_glaciated_areas + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_glaciated_areas_gen_z4_idx ON ne_50m_glaciated_areas_gen_z4 USING gist (geometry); + +-- etldoc: ne_50m_glaciated_areas_gen_z4 -> ne_50m_glaciated_areas_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_glaciated_areas_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_glaciated_areas_gen_z3 AS +( +SELECT + ST_Simplify(geometry, ZRes(5)) as geometry, + subclass +FROM ne_50m_glaciated_areas_gen_z4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_glaciated_areas_gen_z3_idx ON ne_50m_glaciated_areas_gen_z3 USING gist (geometry); + +-- etldoc: ne_50m_glaciated_areas_gen_z3 -> ne_50m_glaciated_areas_gen_z2 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_glaciated_areas_gen_z2 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_glaciated_areas_gen_z2 AS +( +SELECT + ST_Simplify(geometry, ZRes(4)) as geometry, + subclass +FROM ne_50m_glaciated_areas_gen_z3 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_glaciated_areas_gen_z2_idx ON ne_50m_glaciated_areas_gen_z2 USING gist (geometry); + +-- ne_10m_glaciated_areas +-- etldoc: ne_10m_glaciated_areas -> ne_10m_glaciated_areas_gen_z6 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_glaciated_areas_gen_z6 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_glaciated_areas_gen_z6 AS +( +SELECT + ST_Simplify(geometry, ZRes(8)) as geometry, + 'glacier'::text AS subclass +FROM ne_10m_glaciated_areas + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_glaciated_areas_gen_z6_idx ON ne_10m_glaciated_areas_gen_z6 USING gist (geometry); + +-- etldoc: ne_10m_glaciated_areas_gen_z6 -> ne_10m_glaciated_areas_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_glaciated_areas_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_glaciated_areas_gen_z5 AS +( +SELECT + ST_Simplify(geometry, ZRes(7)) as geometry, + subclass +FROM ne_10m_glaciated_areas_gen_z6 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_glaciated_areas_gen_z5_idx ON ne_10m_glaciated_areas_gen_z5 USING gist (geometry); + +-- ne_10m_antarctic_ice_shelves_polys +-- etldoc: ne_10m_antarctic_ice_shelves_polys -> ne_10m_antarctic_ice_shelves_polys_gen_z6 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_antarctic_ice_shelves_polys_gen_z6 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_antarctic_ice_shelves_polys_gen_z6 AS +( +SELECT + ST_Simplify(geometry, ZRes(8)) as geometry, + 'ice_shelf'::text AS subclass +FROM ne_10m_antarctic_ice_shelves_polys + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_antarctic_ice_shelves_polys_gen_z6_idx ON ne_10m_antarctic_ice_shelves_polys_gen_z6 USING gist (geometry); + +-- etldoc: ne_10m_antarctic_ice_shelves_polys_gen_z6 -> ne_10m_antarctic_ice_shelves_polys_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_antarctic_ice_shelves_polys_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_antarctic_ice_shelves_polys_gen_z5 AS +( +SELECT + ST_Simplify(geometry, ZRes(7)) as geometry, + subclass +FROM ne_10m_antarctic_ice_shelves_polys_gen_z6 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_antarctic_ice_shelves_polys_gen_z5_idx ON ne_10m_antarctic_ice_shelves_polys_gen_z5 USING gist (geometry); + +-- etldoc: ne_110m_glaciated_areas_gen_z0 -> landcover_z0 +CREATE OR REPLACE VIEW landcover_z0 AS +( +SELECT + geometry, + subclass +FROM ne_110m_glaciated_areas_gen_z0 + ); + +-- etldoc: ne_110m_glaciated_areas_gen_z1 -> landcover_z1 +CREATE OR REPLACE VIEW landcover_z1 AS +( +SELECT + geometry, + subclass +FROM ne_110m_glaciated_areas_gen_z1 + ); + +CREATE OR REPLACE VIEW landcover_z2 AS +( +-- etldoc: ne_50m_glaciated_areas_gen_z2 -> landcover_z2 +SELECT + geometry, + subclass +FROM ne_50m_glaciated_areas_gen_z2 +UNION ALL +-- etldoc: ne_50m_antarctic_ice_shelves_polys_gen_z2 -> landcover_z2 +SELECT + geometry, + subclass +FROM ne_50m_antarctic_ice_shelves_polys_gen_z2 + ); + +CREATE OR REPLACE VIEW landcover_z3 AS +( +-- etldoc: ne_50m_glaciated_areas_gen_z3 -> landcover_z3 +SELECT + geometry, + subclass +FROM ne_50m_glaciated_areas_gen_z3 +UNION ALL +-- etldoc: ne_50m_antarctic_ice_shelves_polys_gen_z3 -> landcover_z3 +SELECT + geometry, + subclass +FROM ne_50m_antarctic_ice_shelves_polys_gen_z3 + ); + +CREATE OR REPLACE VIEW landcover_z4 AS +( +-- etldoc: ne_50m_glaciated_areas_gen_z4 -> landcover_z4 +SELECT + geometry, + subclass +FROM ne_50m_glaciated_areas_gen_z4 +UNION ALL +-- etldoc: ne_50m_antarctic_ice_shelves_polys_gen_z4 -> landcover_z4 +SELECT + geometry, + subclass +FROM ne_50m_antarctic_ice_shelves_polys_gen_z4 + ); + +CREATE OR REPLACE VIEW landcover_z5 AS +( +-- etldoc: ne_10m_glaciated_areas_gen_z5 -> landcover_z5 +SELECT + geometry, + subclass +FROM ne_10m_glaciated_areas_gen_z5 +UNION ALL +-- etldoc: ne_10m_antarctic_ice_shelves_polys_gen_z5 -> landcover_z5 +SELECT + geometry, + subclass +FROM ne_10m_antarctic_ice_shelves_polys_gen_z5 + ); + +CREATE OR REPLACE VIEW landcover_z6 AS +( +-- etldoc: ne_10m_glaciated_areas_gen_z6 -> landcover_z6 +SELECT + geometry, + subclass +FROM ne_10m_glaciated_areas_gen_z6 +UNION ALL +-- etldoc: ne_10m_antarctic_ice_shelves_polys_gen_z6 -> landcover_z6 +SELECT + geometry, + subclass +FROM ne_10m_antarctic_ice_shelves_polys_gen_z6 + ); + +-- etldoc: layer_landcover[shape=record fillcolor=lightpink, style="rounded, filled", label="layer_landcover | z0 | z1 | z2 | z3 | z4 | z5 | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; + +CREATE OR REPLACE FUNCTION layer_landcover(bbox geometry, zoom_level int) + RETURNS TABLE + ( + geometry geometry, + class text, + subclass text + ) +AS +$$ +SELECT geometry, + landcover_class(subclass) AS class, + subclass +FROM ( + -- etldoc: landcover_z0 -> layer_landcover:z0 + SELECT geometry, + subclass + FROM landcover_z0 + WHERE zoom_level = 0 + AND geometry && bbox + UNION ALL + -- etldoc: landcover_z1 -> layer_landcover:z1 + SELECT geometry, + subclass + FROM landcover_z1 + WHERE zoom_level = 1 + AND geometry && bbox + UNION ALL + -- etldoc: landcover_z2 -> layer_landcover:z2 + SELECT geometry, + subclass + FROM landcover_z2 + WHERE zoom_level = 2 + AND geometry && bbox + UNION ALL + -- etldoc: landcover_z3 -> layer_landcover:z3 + SELECT geometry, + subclass + FROM landcover_z3 + WHERE zoom_level = 3 + AND geometry && bbox + UNION ALL + -- etldoc: landcover_z4 -> layer_landcover:z4 + SELECT geometry, + subclass + FROM landcover_z4 + WHERE zoom_level = 4 + AND geometry && bbox + UNION ALL + -- etldoc: landcover_z5 -> layer_landcover:z5 + SELECT geometry, + subclass + FROM landcover_z5 + WHERE zoom_level = 5 + AND geometry && bbox + UNION ALL + -- etldoc: landcover_z6 -> layer_landcover:z6 + SELECT geometry, + subclass + FROM landcover_z6 + WHERE zoom_level = 6 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z7 -> layer_landcover:z7 + SELECT geometry, + subclass + FROM osm_landcover_gen_z7 + WHERE zoom_level = 7 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z8 -> layer_landcover:z8 + SELECT geometry, + subclass + FROM osm_landcover_gen_z8 + WHERE zoom_level = 8 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z9 -> layer_landcover:z9 + SELECT geometry, + subclass + FROM osm_landcover_gen_z9 + WHERE zoom_level = 9 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z10 -> layer_landcover:z10 + SELECT geometry, + subclass + FROM osm_landcover_gen_z10 + WHERE zoom_level = 10 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z11 -> layer_landcover:z11 + SELECT geometry, + subclass + FROM osm_landcover_gen_z11 + WHERE zoom_level = 11 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z12 -> layer_landcover:z12 + SELECT geometry, + subclass + FROM osm_landcover_gen_z12 + WHERE zoom_level = 12 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z13 -> layer_landcover:z13 + SELECT geometry, + subclass + FROM osm_landcover_gen_z13 + WHERE zoom_level = 13 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_polygon -> layer_landcover:z14_ + SELECT geometry, + subclass + FROM osm_landcover_polygon + WHERE zoom_level >= 14 + AND geometry && bbox + ) AS zoom_levels; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; \ No newline at end of file diff --git a/layers/landcover/landcover.yaml b/layers/landcover/landcover.yaml new file mode 100644 index 0000000..4ac86eb --- /dev/null +++ b/layers/landcover/landcover.yaml @@ -0,0 +1,89 @@ +layer: + id: "landcover" + requires: + tables: + - ne_10m_antarctic_ice_shelves_polys + - ne_10m_glaciated_areas + - ne_50m_antarctic_ice_shelves_polys + - ne_50m_glaciated_areas + - ne_110m_glaciated_areas + description: | + Landcover is used to describe the physical material at the surface of the earth. At lower zoom levels this is + from Natural Earth data for glaciers and ice shelves and at higher zoom levels the landcover is [implied by OSM tags](http://wiki.openstreetmap.org/wiki/Landcover). The most common use case for this layer + is to style wood (`class=wood`) and grass (`class=grass`) areas. + buffer_size: 4 + fields: + class: + description: | + Use the **class** to assign natural colors for **landcover**. + values: + farmland: + subclass: ['farmland', 'farm', 'orchard', 'vineyard', 'plant_nursery'] + ice: + subclass: ['glacier', 'ice_shelf'] + wood: + subclass: ['wood', 'forest'] + rock: + subclass: ['bare_rock', 'scree'] + grass: + subclass: ['fell', 'flowerbed', 'grassland', 'heath', 'scrub', 'shrubbery', 'tundra', 'grass', 'meadow', 'allotments', 'park', 'village_green', 'recreation_ground', 'garden', 'golf_course'] + wetland: + subclass: ['wetland', 'bog', 'swamp', 'wet_meadow', 'marsh', 'reedbed', 'saltern', 'tidalflat', 'saltmarsh', 'mangrove'] + sand: + subclass: ['beach', 'sand', 'dune'] + subclass: + description: | + Use **subclass** to do more precise styling. + Original value of either the + [`natural`](http://wiki.openstreetmap.org/wiki/Key:natural), + [`landuse`](http://wiki.openstreetmap.org/wiki/Key:landuse), + [`leisure`](http://wiki.openstreetmap.org/wiki/Key:leisure), + or [`wetland`](http://wiki.openstreetmap.org/wiki/Key:wetland) tag. + values: + - allotments + - bare_rock + - beach + - bog + - dune + - scrub + - shrubbery + - farm + - farmland + - fell + - flowerbed + - forest + - garden + - glacier + - grass + - grassland + - golf_course + - heath + - mangrove + - marsh + - meadow + - orchard + - park + - plant_nursery + - recreation_ground + - reedbed + - saltern + - saltmarsh + - sand + - scree + - swamp + - tidalflat + - tundra + - village_green + - vineyard + - wet_meadow + - wetland + - wood + datasource: + geometry_field: geometry + query: (SELECT geometry, class, subclass FROM layer_landcover(!bbox!, z(!scale_denominator!))) AS t +schema: + - ./generalized.sql + - ./landcover.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/landcover/mapping.yaml b/layers/landcover/mapping.yaml new file mode 100644 index 0000000..f065f1d --- /dev/null +++ b/layers/landcover/mapping.yaml @@ -0,0 +1,59 @@ +tables: + # etldoc: imposm3 -> osm_landcover_polygon + landcover_polygon: + columns: + - name: osm_id + type: id + - name: geometry + type: validated_geometry + - name: area + type: area + - name: subclass + type: mapping_value + - name: mapping_key + type: mapping_key + mapping: + landuse: + - allotments + - farm + - farmland + - orchard + - flowerbed + - plant_nursery + - vineyard + - grass + - grassland + - meadow + - forest + - village_green + - recreation_ground + natural: + - wood + - wetland + - fell + - grassland + - heath + - scrub + - shrubbery + - tundra + - glacier + - bare_rock + - scree + - beach + - sand + - dune + leisure: + - park + - garden + - golf_course + wetland: + - bog + - swamp + - wet_meadow + - marsh + - reedbed + - saltern + - tidalflat + - saltmarsh + - mangrove + type: polygon diff --git a/layers/landcover/mapping_diagram.png b/layers/landcover/mapping_diagram.png new file mode 100644 index 0000000..fff4722 Binary files /dev/null and b/layers/landcover/mapping_diagram.png differ diff --git a/layers/landcover/style.json b/layers/landcover/style.json new file mode 100644 index 0000000..2783129 --- /dev/null +++ b/layers/landcover/style.json @@ -0,0 +1,482 @@ +{ + "layers": [ + { + "id": "landcover_classes", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landcover", + "maxzoom": 13, + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": [ + "match", + [ + "get", + "class" + ], + "farmland", + "#eef0d5", + "wood", + "#add19e", + "rock", + "#eee5dc", + "grass", + "#cdebb0", + "sand", + "#f5e9c6", + "wetland", + "#add19e", + "#000" + ], + "fill-opacity": { + "stops": [ + [ + 7, + 0.5 + ], + [ + 10, + 1 + ] + ] + }, + "fill-antialias": false + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "farmland", + "wood", + "rock", + "grass", + "wetland", + "sand" + ] + ], + "order": 4 + }, + { + "id": "landcover_class_outline", + "type": "line", + "source": "openmaptiles", + "source-layer": "landcover", + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "#c7c9ae", + "line-width": 0.5 + }, + "filter": [ + "all", + [ + "in", + "class", + "farmland" + ] + ], + "order": 5 + }, + { + "id": "landcover_park", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landcover", + "minzoom": 13, + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": "#c8facc", + "fill-antialias": true + }, + "filter": [ + "all", + [ + "==", + "subclass", + "park" + ] + ], + "order": 6 + }, + { + "id": "landcover_subclasses", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landcover", + "minzoom": 13, + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": [ + "match", + [ + "get", + "subclass" + ], + "allotments", + "#c9e1bf", + "bare_rock", + "#eee5dc", + "beach", + "#fff1ba", + "bog", + "#d6d99f", + "dune", + "#f5e9c6", + "scrub", + "#c8d7ab", + "farm", + "#f5dcba", + "farmland", + "#eef0d5", + "flowerbed", + "#cdebb0", + "forest", + "#add19e", + "grass", + "#cdebb0", + "grassland", + "#cdebb0", + "golf_course", + "#def6c0", + "heath", + "#d6d99f", + "mangrove", + "#c8d7ab", + "meadow", + "#cdebb0", + "orchard", + "#aedfa3", + "park", + "#c8facc", + "garden", + "#cdebb0", + "plant_nursery", + "#aedfa3", + "recreation_ground", + "#d5ffd9", + "reedbed", + "#cdebb0", + "saltmarsh", + "#cdebb0", + "sand", + "#f5e9c6", + "scree", + "#eee5dc", + "swamp", + "#add19e", + "tidalflat", + "#DED6CF", + "village_green", + "#cdebb0", + "vineyard", + "#aedfa3", + "wet_meadow", + "#cdebb0", + "wetland", + "#add19e", + "wood", + "#add19e", + "marsh", + "#ff0", + "#FFFFFF" + ], + "fill-antialias": true + }, + "filter": [ + "all", + [ + "in", + "subclass", + "allotments", + "bare_rock", + "beach", + "dune", + "scrub", + "farm", + "farmland", + "flowerbed", + "forest", + "garden", + "grass", + "grassland", + "golf_course", + "heath", + "meadow", + "orchard", + "plant_nursery", + "recreation_ground", + "reedbed", + "saltmarsh", + "sand", + "scree", + "swamp", + "tidalflat", + "tundra", + "village_green", + "vineyard", + "wet_meadow", + "wetland", + "wood" + ] + ], + "order": 7 + }, + { + "id": "landcover_subclass_patterns", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landcover", + "minzoom": 13, + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-opacity": [ + "match", + [ + "get", + "subclass" + ], + "beach", + 0.4, + "forest", + 0.4, + "bare_rock", + 0.3, + "scrub", + 0.6, + "garden", + 0.6, + "scree", + 0.3, + "wood", + 0.4, + 1 + ], + "fill-pattern": [ + "match", + [ + "get", + "subclass" + ], + "allotments", + "allotments", + "bare_rock", + "rock_overlay", + "beach", + "beach", + "bog", + "wetland_bog", + "scrub", + "scrub", + "flowerbed", + "flowerbed_high_zoom", + "forest", + "leaftype_unknown", + "garden", + "plant_nursery", + "mangrove", + "wetland_mangrove", + "marsh", + "wetland_marsh", + "orchard", + "orchard", + "plant_nursery", + "plant_nursery", + "reedbed", + "wetland_reed", + "saltmarsh", + "wetland_marsh", + "scree", + "scree_overlay", + "swamp", + "wetland_swamp", + "vineyard", + "vineyard", + "wet_meadow", + "wetland_marsh", + "wetland", + "wetland", + "wood", + "leaftype_unknown", + "" + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "subclass", + "allotments", + "bare_rock", + "beach", + "bog", + "dune", + "scrub", + "farm", + "farmland", + "flowerbed", + "forest", + "garden", + "grass", + "grassland", + "golf_course", + "heath", + "mangrove", + "marsh", + "meadow", + "orchard", + "park", + "plant_nursery", + "recreation_ground", + "reedbed", + "saltern", + "saltmarsh", + "sand", + "scree", + "swamp", + "village_green", + "vineyard", + "wet_meadow", + "wetland", + "wood" + ] + ], + "order": 8 + }, + { + "id": "landcover_subclass_outline", + "type": "line", + "source": "openmaptiles", + "source-layer": "landcover", + "minzoom": 15, + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": [ + "match", + [ + "get", + "subclass" + ], + "allotments", + "#B1C6A8", + "farm", + "#d1b48c", + "farmland", + "#c7c9ae", + "recreation_ground", + "#3c6640", + "#000" + ], + "line-width": [ + "match", + [ + "get", + "subclass" + ], + "recreation_ground", + 0.3, + 0.5 + ], + "line-opacity": 1 + }, + "filter": [ + "all", + [ + "in", + "subclass", + "allotments", + "farm", + "farmland", + "recreation_ground" + ] + ], + "order": 9 + }, + { + "id": "landcover_ice", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landcover", + "minzoom": 5, + "paint": { + "fill-color": "#ddecec", + "fill-antialias": false + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "ice" + ] + ], + "order": 10 + }, + { + "id": "landcover_ice_outline", + "type": "line", + "source": "openmaptiles", + "source-layer": "landcover", + "minzoom": 5, + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "#9cf", + "line-width": { + "stops": [ + [ + 5, + 1 + ], + [ + 10, + 1.5 + ] + ] + }, + "line-dasharray": { + "stops": [ + [ + 5, + [ + 1, + 0 + ] + ], + [ + 10, + [ + 4, + 2 + ] + ] + ] + } + }, + "filter": [ + "all", + [ + "in", + "class", + "ice" + ] + ], + "order": 11 + } + ] +} \ No newline at end of file diff --git a/layers/landuse/README.md b/layers/landuse/README.md new file mode 100644 index 0000000..059db20 --- /dev/null +++ b/layers/landuse/README.md @@ -0,0 +1,10 @@ +## landuse + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#landuse** + +### Mapping Diagram +![Mapping diagram for landuse](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for landuse](etl_diagram.png?raw=true) diff --git a/layers/landuse/class.sql b/layers/landuse/class.sql new file mode 100644 index 0000000..80d2052 --- /dev/null +++ b/layers/landuse/class.sql @@ -0,0 +1,10 @@ +-- Unify class names that represent the same type of feature +CREATE OR REPLACE FUNCTION landuse_unify(class text) RETURNS text LANGUAGE plpgsql +AS +$$ +BEGIN + RETURN CASE + WHEN class='grave_yard' THEN 'cemetery' + ELSE class END; +END; +$$; diff --git a/layers/landuse/etl_diagram.png b/layers/landuse/etl_diagram.png new file mode 100644 index 0000000..74be179 Binary files /dev/null and b/layers/landuse/etl_diagram.png differ diff --git a/layers/landuse/landuse.sql b/layers/landuse/landuse.sql new file mode 100644 index 0000000..70a706a --- /dev/null +++ b/layers/landuse/landuse.sql @@ -0,0 +1,379 @@ +-- ne_50m_urban_areas +-- etldoc: ne_50m_urban_areas -> ne_50m_urban_areas_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_urban_areas_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_urban_areas_gen_z5 AS +( +SELECT + NULL::bigint AS osm_id, + ST_Simplify(geometry, ZRes(7)) as geometry, + 'residential'::text AS landuse, + NULL::text AS amenity, + NULL::text AS leisure, + NULL::text AS tourism, + NULL::text AS place, + NULL::text AS waterway, + scalerank +FROM ne_50m_urban_areas + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_urban_areas_gen_z5_idx ON ne_50m_urban_areas_gen_z5 USING gist (geometry); + +-- etldoc: ne_50m_urban_areas_gen_z5 -> ne_50m_urban_areas_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_urban_areas_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_urban_areas_gen_z4 AS +( +SELECT + osm_id, + ST_Simplify(geometry, ZRes(6)) as geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway +FROM ne_50m_urban_areas_gen_z5 +WHERE scalerank <= 2 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_urban_areas_gen_z4_idx ON ne_50m_urban_areas_gen_z4 USING gist (geometry); + +-- etldoc: osm_landuse_polygon_gen_z6 -> osm_landuse_polygon_gen_z6_union +-- etldoc: osm_residential_gen_z6 -> osm_landuse_polygon_gen_z6_union +CREATE OR REPLACE VIEW osm_landuse_polygon_gen_z6_union AS +( + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z6 + WHERE landuse <> 'residential' + UNION ALL + SELECT NULL::bigint AS osm_id, + geometry, + 'residential' AS landuse, + '' AS amenity, + '' AS leisure, + '' AS tourism, + '' AS place, + '' AS waterway + FROM osm_residential_gen_z6 +); + +-- etldoc: osm_landuse_polygon_gen_z7 -> osm_landuse_polygon_gen_z7_union +-- etldoc: osm_residential_gen_z7 -> osm_landuse_polygon_gen_z7_union +CREATE OR REPLACE VIEW osm_landuse_polygon_gen_z7_union AS +( + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z7 + WHERE landuse <> 'residential' + UNION ALL + SELECT NULL::bigint AS osm_id, + geometry, + 'residential' AS landuse, + '' AS amenity, + '' AS leisure, + '' AS tourism, + '' AS place, + '' AS waterway + FROM osm_residential_gen_z7 +); + +-- etldoc: osm_landuse_polygon_gen_z8 -> osm_landuse_polygon_gen_z8_union +-- etldoc: osm_residential_gen_z8 -> osm_landuse_polygon_gen_z8_union +CREATE OR REPLACE VIEW osm_landuse_polygon_gen_z8_union AS +( + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z8 + WHERE landuse <> 'residential' + UNION ALL + SELECT NULL::bigint AS osm_id, + geometry, + 'residential' AS landuse, + '' AS amenity, + '' AS leisure, + '' AS tourism, + '' AS place, + '' AS waterway + FROM osm_residential_gen_z8 +); + +-- etldoc: osm_landuse_polygon_gen_z9 -> osm_landuse_polygon_gen_z9_union +-- etldoc: osm_residential_gen_z9 -> osm_landuse_polygon_gen_z9_union +CREATE OR REPLACE VIEW osm_landuse_polygon_gen_z9_union AS +( + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z9 + WHERE landuse <> 'residential' + UNION ALL + SELECT NULL::bigint AS osm_id, + geometry, + 'residential' AS landuse, + '' AS amenity, + '' AS leisure, + '' AS tourism, + '' AS place, + '' AS waterway + FROM osm_residential_gen_z9 +); + +-- etldoc: osm_landuse_polygon_gen_z10 -> osm_landuse_polygon_gen_z10_union +-- etldoc: osm_residential_gen_z10 -> osm_landuse_polygon_gen_z10_union +CREATE OR REPLACE VIEW osm_landuse_polygon_gen_z10_union AS +( + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z10 + WHERE landuse <> 'residential' + UNION ALL + SELECT NULL::bigint AS osm_id, + geometry, + 'residential' AS landuse, + '' AS amenity, + '' AS leisure, + '' AS tourism, + '' AS place, + '' AS waterway + FROM osm_residential_gen_z10 +); + +-- etldoc: osm_landuse_polygon_gen_z11 -> osm_landuse_polygon_gen_z11_union +-- etldoc: osm_residential_gen_z11 -> osm_landuse_polygon_gen_z11_union +CREATE OR REPLACE VIEW osm_landuse_polygon_gen_z11_union AS +( + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z11 + WHERE landuse <> 'residential' + UNION ALL + SELECT NULL::bigint AS osm_id, + geometry, + 'residential' AS landuse, + '' AS amenity, + '' AS leisure, + '' AS tourism, + '' AS place, + '' AS waterway + FROM osm_residential_gen_z11 +); + +-- etldoc: osm_landuse_polygon_gen_z12 -> osm_landuse_polygon_gen_z12_union +-- etldoc: osm_residential_gen_z12 -> osm_landuse_polygon_gen_z12_union +CREATE OR REPLACE VIEW osm_landuse_polygon_gen_z12_union AS +( + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z12 + WHERE landuse <> 'residential' + UNION ALL + SELECT NULL::bigint AS osm_id, + geometry, + 'residential' AS landuse, + '' AS amenity, + '' AS leisure, + '' AS tourism, + '' AS place, + '' AS waterway + FROM osm_residential_gen_z12 +); + +-- etldoc: layer_landuse[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_landuse | z4| z5| z6| z7| z8| z9| z10| z11| z12| z13| z14+" ] ; + +CREATE OR REPLACE FUNCTION layer_landuse(bbox geometry, zoom_level int) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + class text + ) +AS +$$ +SELECT osm_id, + geometry, + landuse_unify( + COALESCE( + NULLIF(landuse, ''), + NULLIF(amenity, ''), + NULLIF(leisure, ''), + NULLIF(tourism, ''), + NULLIF(place, ''), + NULLIF(waterway, '') + )) AS class +FROM ( + -- etldoc: ne_50m_urban_areas_gen_z4 -> layer_landuse:z4 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM ne_50m_urban_areas_gen_z4 + WHERE zoom_level = 4 + UNION ALL + -- etldoc: ne_50m_urban_areas_gen_z5 -> layer_landuse:z5 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM ne_50m_urban_areas_gen_z5 + WHERE zoom_level = 5 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z6_union -> layer_landuse:z6 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z6_union + WHERE zoom_level = 6 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z7_union -> layer_landuse:z7 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z7_union + WHERE zoom_level = 7 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z8_union -> layer_landuse:z8 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z8_union + WHERE zoom_level = 8 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z9_union -> layer_landuse:z9 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z9_union + WHERE zoom_level = 9 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z10_union -> layer_landuse:z10 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z10_union + WHERE zoom_level = 10 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z11_union -> layer_landuse:z11 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z11_union + WHERE zoom_level = 11 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z12_union -> layer_landuse:z12 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z12_union + WHERE zoom_level = 12 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z13 -> layer_landuse:z13 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z13 + WHERE zoom_level = 13 + UNION ALL + -- etldoc: osm_landuse_polygon -> layer_landuse:z14 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon + WHERE zoom_level >= 14 + ) AS zoom_levels +WHERE geometry && bbox; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/landuse/landuse.yaml b/layers/landuse/landuse.yaml new file mode 100644 index 0000000..2d6f642 --- /dev/null +++ b/layers/landuse/landuse.yaml @@ -0,0 +1,57 @@ +layer: + id: "landuse" + requires: + tables: + - ne_50m_urban_areas + description: | + Landuse is used to describe use of land by humans. At lower zoom levels this is + from Natural Earth data for residential (urban) areas and at higher zoom levels mostly OSM `landuse` tags. + buffer_size: 4 + fields: + class: + description: | + Use the **class** to assign special colors to areas. + Original value of either the + [`landuse`](http://wiki.openstreetmap.org/wiki/Key:landuse), + [`amenity`](http://wiki.openstreetmap.org/wiki/Key:amenity), + [`leisure`](http://wiki.openstreetmap.org/wiki/Key:leisure), + [`tourism`](http://wiki.openstreetmap.org/wiki/Key:tourism), + [`place`](http://wiki.openstreetmap.org/wiki/Key:place) + or [`waterway`](http://wiki.openstreetmap.org/wiki/Key:waterway) tag. + values: + - railway + - cemetery + - military + - residential + - commercial + - industrial + - garages + - retail + - bus_station + - school + - university + - kindergarten + - college + - library + - hospital + - stadium + - pitch + - playground + - track + - theme_park + - zoo + - suburb + - quarter + - neighbourhood + - dam + - quarry + datasource: + geometry_field: geometry + query: (SELECT geometry, class FROM layer_landuse(!bbox!, z(!scale_denominator!))) AS t +schema: + - ./class.sql + - ./prep_landuse.sql + - ./landuse.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/landuse/mapping.yaml b/layers/landuse/mapping.yaml new file mode 100644 index 0000000..581258c --- /dev/null +++ b/layers/landuse/mapping.yaml @@ -0,0 +1,106 @@ +generalized_tables: + # etldoc: osm_landuse_polygon_gen_z7 -> osm_landuse_polygon_gen_z6 + landuse_polygon_gen_z6: + source: landuse_polygon_gen_z7 + tolerance: ZRES6 + sql_filter: area>power(ZRES6,2) + # etldoc: osm_landuse_polygon_gen_z8 -> osm_landuse_polygon_gen_z7 + landuse_polygon_gen_z7: + source: landuse_polygon_gen_z8 + tolerance: ZRES7 + sql_filter: area>power(ZRES6,2) + # etldoc: osm_landuse_polygon_gen_z9 -> osm_landuse_polygon_gen_z8 + landuse_polygon_gen_z8: + source: landuse_polygon_gen_z9 + sql_filter: area>power(ZRES6,2) AND (landuse='residential' OR place='suburb' OR place='quarter' OR place='neighbourhood') + tolerance: ZRES8 + # etldoc: osm_landuse_polygon_gen_z10 -> osm_landuse_polygon_gen_z9 + landuse_polygon_gen_z9: + source: landuse_polygon_gen_z10 + sql_filter: area>power(ZRES7,2) + tolerance: ZRES9 + # etldoc: osm_landuse_polygon_gen_z11 -> osm_landuse_polygon_gen_z10 + landuse_polygon_gen_z10: + source: landuse_polygon_gen_z11 + sql_filter: area>power(ZRES8,2) + tolerance: ZRES10 + # etldoc: osm_landuse_polygon_gen_z12 -> osm_landuse_polygon_gen_z11 + landuse_polygon_gen_z11: + source: landuse_polygon_gen_z12 + sql_filter: area>power(ZRES9,2) + tolerance: ZRES11 + # etldoc: osm_landuse_polygon_gen_z13 -> osm_landuse_polygon_gen_z12 + landuse_polygon_gen_z12: + source: landuse_polygon_gen_z13 + sql_filter: area>power(ZRES10,2) + tolerance: ZRES12 + # etldoc: osm_landuse_polygon -> osm_landuse_polygon_gen_z13 + landuse_polygon_gen_z13: + source: landuse_polygon + sql_filter: area>power(ZRES11,2) AND ST_IsValid(geometry) + tolerance: ZRES13 + +tables: + # etldoc: imposm3 -> osm_landuse_polygon + landuse_polygon: + type: polygon + columns: + - name: osm_id + type: id + - name: geometry + type: validated_geometry + - name: landuse + key: landuse + type: string + - name: amenity + key: amenity + type: string + - name: leisure + key: leisure + type: string + - name: tourism + key: tourism + type: string + - name: place + key: place + type: string + - name: waterway + key: waterway + type: string + - name: area + type: area + mapping: + landuse: + - railway + - cemetery + - military + - quarry + # zoning + - residential + - commercial + - industrial + - garages + - retail + amenity: + - bus_station + - school + - university + - kindergarten + - college + - library + - hospital + - grave_yard + leisure: + - stadium + - pitch + - playground + - track + tourism: + - theme_park + - zoo + place: + - suburb + - quarter + - neighbourhood + waterway: + - dam diff --git a/layers/landuse/mapping_diagram.png b/layers/landuse/mapping_diagram.png new file mode 100644 index 0000000..f9a8245 Binary files /dev/null and b/layers/landuse/mapping_diagram.png differ diff --git a/layers/landuse/prep_landuse.sql b/layers/landuse/prep_landuse.sql new file mode 100644 index 0000000..e737e6b --- /dev/null +++ b/layers/landuse/prep_landuse.sql @@ -0,0 +1,176 @@ +DROP TABLE IF EXISTS cluster_zres14; +CREATE TABLE cluster_zres14 AS +( +WITH single_geom AS ( + SELECT (ST_Dump(geometry)).geom AS geometry + FROM osm_landuse_polygon + WHERE landuse='residential' + ) + SELECT ST_ClusterDBSCAN(geometry, eps := zres(14), minpoints := 1) over () AS cid, + geometry + FROM single_geom +); +CREATE INDEX ON cluster_zres14 USING gist(geometry); + + +DROP TABLE IF EXISTS cluster_zres14_union; +CREATE TABLE cluster_zres14_union AS ( +SELECT ST_Buffer( + ST_Union( + ST_Buffer( + ST_SnapToGrid(geometry, 0.01) + , zres(14), 'join=mitre' + ) + ),-zres(14), 'join=mitre' + ) AS geometry +FROM cluster_zres14 +GROUP BY cid +); +CREATE INDEX ON cluster_zres14_union USING gist(geometry); + + +DROP TABLE IF EXISTS cluster_zres12; +CREATE TABLE cluster_zres12 AS +( +WITH single_geom AS ( + SELECT (ST_Dump(geometry)).geom AS geometry + FROM osm_landuse_polygon + WHERE landuse='residential' + ) + SELECT ST_ClusterDBSCAN(geometry, eps := zres(12), minpoints := 1) over () AS cid, + geometry + FROM single_geom +); +CREATE INDEX ON cluster_zres12 USING gist(geometry); + + +DROP TABLE IF EXISTS cluster_zres12_union; +CREATE TABLE cluster_zres12_union AS +( +SELECT ST_Buffer( + ST_Union( + ST_Buffer( + ST_SnapToGrid(geometry, 1) + , zres(12), 'join=mitre' + ) + ), -zres(12), 'join=mitre' + ) AS geometry +FROM cluster_zres12 +GROUP BY cid +); +CREATE INDEX ON cluster_zres12_union USING gist(geometry); + + +DROP TABLE IF EXISTS cluster_zres9; +CREATE TABLE cluster_zres9 AS +( +WITH single_geom AS ( + SELECT (ST_Dump(geometry)).geom AS geometry + FROM osm_landuse_polygon + WHERE landuse='residential' + ) + SELECT ST_ClusterDBSCAN(geometry, eps := zres(9), minpoints := 1) over () AS cid, + geometry + FROM single_geom +); +CREATE INDEX ON cluster_zres9 USING gist(geometry); + + +DROP TABLE IF EXISTS cluster_zres9_union; +CREATE TABLE cluster_zres9_union AS +( +SELECT ST_Buffer( + ST_Union( + ST_Buffer( + ST_SnapToGrid(geometry, 1) + , zres(9), 'join=mitre' + ) + ), -zres(9), 'join=mitre' + ) AS geometry +FROM cluster_zres9 +GROUP BY cid +); +CREATE INDEX ON cluster_zres9_union USING gist(geometry); + +-- For z6 +-- etldoc: osm_landuse_polygon -> osm_residential_gen_z6 +DROP TABLE IF EXISTS osm_residential_gen_z6 CASCADE; +CREATE TABLE osm_residential_gen_z6 AS +( +SELECT ST_SimplifyVW(geometry, power(zres(6), 2)) AS geometry +FROM cluster_zres9_union +WHERE ST_Area(geometry) > power(zres(6), 2) +); +CREATE INDEX ON osm_residential_gen_z6 USING gist(geometry); + + +-- For z7 +-- etldoc: osm_landuse_polygon -> osm_residential_gen_z7 +DROP TABLE IF EXISTS osm_residential_gen_z7 CASCADE; +CREATE TABLE osm_residential_gen_z7 AS +( +SELECT ST_SimplifyVW(geometry, power(zres(7), 2)) AS geometry +FROM cluster_zres12_union +WHERE ST_Area(geometry) > power(zres(6), 2) +); +CREATE INDEX ON osm_residential_gen_z7 USING gist(geometry); + + +-- For z8 +-- etldoc: osm_landuse_polygon -> osm_residential_gen_z8 +DROP TABLE IF EXISTS osm_residential_gen_z8 CASCADE; +CREATE TABLE osm_residential_gen_z8 AS +( +SELECT ST_SimplifyVW(geometry, power(zres(8), 2)) AS geometry +FROM cluster_zres12_union +WHERE ST_Area(geometry) > power(zres(7), 2) +); +CREATE INDEX ON osm_residential_gen_z8 USING gist(geometry); + + +-- For z9 +-- etldoc: osm_landuse_polygon -> osm_residential_gen_z9 +DROP TABLE IF EXISTS osm_residential_gen_z9 CASCADE; +CREATE TABLE osm_residential_gen_z9 AS +( +SELECT ST_SimplifyVW(geometry, power(zres(9), 2)) AS geometry +FROM cluster_zres12_union +WHERE ST_Area(geometry) > power(zres(9), 2) +); +CREATE INDEX ON osm_residential_gen_z9 USING gist(geometry); + + +-- For z10 +-- etldoc: osm_landuse_polygon -> osm_residential_gen_z10 +DROP TABLE IF EXISTS osm_residential_gen_z10 CASCADE; +CREATE TABLE osm_residential_gen_z10 AS +( +SELECT ST_SimplifyVW(geometry, power(zres(10), 2)) AS geometry +FROM cluster_zres14_union +WHERE ST_Area(geometry) > power(zres(10), 2) +); +CREATE INDEX ON osm_residential_gen_z10 USING gist(geometry); + + +-- For z11 +-- etldoc: osm_landuse_polygon -> osm_residential_gen_z11 +DROP TABLE IF EXISTS osm_residential_gen_z11 CASCADE; +CREATE TABLE osm_residential_gen_z11 AS +( +SELECT ST_SimplifyVW(geometry, power(zres(11), 2)) AS geometry +FROM cluster_zres14_union +WHERE ST_Area(geometry) > power(zres(11), 2) +); +CREATE INDEX ON osm_residential_gen_z11 USING gist(geometry); + + +-- For z12 +-- etldoc: osm_landuse_polygon -> osm_residential_gen_z12 +DROP TABLE IF EXISTS osm_residential_gen_z12 CASCADE; +CREATE TABLE osm_residential_gen_z12 AS +( +SELECT ST_SimplifyVW(geometry, power(zres(12), 2)) AS geometry +FROM cluster_zres14_union +WHERE ST_Area(geometry) > power(zres(12), 2) +); +CREATE INDEX ON osm_residential_gen_z12 USING gist(geometry); diff --git a/layers/landuse/style.json b/layers/landuse/style.json new file mode 100644 index 0000000..a32e70b --- /dev/null +++ b/layers/landuse/style.json @@ -0,0 +1,369 @@ +{ + "layers": [ + { + "id": "landuse_classes", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landuse", + "minzoom": 7, + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": [ + "match", + [ + "get", + "class" + ], + "railway", + "#ebdbe8", + "residential", + "#e0dfdf", + "cemetery", + "#aacbaf", + "military", + "#fceaea", + "commercial", + "#f2dad9", + "industrial", + "#ebdbe8", + "garages", + "#dfddce", + "retail", + "#ffd6d1", + "bus_station", + "#e9e7e2", + "school", + "#ffffe5", + "university", + "#ffffe5", + "kindergarten", + "#ffffe5", + "college", + "#ffffe5", + "hospital", + "#ffffe5", + "stadium", + "#d5ffd9", + "pitch", + "#aae0cb", + "playground", + "#d5ffd9", + "track", + "#aae0cb", + "dam", + "#adadad", + "#000" + ], + "fill-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "railway", + "cemetery", + "military", + "residential", + "commercial", + "industrial", + "garages", + "retail", + "bus_station", + "school", + "university", + "kindergarten", + "college", + "hospital", + "stadium", + "pitch", + "playground", + "track", + "dam" + ], + [ + "==", + "$type", + "Polygon" + ] + ], + "order": 1 + }, + { + "id": "landuse_residential", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landuse", + "minzoom": 6, + "maxzoom": 24, + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": { + "stops": [ + [ + 7, + "#d0d0d0" + ], + [ + 11, + "#dddddd" + ], + [ + 12, + "#e0dfdf" + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "residential", + "suburbs", + "neighbourhood" + ] + ], + "order": 2 + }, + { + "id": "landuse_class_pattern", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landuse", + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": "#000000", + "fill-opacity": 1, + "fill-pattern": [ + "match", + [ + "get", + "class" + ], + "military", + "military_red_hatch", + "cemetery", + "grave_yard_generic", + "" + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "military", + "cemetery" + ] + ], + "order": 25 + }, + { + "id": "landuse_class_outline", + "type": "line", + "source": "openmaptiles", + "source-layer": "landuse", + "minzoom": 13, + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": [ + "match", + [ + "get", + "class" + ], + "railway", + "#c6b3c3", + "military", + "#ff5555", + "residential", + "#b9b9b9", + "commercial", + "#f2dad9", + "industrial", + "#c6b3c3", + "retail", + "#d99c95", + "school", + "#A6A68C", + "university", + "#A6A68C", + "kindergarten", + "#A6A68C", + "college", + "#A6A68C", + "hospital", + "#A6A68C", + "stadium", + "#7ca680", + "pitch", + "#7aaa97", + "playground", + "#3c6640", + "track", + "#7aaa96", + "theme_park", + "#660033", + "zoo", + "#660033", + "dam", + "#444444", + "#000" + ], + "line-width": [ + "match", + [ + "get", + "class" + ], + "railway", + 0.7, + "military", + 2, + "residential", + 0.5, + "commercial", + 0.5, + "industrial", + 0.5, + "retail", + 0.5, + "school", + 0.3, + "university", + 0.3, + "kindergarten", + 0.3, + "college", + 0.3, + "hospital", + 0.3, + "stadium", + 0.3, + "pitch", + 0.5, + "playground", + 0.3, + "track", + 0.5, + "theme_park", + 1, + "zoo", + 1, + "dam", + 2, + 1 + ], + "line-offset": [ + "match", + [ + "get", + "class" + ], + "military", + 1, + 0 + ], + "line-opacity": [ + "match", + [ + "get", + "class" + ], + "military", + 0.24, + 1 + ] + }, + "filter": [ + "all", + [ + "in", + "class", + "railway", + "military", + "residential", + "commercial", + "industrial", + "retail", + "school", + "university", + "kindergarten", + "college", + "hospital", + "stadium", + "pitch", + "playground", + "track", + "theme_park", + "zoo", + "dam" + ] + ], + "order": 26 + }, + { + "id": "landuse_class_themepark", + "type": "line", + "source": "openmaptiles", + "source-layer": "landuse", + "minzoom": 13, + "layout": { + "line-cap": "square", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#660033", + "line-width": { + "stops": [ + [ + 9, + 3.5 + ], + [ + 14, + 5.5 + ] + ] + }, + "line-offset": 2, + "line-opacity": { + "stops": [ + [ + 9, + 0.1 + ], + [ + 12, + 0.3 + ] + ] + } + }, + "filter": [ + "all", + [ + "in", + "class", + "theme_park", + "zoo" + ] + ], + "order": 27 + } + ] +} \ No newline at end of file diff --git a/layers/mountain_peak/README.md b/layers/mountain_peak/README.md new file mode 100644 index 0000000..1c0aebe --- /dev/null +++ b/layers/mountain_peak/README.md @@ -0,0 +1,7 @@ +## Mountain Peaks + +### Mapping Diagram +![Mapping diagram for mountain peaks](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for mountain peaks](etl_diagram.png?raw=true) diff --git a/layers/mountain_peak/etl_diagram.png b/layers/mountain_peak/etl_diagram.png new file mode 100644 index 0000000..0f5c6fd Binary files /dev/null and b/layers/mountain_peak/etl_diagram.png differ diff --git a/layers/mountain_peak/mapping.yaml b/layers/mountain_peak/mapping.yaml new file mode 100644 index 0000000..21952d1 --- /dev/null +++ b/layers/mountain_peak/mapping.yaml @@ -0,0 +1,60 @@ +tables: + + # etldoc: imposm3 -> osm_peak_point + peak_point: + type: point + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - name: name + key: name + type: string + - name: name_en + key: name:en + type: string + - name: name_de + key: name:de + type: string + - name: tags + type: hstore_tags + - name: ele + key: ele + type: string + - name: wikipedia + key: wikipedia + type: string + mapping: + natural: + - peak + - volcano + - saddle + + # etldoc: imposm3 -> osm_mountain_linestring + mountain_linestring: + type: linestring + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - name: name + key: name + type: string + - name: name_en + key: name:en + type: string + - name: name_de + key: name:de + type: string + - name: tags + type: hstore_tags + - name: wikipedia + key: wikipedia + type: string + mapping: + natural: + - ridge + - cliff + - arete diff --git a/layers/mountain_peak/mapping_diagram.png b/layers/mountain_peak/mapping_diagram.png new file mode 100644 index 0000000..9e8a3e2 Binary files /dev/null and b/layers/mountain_peak/mapping_diagram.png differ diff --git a/layers/mountain_peak/mountain_peak.sql b/layers/mountain_peak/mountain_peak.sql new file mode 100644 index 0000000..4ef027d --- /dev/null +++ b/layers/mountain_peak/mountain_peak.sql @@ -0,0 +1,120 @@ +-- etldoc: osm_peak_point -> peak_point +-- etldoc: ne_10m_admin_0_countries -> peak_point +CREATE OR REPLACE VIEW peak_point AS +( +SELECT pp.osm_id, + pp.geometry, + pp.name, + pp.name_en, + pp.name_de, + pp.tags, + pp.ele, + ne.iso_a2, + pp.wikipedia +FROM osm_peak_point pp, ne_10m_admin_0_countries ne +WHERE ST_Intersects(pp.geometry, ne.geometry) + ); + + + +-- etldoc: layer_mountain_peak[shape=record fillcolor=lightpink, +-- etldoc: style="rounded,filled", label="layer_mountain_peak | z7+ | z13+" ] ; + +CREATE OR REPLACE FUNCTION layer_mountain_peak(bbox geometry, + zoom_level integer, + pixel_width numeric) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + name text, + name_en text, + name_de text, + class text, + tags hstore, + ele int, + ele_ft int, + customary_ft int, + "rank" int + ) +AS +$$ +SELECT + -- etldoc: peak_point -> layer_mountain_peak:z7_ + osm_id, + geometry, + name, + name_en, + name_de, + tags->'natural' AS class, + tags, + ele::int, + ele_ft::int, + customary_ft, + rank::int +FROM ( + SELECT osm_id, + geometry, + NULLIF(name, '') as name, + COALESCE(NULLIF(name_en, ''), NULLIF(name, '')) AS name_en, + COALESCE(NULLIF(name_de, ''), NULLIF(name, ''), NULLIF(name_en, '')) AS name_de, + tags, + substring(ele FROM E'^(-?\\d+)(\\D|$)')::int AS ele, + round(substring(ele FROM E'^(-?\\d+)(\\D|$)')::int * 3.2808399)::int AS ele_ft, + CASE WHEN iso_a2 = 'US' THEN 1 END AS customary_ft, + row_number() OVER ( + PARTITION BY LabelGrid(geometry, 100 * pixel_width) + ORDER BY ( + (CASE WHEN ele <> '' THEN substring(ele FROM E'^(-?\\d+)(\\D|$)')::int ELSE 0 END) + + (CASE WHEN wikipedia <> '' THEN 10000 ELSE 0 END) + + (CASE WHEN name <> '' THEN 10000 ELSE 0 END) + ) DESC + )::int AS "rank" + FROM peak_point + WHERE geometry && bbox + AND ( + (ele <> '' AND ele ~ E'^-?\\d{1,4}(\\D|$)') + OR name <> '' + ) + ) AS ranked_peaks +WHERE zoom_level >= 7 + AND (rank <= 5 OR zoom_level >= 14) + +UNION ALL + +SELECT + -- etldoc: osm_mountain_linestring -> layer_mountain_peak:z13_ + osm_id, + geometry, + name, + name_en, + name_de, + tags->'natural' AS class, + tags, + NULL AS ele, + NULL AS ele_ft, + NULL AS customary_ft, + rank::int +FROM ( + SELECT osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + row_number() OVER ( + PARTITION BY LabelGrid(geometry, 100 * pixel_width) + ORDER BY ( + (CASE WHEN wikipedia <> '' THEN 10000 ELSE 0 END) + + (CASE WHEN name <> '' THEN 10000 ELSE 0 END) + ) DESC + )::int AS "rank" + FROM osm_mountain_linestring + WHERE geometry && bbox + ) AS ranked_mountain_linestring +WHERE zoom_level >= 13 +ORDER BY "rank" ASC; + +$$ LANGUAGE SQL STABLE + PARALLEL SAFE; +-- TODO: Check if the above can be made STRICT -- i.e. if pixel_width could be NULL diff --git a/layers/mountain_peak/mountain_peak.yaml b/layers/mountain_peak/mountain_peak.yaml new file mode 100644 index 0000000..2b6a3b1 --- /dev/null +++ b/layers/mountain_peak/mountain_peak.yaml @@ -0,0 +1,45 @@ +layer: + id: "mountain_peak" + requires: + tables: + - ne_10m_admin_0_countries + description: | + [Natural peaks](http://wiki.openstreetmap.org/wiki/Tag:natural%3Dpeak) + buffer_size: 64 + srs: +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over + fields: + name: The OSM [`name`](http://wiki.openstreetmap.org/wiki/Key:name) value of the peak. Language-specific values are in `name:xx`. + name_en: English name `name:en` if available, otherwise `name`. This is deprecated and will be removed in a future release in favor of `name:en`. + name_de: German name `name:de` if available, otherwise `name` or `name:en`. This is deprecated and will be removed in a future release in favor of `name:de`. + class: + description: | + Use the **class** to differentiate between natural objects. + values: + - peak + - volcano + - saddle + - ridge + - cliff + - arete + ele: Elevation (`ele`) in meters. + ele_ft: Elevation (`ele`) in feet. + customary_ft: + description: | + Value 1 for peaks in location where feet is used as customary unit (USA). + values: + - 1 + - NULL + rank: Rank of the peak within one tile (starting at 1 that is the most important peak). + datasource: + geometry_field: geometry + key_field: osm_id + key_field_as_attribute: no + srid: 900913 + query: (SELECT osm_id, geometry, name, name_en, name_de, {name_languages}, class, ele, ele_ft, customary_ft, rank FROM layer_mountain_peak(!bbox!, z(!scale_denominator!), !pixel_width!)) AS t +schema: + - ./update_peak_point.sql + - ./update_mountain_linestring.sql + - ./mountain_peak.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/mountain_peak/style.json b/layers/mountain_peak/style.json new file mode 100644 index 0000000..befc0f0 --- /dev/null +++ b/layers/mountain_peak/style.json @@ -0,0 +1,101 @@ +{ + "layers": [ + { + "id": "mountain_peak", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "mountain_peak", + "maxzoom": 16, + "filter": [ + "all", + [ + "!in", + "class", + "cliff", + "volcano" + ] + ], + "layout": { + "text-size": 10, + "icon-image": "peak", + "text-field": { + "stops": [ + [ + 6, + " " + ], + [ + 12, + "{name} {ele}m" + ] + ] + }, + "text-anchor": "top", + "text-offset": [ + 0, + 0.5 + ], + "text-max-width": 6, + "text-line-height": 1.1, + "text-font": [ + "Noto Sans Regular", + "Noto Sans Italic" + ] + }, + "paint": { + "text-color": "#6e441e", + "text-halo-color": "rgba(255, 255, 255, .8)", + "text-halo-width": 1 + }, + "order": 197 + }, + { + "id": "mountain_peak_volcano", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "mountain_peak", + "maxzoom": 16, + "filter": [ + "all", + [ + "==", + "class", + "volcano" + ] + ], + "layout": { + "text-size": 10, + "icon-image": "volcano", + "text-field": { + "stops": [ + [ + 6, + " " + ], + [ + 12, + "{name} {ele}m" + ] + ] + }, + "text-anchor": "top", + "text-offset": [ + 0, + 0.5 + ], + "text-max-width": 6, + "text-line-height": 1.1, + "text-font": [ + "Noto Sans Regular", + "Noto Sans Italic" + ] + }, + "paint": { + "text-color": "#d40000", + "text-halo-color": "rgba(255, 255, 255, .8)", + "text-halo-width": 1 + }, + "order": 198 + } + ] +} \ No newline at end of file diff --git a/layers/mountain_peak/update_mountain_linestring.sql b/layers/mountain_peak/update_mountain_linestring.sql new file mode 100644 index 0000000..4862ff8 --- /dev/null +++ b/layers/mountain_peak/update_mountain_linestring.sql @@ -0,0 +1,87 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_mountain_linestring; +DROP TRIGGER IF EXISTS trigger_store ON osm_mountain_linestring; +DROP TRIGGER IF EXISTS trigger_refresh ON mountain_linestring.updates; + +CREATE SCHEMA IF NOT EXISTS mountain_linestring; + +CREATE TABLE IF NOT EXISTS mountain_linestring.osm_ids +( + osm_id bigint PRIMARY KEY +); + +-- etldoc: osm_mountain_linestring -> osm_mountain_linestring +CREATE OR REPLACE FUNCTION update_osm_mountain_linestring(full_update boolean) RETURNS void AS +$$ + UPDATE osm_mountain_linestring + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM mountain_linestring.osm_ids)) + AND COALESCE(tags -> 'name:latin', tags -> 'name:nonlatin', tags -> 'name_int') IS NULL + AND tags != update_tags(tags, geometry) +$$ LANGUAGE SQL; + +SELECT update_osm_mountain_linestring(true); + +-- Handle updates + +CREATE OR REPLACE FUNCTION mountain_linestring.store() RETURNS trigger AS +$$ +BEGIN + INSERT INTO mountain_linestring.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS mountain_linestring.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION mountain_linestring.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO mountain_linestring.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION mountain_linestring.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh mountain_linestring'; + + -- Analyze tracking and source tables before performing update + ANALYZE mountain_linestring.osm_ids; + ANALYZE osm_mountain_linestring; + + PERFORM update_osm_mountain_linestring(false); + -- noinspection SqlWithoutWhere + DELETE FROM mountain_linestring.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM mountain_linestring.updates; + + RAISE LOG 'Refresh mountain_linestring done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE + ON osm_mountain_linestring + FOR EACH ROW +EXECUTE PROCEDURE mountain_linestring.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE + ON osm_mountain_linestring + FOR EACH STATEMENT +EXECUTE PROCEDURE mountain_linestring.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON mountain_linestring.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE mountain_linestring.refresh(); diff --git a/layers/mountain_peak/update_peak_point.sql b/layers/mountain_peak/update_peak_point.sql new file mode 100644 index 0000000..7346538 --- /dev/null +++ b/layers/mountain_peak/update_peak_point.sql @@ -0,0 +1,87 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_peak_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_peak_point; +DROP TRIGGER IF EXISTS trigger_refresh ON mountain_peak_point.updates; + +CREATE SCHEMA IF NOT EXISTS mountain_peak_point; + +CREATE TABLE IF NOT EXISTS mountain_peak_point.osm_ids +( + osm_id bigint PRIMARY KEY +); + +-- etldoc: osm_peak_point -> osm_peak_point +CREATE OR REPLACE FUNCTION update_osm_peak_point(full_update boolean) RETURNS void AS +$$ + UPDATE osm_peak_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM mountain_peak_point.osm_ids)) + AND COALESCE(tags -> 'name:latin', tags -> 'name:nonlatin', tags -> 'name_int') IS NULL + AND tags != update_tags(tags, geometry) +$$ LANGUAGE SQL; + +SELECT update_osm_peak_point(true); + +-- Handle updates + +CREATE OR REPLACE FUNCTION mountain_peak_point.store() RETURNS trigger AS +$$ +BEGIN + INSERT INTO mountain_peak_point.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS mountain_peak_point.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION mountain_peak_point.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO mountain_peak_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION mountain_peak_point.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh mountain_peak_point'; + + -- Analyze tracking and source tables before performing update + ANALYZE mountain_peak_point.osm_ids; + ANALYZE osm_peak_point; + + PERFORM update_osm_peak_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM mountain_peak_point.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM mountain_peak_point.updates; + + RAISE LOG 'Refresh mountain_peak_point done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE + ON osm_peak_point + FOR EACH ROW +EXECUTE PROCEDURE mountain_peak_point.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE + ON osm_peak_point + FOR EACH STATEMENT +EXECUTE PROCEDURE mountain_peak_point.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON mountain_peak_point.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE mountain_peak_point.refresh(); diff --git a/layers/outdoor_poi/layer.sql b/layers/outdoor_poi/layer.sql deleted file mode 100644 index 1dc0f49..0000000 --- a/layers/outdoor_poi/layer.sql +++ /dev/null @@ -1,19 +0,0 @@ -CREATE OR REPLACE FUNCTION layer_outdoor_poi(bbox geometry, zoom_level int) -RETURNS TABLE(osm_id bigint, geometry geometry, class text, subclass text, name text) AS $$ - SELECT - osm_id * 10 + 1, - geometry, - CASE - WHEN subclass = 'memorial' - THEN 'subclass' - ELSE class - END AS class, - CASE - WHEN subclass = 'memorial' - THEN NULLIF(memorial, '') - ELSE subclass - END AS subclass, - NULLIF(name, '') AS name - FROM osm_exploration_point - WHERE zoom_level >= 14 AND geometry && bbox; -$$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/outdoor_poi/mapping.yaml b/layers/outdoor_poi/mapping.yaml deleted file mode 100644 index e84e83c..0000000 --- a/layers/outdoor_poi/mapping.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# can't overlap with https://github.com/openmaptiles/openmaptiles/blob/master/layers/poi/mapping.yaml - -tables: - # etldoc: imposm3 -> osm_exploration_point - exploration_point: - type: point - fields: - - name: osm_id - type: id - - name: geometry - type: geometry - - name: class - type: mapping_key - - name: subclass - type: mapping_value - - key: name - name: name - type: string - - key: memorial - name: memorial - type: string - - key: tourism - name: tourism - type: string - mapping: - historic: - - memorial - military: - - bunker - filters: - reject: - tourism: [__any__] diff --git a/layers/outdoor_poi/outdoor_poi.yaml b/layers/outdoor_poi/outdoor_poi.yaml deleted file mode 100644 index 55c88c7..0000000 --- a/layers/outdoor_poi/outdoor_poi.yaml +++ /dev/null @@ -1,20 +0,0 @@ -layer: - id: "outdoor_poi" - description: Outdoor POI missing from OMT - buffer_size: 4 - srs: +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over - fields: - class: - subclass: - name: - datasource: - key_field: osm_id - key_field_as_attribute: no - geometry_field: geometry - srid: 900913 - query: (SELECT osm_id, geometry, class, subclass, name FROM layer_outdoor_poi(!bbox!, z(!scale_denominator!))) AS t -schema: - - ./layer.sql -datasources: - - type: imposm3 - mapping_file: ./mapping.yaml diff --git a/layers/park/README.md b/layers/park/README.md new file mode 100644 index 0000000..e724445 --- /dev/null +++ b/layers/park/README.md @@ -0,0 +1,10 @@ +## park + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#park** + +### Mapping Diagram +![Mapping diagram for park](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for park](etl_diagram.png?raw=true) diff --git a/layers/park/etl_diagram.png b/layers/park/etl_diagram.png new file mode 100644 index 0000000..3a27352 Binary files /dev/null and b/layers/park/etl_diagram.png differ diff --git a/layers/park/mapping.yaml b/layers/park/mapping.yaml new file mode 100644 index 0000000..746803c --- /dev/null +++ b/layers/park/mapping.yaml @@ -0,0 +1,103 @@ +generalized_tables: + # etldoc: osm_park_polygon_gen_z5 -> osm_park_polygon_gen_z4 + park_polygon_gen_z4: + source: park_polygon_gen_z5 + sql_filter: area>power(ZRES3,2) + tolerance: ZRES4 + + # etldoc: osm_park_polygon_gen_z6 -> osm_park_polygon_gen_z5 + park_polygon_gen_z5: + source: park_polygon_gen_z6 + sql_filter: area>power(ZRES4,2) + tolerance: ZRES5 + + # etldoc: osm_park_polygon_gen_z7 -> osm_park_polygon_gen_z6 + park_polygon_gen_z6: + source: park_polygon_gen_z7 + sql_filter: area>power(ZRES5,2) + tolerance: ZRES6 + + # etldoc: osm_park_polygon_gen_z8 -> osm_park_polygon_gen_z7 + park_polygon_gen_z7: + source: park_polygon_gen_z8 + sql_filter: area>power(ZRES6,2) + tolerance: ZRES7 + + # etldoc: osm_park_polygon_gen_z9 -> osm_park_polygon_gen_z8 + park_polygon_gen_z8: + source: park_polygon_gen_z9 + sql_filter: area>power(ZRES7,2) + tolerance: ZRES8 + + # etldoc: osm_park_polygon_gen_z10 -> osm_park_polygon_gen_z9 + park_polygon_gen_z9: + source: park_polygon_gen_z10 + sql_filter: area>power(ZRES8,2) + tolerance: ZRES9 + + # etldoc: osm_park_polygon_gen_z11 -> osm_park_polygon_gen_z10 + park_polygon_gen_z10: + source: park_polygon_gen_z11 + sql_filter: area>power(ZRES9,2) + tolerance: ZRES10 + + # etldoc: osm_park_polygon_gen_z12 -> osm_park_polygon_gen_z11 + park_polygon_gen_z11: + source: park_polygon_gen_z12 + sql_filter: area>power(ZRES10,2) + tolerance: ZRES11 + + # etldoc: osm_park_polygon_gen_z13 -> osm_park_polygon_gen_z12 + park_polygon_gen_z12: + source: park_polygon_gen_z13 + sql_filter: area>power(ZRES11,2) + tolerance: ZRES12 + + # etldoc: osm_park_polygon -> osm_park_polygon_gen_z13 + park_polygon_gen_z13: + source: park_polygon + sql_filter: area>power(ZRES12,2) AND ST_IsValid(geometry) + tolerance: ZRES13 + +tables: + + # etldoc: imposm3 -> osm_park_polygon + park_polygon: + type: polygon + _resolve_wikidata: false + columns: + - name: osm_id + type: id + - name: geometry + type: validated_geometry + - name: name + key: name + type: string + - name: name_en + key: name:en + type: string + - name: name_de + key: name:de + type: string + - name: tags + type: hstore_tags + - name: landuse + key: landuse + type: string + - name: leisure + key: leisure + type: string + - name: boundary + key: boundary + type: string + - name: protection_title + key: protection_title + type: string + - name: area + type: area + mapping: + leisure: + - nature_reserve + boundary: + - national_park + - protected_area diff --git a/layers/park/mapping_diagram.png b/layers/park/mapping_diagram.png new file mode 100644 index 0000000..72d2edb Binary files /dev/null and b/layers/park/mapping_diagram.png differ diff --git a/layers/park/park.sql b/layers/park/park.sql new file mode 100644 index 0000000..4b90696 --- /dev/null +++ b/layers/park/park.sql @@ -0,0 +1,386 @@ +-- etldoc: layer_park[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_park | z4 | z5 | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; + +CREATE OR REPLACE FUNCTION layer_park(bbox geometry, zoom_level int, pixel_width numeric) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + class text, + name text, + name_en text, + name_de text, + tags hstore, + rank int + ) +AS +$$ +SELECT osm_id, + geometry, + class, + NULLIF(name, '') AS name, + NULLIF(name_en, '') AS name_en, + NULLIF(name_de, '') AS name_de, + tags, + rank +FROM ( + SELECT osm_id, + geometry, + COALESCE( + LOWER(REPLACE(NULLIF(protection_title, ''), ' ', '_')), + NULLIF(boundary, ''), + NULLIF(leisure, '')) + AS class, + name, + name_en, + name_de, + tags, + NULL::int AS rank + FROM ( + -- etldoc: osm_park_polygon_dissolve_z4 -> layer_park:z4 + SELECT NULL::int AS osm_id, + geometry, + NULL AS name, + NULL AS name_en, + NULL AS name_de, + NULL AS tags, + NULL AS leisure, + NULL AS boundary, + NULL AS protection_title + FROM osm_park_polygon_dissolve_z4 + WHERE zoom_level = 4 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z5 -> layer_park:z5 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z5 + WHERE zoom_level = 5 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z6 -> layer_park:z6 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z6 + WHERE zoom_level = 6 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z7 -> layer_park:z7 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z7 + WHERE zoom_level = 7 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z8 -> layer_park:z8 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z8 + WHERE zoom_level = 8 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z9 -> layer_park:z9 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z9 + WHERE zoom_level = 9 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z10 -> layer_park:z10 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z10 + WHERE zoom_level = 10 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z11 -> layer_park:z11 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z11 + WHERE zoom_level = 11 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z12 -> layer_park:z12 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z12 + WHERE zoom_level = 12 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z13 -> layer_park:z13 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z13 + WHERE zoom_level = 13 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon -> layer_park:z14 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon + WHERE zoom_level >= 14 + AND geometry && bbox + ) AS park_polygon + + UNION ALL + SELECT osm_id, + geometry_point AS geometry, + COALESCE( + LOWER(REPLACE(NULLIF(protection_title, ''), ' ', '_')), + NULLIF(boundary, ''), + NULLIF(leisure, '') + ) AS class, + name, + name_en, + name_de, + tags, + row_number() OVER ( + PARTITION BY LabelGrid(geometry_point, 100 * pixel_width) + ORDER BY + (CASE WHEN boundary = 'national_park' THEN TRUE ELSE FALSE END) DESC, + (COALESCE(NULLIF(tags->'wikipedia', ''), NULLIF(tags->'wikidata', '')) IS NOT NULL) DESC, + area DESC + )::int AS "rank" + FROM ( + -- etldoc: osm_park_polygon_gen_z5 -> layer_park:z5 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z5 + WHERE zoom_level = 5 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z6 -> layer_park:z6 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z6 + WHERE zoom_level = 6 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z7 -> layer_park:z7 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z7 + WHERE zoom_level = 7 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z8 -> layer_park:z8 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z8 + WHERE zoom_level = 8 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z9 -> layer_park:z9 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z9 + WHERE zoom_level = 9 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z10 -> layer_park:z10 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z10 + WHERE zoom_level = 10 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z11 -> layer_park:z11 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z11 + WHERE zoom_level = 11 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z12 -> layer_park:z12 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z12 + WHERE zoom_level = 12 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z13 -> layer_park:z13 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z13 + WHERE zoom_level = 13 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon -> layer_park:z14 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon + WHERE zoom_level >= 14 + AND geometry_point && bbox + ) AS park_point + ) AS park_all; +$$ LANGUAGE SQL STABLE + PARALLEL SAFE; +-- TODO: Check if the above can be made STRICT -- i.e. if pixel_width could be NULL diff --git a/layers/park/park.yaml b/layers/park/park.yaml new file mode 100644 index 0000000..000155d --- /dev/null +++ b/layers/park/park.yaml @@ -0,0 +1,32 @@ +layer: + id: "park" + description: | + The park layer in OpenMapTiles contains natural and protected areas from OpenStreetMap, + such as parks tagged with [`boundary=national_park`](https://wiki.openstreetmap.org/wiki/Tag:boundary%3Dnational_park), + [`boundary=protected_area`](https://wiki.openstreetmap.org/wiki/Tag:boundary%3Dprotected_area), + or [`leisure=nature_reserve`](https://wiki.openstreetmap.org/wiki/Tag:leisure%3Dnature_reserve). + buffer_size: 4 + fields: + class: + description: | + Use the **class** to differentiate between different kinds of features in the `parks` layer. + The class for `boundary=protected_area` parks is the lower-case of the + [`protection_title`](http://wiki.openstreetmap.org/wiki/key:protection_title) + value with blanks replaced by `_`. + `national_park` is the class of `protection_title=National Park` and `boundary=national_park`. + `nature_reserve` is the class of `protection_title=Nature Reserve` and `leisure=nature_reserve`. + The class for other [`protection_title`](http://wiki.openstreetmap.org/wiki/key:protection_title) + values is similarly assigned. + name: The OSM [`name`](http://wiki.openstreetmap.org/wiki/Key:name) value of the park (point features only). Language-specific values are in `name:xx`. + name_en: English name `name:en` if available, otherwise `name` (point features only). This is deprecated and will be removed in a future release in favor of `name:en`. + name_de: German name `name:de` if available, otherwise `name` or `name:en` (point features only). This is deprecated and will be removed in a future release in favor of `name:de`. + rank: Rank of the park within one tile, starting at 1 that is the most important park (point features only). + datasource: + geometry_field: geometry + query: (SELECT geometry, class, name, name_en, name_de, {name_languages}, rank FROM layer_park(!bbox!, z(!scale_denominator!), !pixel_width!)) AS t +schema: + - ./update_park_polygon.sql + - ./park.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/park/style.json b/layers/park/style.json new file mode 100644 index 0000000..c15f90b --- /dev/null +++ b/layers/park/style.json @@ -0,0 +1,111 @@ +{ + "layers": [ + { + "id": "national_parks", + "type": "line", + "source": "openmaptiles", + "source-layer": "park", + "minzoom": 8, + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(154, 199, 136, 1)", + "line-width": { + "base": 1, + "stops": [ + [ + 8, + 1.2 + ], + [ + 9, + 1.5 + ], + [ + 10, + 3.6 + ], + [ + 24, + 3.6 + ] + ] + }, + "line-offset": 1, + "line-opacity": 0.8 + }, + "order": 20 + }, + { + "id": "national_parks_thin", + "type": "line", + "source": "openmaptiles", + "source-layer": "park", + "minzoom": 10, + "layout": { + "visibility": "none" + }, + "paint": { + "line-color": "rgba(93, 156, 76, 1)", + "line-width": 1.5 + }, + "order": 21 + }, + { + "id": "park-national", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "park", + "minzoom": 7, + "maxzoom": 12, + "layout": { + "text-font": [ + "Noto Sans Italic" + ], + "text-size": 12, + "text-field": "{name:latin}{name:nonlatin}", + "visibility": "visible", + "symbol-spacing": 150, + "text-allow-overlap": false + }, + "paint": { + "text-color": { + "stops": [ + [ + 7, + "rgba(70, 164, 70, 1)" + ], + [ + 10, + "#008000" + ] + ] + }, + "text-halo-blur": 0.1, + "text-halo-color": { + "stops": [ + [ + 7, + "rgba(241, 255, 234, 1)" + ], + [ + 10, + "rgba(208, 250, 200, 1)" + ] + ] + }, + "text-halo-width": 0.3 + }, + "filter": [ + "all", + [ + "<=", + "rank", + 2 + ] + ], + "order": 195 + } + ] +} \ No newline at end of file diff --git a/layers/park/update_park_polygon.sql b/layers/park/update_park_polygon.sql new file mode 100644 index 0000000..e0b8b97 --- /dev/null +++ b/layers/park/update_park_polygon.sql @@ -0,0 +1,260 @@ +ALTER TABLE osm_park_polygon + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z13 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z12 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z11 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z10 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z9 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z8 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z7 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z6 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z5 + ADD COLUMN IF NOT EXISTS geometry_point geometry; + +-- etldoc: osm_park_polygon_gen_z4 -> osm_park_polygon_dissolve_z4 +DROP MATERIALIZED VIEW IF EXISTS osm_park_polygon_dissolve_z4 CASCADE; +CREATE MATERIALIZED VIEW osm_park_polygon_dissolve_z4 AS +( + SELECT min(osm_id) AS osm_id, + ST_Union(geometry) AS geometry, + boundary + FROM ( + SELECT ST_ClusterDBSCAN(geometry, 0, 1) OVER() AS cluster, + osm_id, + geometry, + boundary + FROM osm_park_polygon_gen_z4 + ) park_cluster + GROUP BY boundary, cluster +); +CREATE UNIQUE INDEX IF NOT EXISTS osm_park_polygon_dissolve_idx ON osm_park_polygon_dissolve_z4 (osm_id); + +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z13; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z12; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z11; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z10; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z9; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z8; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z7; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z6; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z5; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z4; +DROP TRIGGER IF EXISTS trigger_flag ON osm_park_polygon; +DROP TRIGGER IF EXISTS trigger_refresh ON park_polygon.updates; + +-- etldoc: osm_park_polygon -> osm_park_polygon +-- etldoc: osm_park_polygon_gen_z13 -> osm_park_polygon_gen_z13 +-- etldoc: osm_park_polygon_gen_z12 -> osm_park_polygon_gen_z12 +-- etldoc: osm_park_polygon_gen_z11 -> osm_park_polygon_gen_z11 +-- etldoc: osm_park_polygon_gen_z10 -> osm_park_polygon_gen_z10 +-- etldoc: osm_park_polygon_gen_z9 -> osm_park_polygon_gen_z9 +-- etldoc: osm_park_polygon_gen_z8 -> osm_park_polygon_gen_z8 +-- etldoc: osm_park_polygon_gen_z7 -> osm_park_polygon_gen_z7 +-- etldoc: osm_park_polygon_gen_z6 -> osm_park_polygon_gen_z6 +-- etldoc: osm_park_polygon_gen_z5 -> osm_park_polygon_gen_z5 +-- etldoc: osm_park_polygon_gen_z4 -> osm_park_polygon_gen_z4 +CREATE OR REPLACE FUNCTION update_osm_park_polygon() RETURNS void AS +$$ +BEGIN + UPDATE osm_park_polygon + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_park_polygon_gen_z13 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_park_polygon_gen_z12 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_park_polygon_gen_z11 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_park_polygon_gen_z10 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_park_polygon_gen_z9 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_park_polygon_gen_z8 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_park_polygon_gen_z7 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_park_polygon_gen_z6 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + UPDATE osm_park_polygon_gen_z5 + SET tags = update_tags(tags, geometry), + geometry_point = ST_PointOnSurface(geometry); + + REFRESH MATERIALIZED VIEW CONCURRENTLY osm_park_polygon_dissolve_z4; +END; +$$ LANGUAGE plpgsql; + +SELECT update_osm_park_polygon(); +CREATE INDEX IF NOT EXISTS osm_park_polygon_point_geom_idx ON osm_park_polygon USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z13_point_geom_idx ON osm_park_polygon_gen_z13 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z12_point_geom_idx ON osm_park_polygon_gen_z12 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z11_point_geom_idx ON osm_park_polygon_gen_z11 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z10_point_geom_idx ON osm_park_polygon_gen_z10 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z9_point_geom_idx ON osm_park_polygon_gen_z9 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z8_point_geom_idx ON osm_park_polygon_gen_z8 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z7_point_geom_idx ON osm_park_polygon_gen_z7 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z6_point_geom_idx ON osm_park_polygon_gen_z6 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z5_point_geom_idx ON osm_park_polygon_gen_z5 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z4_polygon_geom_idx ON osm_park_polygon_gen_z4 USING gist (geometry); +CREATE INDEX IF NOT EXISTS osm_park_polygon_dissolve_z4_polygon_geom_idx ON osm_park_polygon_dissolve_z4 USING gist (geometry); + +CREATE SCHEMA IF NOT EXISTS park_polygon; + +CREATE TABLE IF NOT EXISTS park_polygon.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); + +CREATE OR REPLACE FUNCTION park_polygon.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO park_polygon.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION park_polygon.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh park_polygon'; + + -- Analyze tracking and source tables before performing update + ANALYZE osm_park_polygon_gen_z4; + REFRESH MATERIALIZED VIEW osm_park_polygon_dissolve_z4; + + -- noinspection SqlWithoutWhere + DELETE FROM park_polygon.updates; + + RAISE LOG 'Refresh park_polygon done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION update_osm_park_polygon_row() + RETURNS trigger +AS +$$ +BEGIN + NEW.tags = update_tags(NEW.tags, NEW.geometry); + NEW.geometry_point = ST_PointOnSurface(NEW.geometry); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION update_osm_park_dissolved_polygon_row() + RETURNS trigger +AS +$$ +BEGIN + NEW.tags = update_tags(NEW.tags, NEW.geometry); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_park_polygon + FOR EACH ROW +EXECUTE PROCEDURE update_osm_park_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z13 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_park_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z12 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_park_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z11 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_park_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z10 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_park_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z9 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_park_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z8 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_park_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z7 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_park_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z6 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_park_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z5 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_park_polygon_row(); + +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z4 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_park_dissolved_polygon_row(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE + ON osm_park_polygon + FOR EACH STATEMENT +EXECUTE PROCEDURE park_polygon.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON park_polygon.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE park_polygon.refresh(); diff --git a/layers/place/README.md b/layers/place/README.md new file mode 100644 index 0000000..cf663a9 --- /dev/null +++ b/layers/place/README.md @@ -0,0 +1,10 @@ +## place + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#place** + +### Mapping Diagram +![Mapping diagram for place](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for place](etl_diagram.png?raw=true) diff --git a/layers/place/area_rank.sql b/layers/place/area_rank.sql new file mode 100644 index 0000000..d9af8e4 --- /dev/null +++ b/layers/place/area_rank.sql @@ -0,0 +1,14 @@ +CREATE OR REPLACE FUNCTION area_rank(area real) RETURNS int AS +$$ +SELECT CASE + WHEN area > 640000000 THEN 1 + WHEN area > 160000000 THEN 2 + WHEN area > 40000000 THEN 3 + WHEN area > 15000000 THEN 4 + WHEN area > 10000000 THEN 5 + WHEN area > 0 THEN 6 + ELSE 7 + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; diff --git a/layers/place/capital.sql b/layers/place/capital.sql new file mode 100644 index 0000000..1122109 --- /dev/null +++ b/layers/place/capital.sql @@ -0,0 +1,10 @@ +CREATE OR REPLACE FUNCTION normalize_capital_level(capital text) + RETURNS int AS +$$ +SELECT CASE + WHEN capital = 'yes' THEN 2 + WHEN capital IN ('2', '3', '4', '5', '6') THEN capital::int + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; diff --git a/layers/place/city.sql b/layers/place/city.sql new file mode 100644 index 0000000..41438d5 --- /dev/null +++ b/layers/place/city.sql @@ -0,0 +1,79 @@ +-- etldoc: layer_city[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_city | z2-z14+" ] ; + +-- etldoc: osm_city_point -> layer_city:z2_14 +CREATE OR REPLACE FUNCTION layer_city(bbox geometry, zoom_level int, pixel_width numeric) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + name text, + name_en text, + name_de text, + tags hstore, + place city_place, + "rank" int, + capital int + ) +AS +$$ +SELECT * +FROM ( + SELECT osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + place, + "rank", + normalize_capital_level(capital) AS capital + FROM osm_city_point + WHERE geometry && bbox + AND ((zoom_level = 2 AND "rank" = 1) + OR (zoom_level BETWEEN 3 AND 7 AND "rank" <= zoom_level + 1) + ) + UNION ALL + SELECT osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + place, + COALESCE("rank", gridrank + 10), + normalize_capital_level(capital) AS capital + FROM ( + SELECT osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + place, + "rank", + capital, + row_number() OVER ( + PARTITION BY LabelGrid(geometry, 128 * pixel_width) + ORDER BY "rank" ASC NULLS LAST, + place ASC NULLS LAST, + population DESC NULLS LAST, + length(name) ASC + )::int AS gridrank + FROM osm_city_point + WHERE geometry && bbox + AND ((zoom_level = 7 AND place <= 'town'::city_place + OR (zoom_level BETWEEN 8 AND 10 AND place <= 'village'::city_place) + OR (zoom_level BETWEEN 11 AND 13 AND place <= 'suburb'::city_place) + OR (zoom_level >= 14) + )) + ) AS ranked_places + WHERE (zoom_level BETWEEN 7 AND 8 AND (gridrank <= 4 OR "rank" IS NOT NULL)) + OR (zoom_level = 9 AND (gridrank <= 8 OR "rank" IS NOT NULL)) + OR (zoom_level = 10 AND (gridrank <= 12 OR "rank" IS NOT NULL)) + OR (zoom_level BETWEEN 11 AND 12 AND (gridrank <= 14 OR "rank" IS NOT NULL)) + OR (zoom_level >= 13) + ) AS city_all; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/place/etl_diagram.png b/layers/place/etl_diagram.png new file mode 100644 index 0000000..8100ce5 Binary files /dev/null and b/layers/place/etl_diagram.png differ diff --git a/layers/place/mapping.yaml b/layers/place/mapping.yaml new file mode 100644 index 0000000..ff007b1 --- /dev/null +++ b/layers/place/mapping.yaml @@ -0,0 +1,184 @@ +name_field: &name + name: name + key: name + type: string +name_en_field: &name_en + name: name_en + key: name:en + type: string +name_de_field: &name_de + name: name_de + key: name:de + type: string +rank_field: &rank + name: rank + key: rank + type: integer + +tables: + + # etldoc: imposm3 -> osm_continent_point + continent_point: + type: point + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - *name + - *name_en + - *name_de + - name: tags + type: hstore_tags + filters: + require: + name: ["__any__"] + mapping: + place: + - continent + + # etldoc: imposm3 -> osm_country_point + country_point: + type: point + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - *name + - *name_en + - *name_de + - *rank + - name: country_code_iso3166_1_alpha_2 + key: country_code_iso3166_1_alpha_2 + type: string + - name: iso3166_1_alpha_2 + key: ISO3166-1:alpha2 + type: string + - name: iso3166_1 + key: ISO3166-1 + type: string + - name: tags + type: hstore_tags + filters: + require: + name: ["__any__"] + mapping: + place: + - country + + # etldoc: imposm3 -> osm_island_polygon + island_polygon: + type: polygon + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - name: area + type: area + - *name + - *name_en + - *name_de + - name: tags + type: hstore_tags + - *rank + filters: + require: + name: ["__any__"] + mapping: + place: + - island + + # etldoc: imposm3 -> osm_island_point + island_point: + type: point + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - *name + - *name_en + - *name_de + - name: tags + type: hstore_tags + - *rank + filters: + require: + name: ["__any__"] + mapping: + place: + - island + + # etldoc: imposm3 -> osm_state_point + state_point: + type: point + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - *name + - *name_en + - *name_de + - name: tags + type: hstore_tags + - name: place + key: place + type: string + - name: is_in_country + key: is_in:country + type: string + - name: is_in_country_code + key: is_in:country_code + type: string + - name: ref + key: ref + type: string + - *rank + filters: + require: + name: ["__any__"] + mapping: + place: + - state + - province + + # etldoc: imposm3 -> osm_city_point + city_point: + type: point + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - *name + - *name_en + - *name_de + - name: tags + type: hstore_tags + - name: place + key: place + type: string + - key: population + name: population + type: integer + - key: capital + name: capital + type: string + - *rank + filters: + require: + name: ["__any__"] + mapping: + place: + - city + - town + - village + - hamlet + - borough + - suburb + - quarter + - neighbourhood + - isolated_dwelling diff --git a/layers/place/mapping_diagram.png b/layers/place/mapping_diagram.png new file mode 100644 index 0000000..48606cf Binary files /dev/null and b/layers/place/mapping_diagram.png differ diff --git a/layers/place/place.sql b/layers/place/place.sql new file mode 100644 index 0000000..7c6fa0e --- /dev/null +++ b/layers/place/place.sql @@ -0,0 +1,165 @@ +-- etldoc: layer_place[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_place | z0-3| z4-7| z8-11| z12-z14+" ] ; + +CREATE OR REPLACE FUNCTION layer_place(bbox geometry, zoom_level int, pixel_width numeric) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + name text, + name_en text, + name_de text, + tags hstore, + class text, + "rank" int, + capital int, + iso_a2 text + ) +AS +$$ +SELECT * +FROM ( + SELECT + -- etldoc: osm_continent_point -> layer_place:z0_3 + osm_id * 10 AS osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + 'continent' AS class, + 1 AS "rank", + NULL::int AS capital, + NULL::text AS iso_a2 + FROM osm_continent_point + WHERE geometry && bbox + AND zoom_level < 4 + + UNION ALL + + SELECT + -- etldoc: osm_country_point -> layer_place:z0_3 + -- etldoc: osm_country_point -> layer_place:z4_7 + -- etldoc: osm_country_point -> layer_place:z8_11 + -- etldoc: osm_country_point -> layer_place:z12_14 + osm_id * 10 AS osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + 'country' AS class, + "rank", + NULL::int AS capital, + iso3166_1_alpha_2 AS iso_a2 + FROM osm_country_point + WHERE geometry && bbox + AND "rank" <= zoom_level + 1 + AND name <> '' + + UNION ALL + + SELECT + -- etldoc: osm_state_point -> layer_place:z0_3 + -- etldoc: osm_state_point -> layer_place:z4_7 + -- etldoc: osm_state_point -> layer_place:z8_11 + -- etldoc: osm_state_point -> layer_place:z12_14 + osm_id * 10 AS osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + place::text AS class, + "rank", + NULL::int AS capital, + NULL::text AS iso_a2 + FROM osm_state_point + WHERE geometry && bbox + AND name <> '' + AND zoom_level > 1 + + UNION ALL + + SELECT + -- etldoc: osm_island_point -> layer_place:z12_14 + osm_id * 10 AS osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + 'island' AS class, + 7 AS "rank", + NULL::int AS capital, + NULL::text AS iso_a2 + FROM osm_island_point + WHERE zoom_level >= 12 + AND geometry && bbox + + UNION ALL + + SELECT + -- etldoc: osm_island_polygon -> layer_place:z8_11 + -- etldoc: osm_island_polygon -> layer_place:z12_14 + osm_id * 10 AS osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + 'island' AS class, + area_rank(area) AS "rank", + NULL::int AS capital, + NULL::text AS iso_a2 + FROM osm_island_polygon + WHERE geometry && bbox + AND ((zoom_level = 8 AND area_rank(area) <= 3) + OR (zoom_level = 9 AND area_rank(area) <= 4) + OR (zoom_level >= 10)) + + UNION ALL + + SELECT + -- etldoc: osm_boundary_polygon -> layer_place:z6_11 + -- etldoc: osm_boundary_polygon -> layer_place:z12_14 + osm_id * 10 AS osm_id, + geometry_point, + name, + NULL::text AS name_en, -- deprecated + NULL::text AS name_de, -- deprecated + tags, + 'aboriginal_lands' AS class, + area_rank(area) AS "rank", + NULL::int AS capital, + NULL::text AS iso_a2 + FROM osm_boundary_polygon + WHERE geometry_point && bbox + AND ((zoom_level = 6 AND area_rank(area) <= 1) + OR (zoom_level = 7 AND area_rank(area) <= 2) + OR (zoom_level = 8 AND area_rank(area) <= 3) + OR (zoom_level = 9 AND area_rank(area) <= 4) + OR (zoom_level >= 10)) + UNION ALL + + SELECT + -- etldoc: layer_city -> layer_place:z0_3 + -- etldoc: layer_city -> layer_place:z4_7 + -- etldoc: layer_city -> layer_place:z8_11 + -- etldoc: layer_city -> layer_place:z12_14 + osm_id * 10 AS osm_id, + geometry, + name, + name_en, + name_de, + tags, + place::text AS class, + "rank", + capital, + NULL::text AS iso_a2 + FROM layer_city(bbox, zoom_level, pixel_width) + ORDER BY "rank" ASC + ) AS place_all +$$ LANGUAGE SQL STABLE + PARALLEL SAFE; +-- TODO: Check if the above can be made STRICT -- i.e. if pixel_width could be NULL diff --git a/layers/place/place.yaml b/layers/place/place.yaml new file mode 100644 index 0000000..a81c9a1 --- /dev/null +++ b/layers/place/place.yaml @@ -0,0 +1,93 @@ +layer: + id: "place" + requires: + tables: + - ne_10m_admin_1_states_provinces + - ne_10m_admin_0_countries + - ne_10m_populated_places + layers: + - boundary + description: | + The place layer consists out of [countries](http://wiki.openstreetmap.org/wiki/Tag:place%3Dcountry), + [states](http://wiki.openstreetmap.org/wiki/Tag:place%3Dstate), [cities](http://wiki.openstreetmap.org/wiki/Key:place) + and [islands](https://wiki.openstreetmap.org/wiki/Tag:place%3Disland). + Apart from the roads this is also one of the more important layers to create a beautiful map. + We suggest you use different font styles and sizes to create a text hierarchy. + fields: + name: The OSM [`name`](http://wiki.openstreetmap.org/wiki/Key:name) value of the place. Language-specific values are in `name:xx`. + name_en: English name `name:en` if available, otherwise `name`. This is deprecated and will be removed in a future release in favor of `name:en`. + name_de: German name `name:de` if available, otherwise `name` or `name:en`. This is deprecated and will be removed in a future release in favor of `name:de`. + capital: + description: | + The **capital** field marks the + [`admin_level`](http://wiki.openstreetmap.org/wiki/Tag:boundary%3Dadministrative#admin_level) + of the boundary the place is a capital of. + values: [2, 3, 4, 5, 6] + class: + description: | + Original value of the + [`place`](http://wiki.openstreetmap.org/wiki/Key:place) tag. + Distinguish between continents, countries, states, islands and + places like settlements or smaller entities. + Use **class** to separately style the different places and build + a text hierarchy according to their importance. For places derived + from boundaries, the original value of the + [`boundary`](http://wiki.openstreetmap.org/wiki/Key:boundary) tag. + values: + - continent + - country + - state + - province + - city + - town + - village + - hamlet + - borough + - suburb + - quarter + - neighbourhood + - isolated_dwelling + - island + - aboriginal_lands + iso_a2: + description: | + Two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Available only for `class=country`. + Original value of the + [`country_code_iso3166_1_alpha_2`](http://wiki.openstreetmap.org/wiki/Tag:place%3Dcountry) tag. + rank: + description: | + Countries, states and the most important cities all have a + **rank** to boost their importance on the map. + The **rank** field for countries and states ranges from + `1` to `6` while the **rank** field for cities ranges from + `1` to `10` for the most important cities + and continues from `10` serially based on the + local importance of the city (derived from population and city class). + You can use the **rank** to limit density of labels or improve + the text hierarchy. + The rank value is a combination of the Natural Earth + `scalerank`, `labelrank` and `datarank` values for countries + and states and for cities consists out of a shifted + Natural Earth `scalerank` combined with a local rank + within a grid for cities that do not have a Natural Earth `scalerank`. + buffer_size: 256 + datasource: + geometry_field: geometry + key_field: osm_id + key_field_as_attribute: no + query: (SELECT osm_id, geometry, name, name_en, name_de, {name_languages}, class, rank, capital, iso_a2 FROM layer_place(!bbox!, z(!scale_denominator!), !pixel_width!)) AS t +schema: + - ./types.sql + - ./capital.sql + - ./city.sql + - ./area_rank.sql + - ./update_continent_point.sql + - ./update_country_point.sql + - ./update_island_polygon.sql + - ./update_island_point.sql + - ./update_state_point.sql + - ./update_city_point.sql + - ./place.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/place/style.json b/layers/place/style.json new file mode 100644 index 0000000..3ee5457 --- /dev/null +++ b/layers/place/style.json @@ -0,0 +1,662 @@ +{ + "layers": [ + { + "id": "place_other", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "minzoom": 8, + "layout": { + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "base": 1.2, + "stops": [ + [ + 11, + 10 + ], + [ + 14, + 14 + ], + [ + 18, + 16 + ] + ] + }, + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "symbol-spacing": 150, + "text-max-width": 10, + "text-transform": "none" + }, + "paint": { + "text-color": { + "stops": [ + [ + 12.5, + "#222222" + ], + [ + 12.6, + "#777777" + ] + ] + }, + "text-halo-blur": 0, + "text-halo-color": { + "stops": [ + [ + 11, + "rgba(255,255,255,0.6)" + ], + [ + 13, + "#ffffff" + ] + ] + }, + "text-halo-width": { + "stops": [ + [ + 8, + 0.8 + ], + [ + 13, + 1.5 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "hamlet", + "island", + "islet", + "neighbourhood", + "suburb", + "borough" + ] + ], + "order": 193 + }, + { + "id": "place_village", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "minzoom": 8, + "layout": { + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "base": 1.2, + "stops": [ + [ + 10, + 10 + ], + [ + 15, + 16 + ] + ] + }, + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-max-width": 8 + }, + "paint": { + "text-color": "#333", + "text-halo-color": "rgba(255,255,255,0.8)", + "text-halo-width": 1.2 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "village" + ] + ], + "order": 199 + }, + { + "id": "place_town", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "minzoom": 6, + "layout": { + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "base": 1.2, + "stops": [ + [ + 7, + 10 + ], + [ + 11, + 13 + ] + ] + }, + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "bottom", + "text-offset": [ + 0, + 0 + ], + "text-max-width": 8 + }, + "paint": { + "text-color": "#333", + "text-halo-color": "rgba(255,255,255,0.8)", + "text-halo-width": 1.2 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "town" + ] + ], + "order": 200 + }, + { + "id": "place_state", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "minzoom": 4, + "maxzoom": 12, + "layout": { + "text-font": [ + "Noto Sans Regular", + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 3, + 10 + ], + [ + 6, + 14 + ] + ] + }, + "text-field": "{name:latin}", + "visibility": "visible", + "text-padding": 2, + "text-transform": "none", + "text-letter-spacing": 0 + }, + "paint": { + "text-color": "#7e587d", + "text-halo-color": "rgba(255,255,255,0.7)", + "text-halo-width": 0.8 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "state" + ], + [ + "<", + "rank", + 3 + ] + ], + "order": 201 + }, + { + "id": "place_city", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "minzoom": 4, + "maxzoom": 14, + "layout": { + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "base": 1.2, + "stops": [ + [ + 4, + 12 + ], + [ + 15, + 18 + ] + ] + }, + "icon-image": { + "stops": [ + [ + 4, + "place-6" + ], + [ + 7, + " " + ] + ] + }, + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "icon-offset": [ + 0, + 3 + ], + "text-anchor": "bottom", + "text-offset": [ + 0, + 0 + ], + "icon-optional": false, + "text-max-width": 8, + "icon-allow-overlap": true + }, + "paint": { + "text-color": { + "stops": [ + [ + 6, + "rgba(88, 88, 88, 1)" + ], + [ + 14, + "rgba(32, 32, 32, 1)" + ] + ] + }, + "text-halo-color": "rgba(255,255,255,0.8)", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "city" + ], + [ + "!=", + "rank", + 1 + ] + ], + "order": 202 + }, + { + "id": "place_capital", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "minzoom": 3, + "maxzoom": 15, + "layout": { + "icon-size": 1, + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "base": 1.2, + "stops": [ + [ + 4, + 11 + ], + [ + 12, + 16 + ] + ] + }, + "icon-image": { + "stops": [ + [ + 6, + "place-capital-8" + ], + [ + 8, + "" + ] + ] + }, + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "icon-offset": [ + 0, + 3 + ], + "text-anchor": "bottom", + "text-offset": [ + 0, + 0 + ], + "icon-optional": false, + "text-max-width": 8, + "icon-allow-overlap": true + }, + "paint": { + "text-color": { + "stops": [ + [ + 6, + "rgba(73, 73, 73, 1)" + ], + [ + 14, + "rgba(32, 32, 32, 1)" + ] + ] + }, + "text-halo-color": "rgba(255,255,255,0.8)", + "text-halo-width": 1.2 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "city" + ], + [ + "in", + "capital", + 1, + 2 + ] + ], + "order": 203 + }, + { + "id": "country_other", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "minzoom": 4, + "maxzoom": 15, + "layout": { + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "stops": [ + [ + 3, + 11 + ], + [ + 5, + 13 + ], + [ + 7, + 20 + ] + ] + }, + "text-field": "{name:latin}", + "visibility": "visible", + "text-max-width": 6.25, + "text-transform": "none" + }, + "paint": { + "text-color": "rgba(131, 81, 130, 1)", + "text-halo-blur": 1, + "text-halo-color": "rgba(255,255,255,0.8)", + "text-halo-width": 0.8 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "country" + ], + [ + "!has", + "iso_a2" + ] + ], + "order": 204 + }, + { + "id": "country_3", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "minzoom": 5, + "maxzoom": 12, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 3, + 11 + ], + [ + 5, + 13 + ], + [ + 7, + 17 + ] + ] + }, + "text-field": "{name:latin}", + "visibility": "visible", + "text-max-width": 6.25, + "text-transform": "none" + }, + "paint": { + "text-color": { + "stops": [ + [ + 3, + "rgba(108, 78, 107, 1)" + ], + [ + 10, + "rgba(57, 37, 73, 1)" + ] + ] + }, + "text-halo-blur": 1, + "text-halo-color": "rgba(255,255,255,0.8)", + "text-halo-width": 0.8 + }, + "metadata": {}, + "filter": [ + "all", + [ + ">=", + "rank", + 3 + ], + [ + "==", + "class", + "country" + ], + [ + "has", + "iso_a2" + ] + ], + "order": 205 + }, + { + "id": "country_2", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "minzoom": 2, + "maxzoom": 12, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 3, + 11 + ], + [ + 5, + 14 + ], + [ + 7, + 19 + ] + ] + }, + "text-field": "{name:latin}", + "visibility": "visible", + "text-max-width": 6.25, + "text-transform": "none" + }, + "paint": { + "text-color": { + "stops": [ + [ + 3, + "rgba(108, 78, 107, 1)" + ], + [ + 10, + "rgba(57, 37, 73, 1)" + ] + ] + }, + "text-halo-blur": 1, + "text-halo-color": "rgba(255,255,255,0.8)", + "text-halo-width": 0.8 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "rank", + 2 + ], + [ + "==", + "class", + "country" + ], + [ + "has", + "iso_a2" + ] + ], + "order": 206 + }, + { + "id": "country_1", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "minzoom": 2, + "maxzoom": 12, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 3, + 11 + ], + [ + 5, + 14 + ], + [ + 7, + 19 + ] + ] + }, + "text-field": "{name:latin}", + "visibility": "visible", + "text-max-width": 6.25, + "text-transform": "none" + }, + "paint": { + "text-color": { + "stops": [ + [ + 2, + "rgba(108, 78, 107, 1)" + ], + [ + 10, + "rgba(57, 37, 73, 1)" + ] + ] + }, + "text-halo-blur": 1, + "text-halo-color": "rgba(255,255,255,0.8)", + "text-halo-width": 0.8 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "rank", + 1 + ], + [ + "==", + "class", + "country" + ], + [ + "has", + "iso_a2" + ] + ], + "order": 207 + } + ] +} \ No newline at end of file diff --git a/layers/place/types.sql b/layers/place/types.sql new file mode 100644 index 0000000..2fac395 --- /dev/null +++ b/layers/place/types.sql @@ -0,0 +1,12 @@ +DO +$$ + BEGIN + PERFORM 'city_place'::regtype; + EXCEPTION + WHEN undefined_object THEN + CREATE TYPE city_place AS enum ('city', 'town', 'village', 'hamlet', 'borough', 'suburb', 'quarter', 'neighbourhood', 'isolated_dwelling'); + END +$$; + +ALTER TABLE osm_city_point + ALTER COLUMN place TYPE city_place USING place::city_place; diff --git a/layers/place/update_city_point.sql b/layers/place/update_city_point.sql new file mode 100644 index 0000000..ef83112 --- /dev/null +++ b/layers/place/update_city_point.sql @@ -0,0 +1,115 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_city_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_city_point; +DROP TRIGGER IF EXISTS trigger_refresh ON place_city.updates; + +CREATE EXTENSION IF NOT EXISTS unaccent; + +CREATE SCHEMA IF NOT EXISTS place_city; + +CREATE TABLE IF NOT EXISTS place_city.osm_ids +( + osm_id bigint PRIMARY KEY +); + +CREATE OR REPLACE FUNCTION update_osm_city_point(full_update boolean) RETURNS void AS +$$ + -- etldoc: ne_10m_populated_places -> osm_city_point + -- etldoc: osm_city_point -> osm_city_point + + WITH important_city_point AS ( + SELECT osm.osm_id, ne.scalerank + FROM osm_city_point AS osm + -- Clear OSM key:rank ( https://github.com/openmaptiles/openmaptiles/issues/108 ) + LEFT JOIN ne_10m_populated_places AS ne ON + ( + (osm.tags ? 'wikidata' AND osm.tags->'wikidata' = ne.wikidataid) OR + lower(osm.name) IN (lower(ne.name), lower(ne.namealt), lower(ne.meganame), lower(ne.name_en), lower(ne.nameascii)) OR + lower(osm.name_en) IN (lower(ne.name), lower(ne.namealt), lower(ne.meganame), lower(ne.name_en), lower(ne.nameascii)) OR + ne.name = unaccent(osm.name) + ) + AND osm.place IN ('city', 'town', 'village') + AND ST_DWithin(ne.geometry, osm.geometry, 50000) + ) + UPDATE osm_city_point AS osm + -- Move scalerank to range 1 to 10 and merge scalerank 5 with 6 since not enough cities + -- are in the scalerank 5 bucket + SET "rank" = CASE WHEN scalerank <= 5 THEN scalerank + 1 ELSE scalerank END + FROM important_city_point AS ne + WHERE (full_update OR osm.osm_id IN (SELECT osm_id FROM place_city.osm_ids)) + AND rank IS DISTINCT FROM CASE WHEN scalerank <= 5 THEN scalerank + 1 ELSE scalerank END + AND osm.osm_id = ne.osm_id; + + UPDATE osm_city_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_city.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); + +$$ LANGUAGE SQL; + +SELECT update_osm_city_point(true); + +-- Handle updates + +CREATE OR REPLACE FUNCTION place_city.store() RETURNS trigger AS +$$ +BEGIN + INSERT INTO place_city.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS place_city.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION place_city.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO place_city.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION place_city.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh place_city rank'; + + -- Analyze tracking and source tables before performing update + ANALYZE place_city.osm_ids; + ANALYZE osm_city_point; + + PERFORM update_osm_city_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM place_city.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM place_city.updates; + + RAISE LOG 'Refresh place_city done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE + ON osm_city_point + FOR EACH ROW +EXECUTE PROCEDURE place_city.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE + ON osm_city_point + FOR EACH STATEMENT +EXECUTE PROCEDURE place_city.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON place_city.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE place_city.refresh(); diff --git a/layers/place/update_continent_point.sql b/layers/place/update_continent_point.sql new file mode 100644 index 0000000..3d92063 --- /dev/null +++ b/layers/place/update_continent_point.sql @@ -0,0 +1,87 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_continent_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_continent_point; +DROP TRIGGER IF EXISTS trigger_refresh ON place_continent_point.updates; + +CREATE SCHEMA IF NOT EXISTS place_continent_point; + +CREATE TABLE IF NOT EXISTS place_continent_point.osm_ids +( + osm_id bigint PRIMARY KEY +); + +-- etldoc: osm_continent_point -> osm_continent_point +CREATE OR REPLACE FUNCTION update_osm_continent_point(full_update boolean) RETURNS void AS +$$ + UPDATE osm_continent_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_continent_point.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); +$$ LANGUAGE SQL; + +SELECT update_osm_continent_point(true); + +-- Handle updates + +CREATE OR REPLACE FUNCTION place_continent_point.store() RETURNS trigger AS +$$ +BEGIN + INSERT INTO place_continent_point.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS place_continent_point.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION place_continent_point.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO place_continent_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION place_continent_point.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh place_continent_point'; + + -- Analyze tracking and source tables before performing update + ANALYZE place_continent_point.osm_ids; + ANALYZE osm_continent_point; + + PERFORM update_osm_continent_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM place_continent_point.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM place_continent_point.updates; + + RAISE LOG 'Refresh place_continent_point done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE + ON osm_continent_point + FOR EACH ROW +EXECUTE PROCEDURE place_continent_point.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE + ON osm_continent_point + FOR EACH STATEMENT +EXECUTE PROCEDURE place_continent_point.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON place_continent_point.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE place_continent_point.refresh(); diff --git a/layers/place/update_country_point.sql b/layers/place/update_country_point.sql new file mode 100644 index 0000000..adc8004 --- /dev/null +++ b/layers/place/update_country_point.sql @@ -0,0 +1,164 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_country_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_country_point; +DROP TRIGGER IF EXISTS trigger_refresh ON place_country.updates; + +CREATE SCHEMA IF NOT EXISTS place_country; + +CREATE TABLE IF NOT EXISTS place_country.osm_ids +( + osm_id bigint PRIMARY KEY +); + +-- etldoc: ne_10m_admin_0_countries -> osm_country_point +-- etldoc: osm_country_point -> osm_country_point + +CREATE OR REPLACE FUNCTION update_osm_country_point(full_update boolean) RETURNS void AS +$$ + UPDATE osm_country_point AS osm + SET "rank" = 7, + iso3166_1_alpha_2 = COALESCE( + NULLIF(osm.country_code_iso3166_1_alpha_2, ''), + NULLIF(osm.iso3166_1_alpha_2, ''), + NULLIF(osm.iso3166_1, '') + ) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_country.osm_ids)) + AND rank IS NULL; + + WITH important_country_point AS ( + SELECT osm.geometry, + osm.osm_id, + osm.name, + COALESCE(NULLIF(osm.name_en, ''), ne.name) AS name_en, + ne.scalerank, + ne.labelrank + FROM ne_10m_admin_0_countries AS ne, + osm_country_point AS osm + WHERE + -- We match only countries with ISO codes to eliminate disputed countries + iso3166_1_alpha_2 IS NOT NULL + -- that lies inside polygon of sovereign country + AND ST_Within(osm.geometry, ne.geometry) + ) + UPDATE osm_country_point AS osm + -- Normalize both scalerank and labelrank into a ranking system from 1 to 6 + -- where the ranks are still distributed uniform enough across all countries + SET "rank" = LEAST(6, CEILING((scalerank + labelrank) / 2.0)) + FROM important_country_point AS ne + WHERE (full_update OR osm.osm_id IN (SELECT osm_id FROM place_country.osm_ids)) + AND rank = 7 + AND osm.osm_id = ne.osm_id; + + -- Repeat the step for archipelago countries like Philippines or Indonesia + -- whose label point is not within country's polygon + WITH important_country_point AS ( + SELECT osm.osm_id, +-- osm.name, + ne.scalerank, + ne.labelrank, +-- ST_Distance(osm.geometry, ne.geometry) AS distance, + ROW_NUMBER() + OVER ( + PARTITION BY osm.osm_id + ORDER BY + ST_Distance(osm.geometry, ne.geometry) + ) AS rk + FROM osm_country_point osm, + ne_10m_admin_0_countries AS ne + WHERE iso3166_1_alpha_2 IS NOT NULL + AND NOT (osm."rank" BETWEEN 1 AND 6) + ) + UPDATE osm_country_point AS osm + -- Normalize both scalerank and labelrank into a ranking system from 1 to 6 + -- where the ranks are still distributed uniform enough across all countries + SET "rank" = LEAST(6, CEILING((ne.scalerank + ne.labelrank) / 2.0)) + FROM important_country_point AS ne + WHERE (full_update OR osm.osm_id IN (SELECT osm_id FROM place_country.osm_ids)) + AND rank = 7 + AND osm.osm_id = ne.osm_id + AND ne.rk = 1; + + UPDATE osm_country_point AS osm + SET "rank" = 6 + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_country.osm_ids)) + AND "rank" = 7; + + -- TODO: This shouldn't be necessary? The rank function makes something wrong... + UPDATE osm_country_point AS osm + SET "rank" = 1 + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_country.osm_ids)) + AND "rank" = 0; + + UPDATE osm_country_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_country.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); + +$$ LANGUAGE SQL; + +SELECT update_osm_country_point(true); + +-- Handle updates + +CREATE OR REPLACE FUNCTION place_country.store() RETURNS trigger AS +$$ +BEGIN + INSERT INTO place_country.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS place_country.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION place_country.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO place_country.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION place_country.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh place_country rank'; + + -- Analyze tracking and source tables before performing update + ANALYZE place_country.osm_ids; + ANALYZE osm_country_point; + + PERFORM update_osm_country_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM place_country.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM place_country.updates; + + RAISE LOG 'Refresh place_country done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE + ON osm_country_point + FOR EACH ROW +EXECUTE PROCEDURE place_country.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE + ON osm_country_point + FOR EACH STATEMENT +EXECUTE PROCEDURE place_country.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON place_country.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE place_country.refresh(); diff --git a/layers/place/update_island_point.sql b/layers/place/update_island_point.sql new file mode 100644 index 0000000..3503464 --- /dev/null +++ b/layers/place/update_island_point.sql @@ -0,0 +1,87 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_island_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_island_point; +DROP TRIGGER IF EXISTS trigger_refresh ON place_island_point.updates; + +CREATE SCHEMA IF NOT EXISTS place_island_point; + +CREATE TABLE IF NOT EXISTS place_island_point.osm_ids +( + osm_id bigint PRIMARY KEY +); + +-- etldoc: osm_island_point -> osm_island_point +CREATE OR REPLACE FUNCTION update_osm_island_point(full_update boolean) RETURNS void AS +$$ + UPDATE osm_island_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_island_point.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); +$$ LANGUAGE SQL; + +SELECT update_osm_island_point(true); + +-- Handle updates + +CREATE OR REPLACE FUNCTION place_island_point.store() RETURNS trigger AS +$$ +BEGIN + INSERT INTO place_island_point.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS place_island_point.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION place_island_point.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO place_island_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION place_island_point.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh place_island_point'; + + -- Analyze tracking and source tables before performing update + ANALYZE place_island_point.osm_ids; + ANALYZE osm_island_point; + + PERFORM update_osm_island_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM place_island_point.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM place_island_point.updates; + + RAISE LOG 'Refresh place_island_point done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE + ON osm_island_point + FOR EACH ROW +EXECUTE PROCEDURE place_island_point.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE + ON osm_island_point + FOR EACH STATEMENT +EXECUTE PROCEDURE place_island_point.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON place_island_point.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE place_island_point.refresh(); diff --git a/layers/place/update_island_polygon.sql b/layers/place/update_island_polygon.sql new file mode 100644 index 0000000..23e76c7 --- /dev/null +++ b/layers/place/update_island_polygon.sql @@ -0,0 +1,94 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_island_polygon; +DROP TRIGGER IF EXISTS trigger_store ON osm_island_polygon; +DROP TRIGGER IF EXISTS trigger_refresh ON place_island_polygon.updates; + +CREATE SCHEMA IF NOT EXISTS place_island_polygon; + +CREATE TABLE IF NOT EXISTS place_island_polygon.osm_ids +( + osm_id bigint PRIMARY KEY +); + +-- etldoc: osm_island_polygon -> osm_island_polygon +CREATE OR REPLACE FUNCTION update_osm_island_polygon(full_update boolean) RETURNS void AS +$$ + UPDATE osm_island_polygon + SET geometry = ST_PointOnSurface(geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_island_polygon.osm_ids)) + AND ST_GeometryType(geometry) <> 'ST_Point' + AND ST_IsValid(geometry); + + UPDATE osm_island_polygon + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_island_polygon.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); + +$$ LANGUAGE SQL; + +SELECT update_osm_island_polygon(true); + +-- Handle updates + +CREATE OR REPLACE FUNCTION place_island_polygon.store() RETURNS trigger AS +$$ +BEGIN + INSERT INTO place_island_polygon.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS place_island_polygon.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION place_island_polygon.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO place_island_polygon.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION place_island_polygon.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh place_island_polygon'; + + -- Analyze tracking and source tables before performing update + ANALYZE place_island_polygon.osm_ids; + ANALYZE osm_island_polygon; + + PERFORM update_osm_island_polygon(false); + -- noinspection SqlWithoutWhere + DELETE FROM place_island_polygon.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM place_island_polygon.updates; + + RAISE LOG 'Refresh place_island_polygon done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE + ON osm_island_polygon + FOR EACH ROW +EXECUTE PROCEDURE place_island_polygon.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE + ON osm_island_polygon + FOR EACH STATEMENT +EXECUTE PROCEDURE place_island_polygon.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON place_island_polygon.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE place_island_polygon.refresh(); diff --git a/layers/place/update_state_point.sql b/layers/place/update_state_point.sql new file mode 100644 index 0000000..9c75c3e --- /dev/null +++ b/layers/place/update_state_point.sql @@ -0,0 +1,126 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_state_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_state_point; +DROP TRIGGER IF EXISTS trigger_refresh ON place_state.updates; + +CREATE SCHEMA IF NOT EXISTS place_state; + +CREATE TABLE IF NOT EXISTS place_state.osm_ids +( + osm_id bigint PRIMARY KEY +); + +-- etldoc: ne_10m_admin_1_states_provinces -> osm_state_point +-- etldoc: osm_state_point -> osm_state_point + +CREATE OR REPLACE FUNCTION update_osm_state_point(full_update boolean) RETURNS void AS +$$ + WITH important_state_point AS ( + SELECT osm.geometry, + osm.osm_id, + osm.name, + COALESCE(NULLIF(osm.name_en, ''), ne.name) AS name_en, + ne.scalerank, + ne.labelrank, + ne.datarank + FROM ne_10m_admin_1_states_provinces AS ne, + osm_state_point AS osm + WHERE + -- We only match whether the point is within the Natural Earth polygon + -- because name matching is difficult + ST_Within(osm.geometry, ne.geometry) + -- We leave out leess important states + AND ne.scalerank <= 6 + AND ne.labelrank <= 7 + ) + UPDATE osm_state_point AS osm + -- Normalize both scalerank and labelrank into a ranking system from 1 to 6. + SET "rank" = LEAST(6, CEILING((scalerank + labelrank + datarank) / 3.0)) + FROM important_state_point AS ne + WHERE (full_update OR osm.osm_id IN (SELECT osm_id FROM place_state.osm_ids)) + AND rank IS NULL + AND osm.osm_id = ne.osm_id; + + -- TODO: This shouldn't be necessary? The rank function makes something wrong... + UPDATE osm_state_point AS osm + SET "rank" = 1 + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_state.osm_ids)) + AND "rank" = 0; + + DELETE FROM osm_state_point + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_state.osm_ids)) + AND "rank" IS NULL; + + UPDATE osm_state_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_state.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); + +$$ LANGUAGE SQL; + +SELECT update_osm_state_point(true); + +-- Handle updates + +CREATE OR REPLACE FUNCTION place_state.store() RETURNS trigger AS +$$ +BEGIN + INSERT INTO place_state.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS place_state.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION place_state.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO place_state.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION place_state.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh place_state rank'; + + -- Analyze tracking and source tables before performing update + ANALYZE place_state.osm_ids; + ANALYZE osm_state_point; + + PERFORM update_osm_state_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM place_state.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM place_state.updates; + + RAISE LOG 'Refresh place_state done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE + ON osm_state_point + FOR EACH ROW +EXECUTE PROCEDURE place_state.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE + ON osm_state_point + FOR EACH STATEMENT +EXECUTE PROCEDURE place_state.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON place_state.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE place_state.refresh(); diff --git a/layers/poi/README.md b/layers/poi/README.md new file mode 100644 index 0000000..dc7b2d6 --- /dev/null +++ b/layers/poi/README.md @@ -0,0 +1,10 @@ +## poi + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#poi** + +### Mapping Diagram +![Mapping diagram for poi](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for poi](etl_diagram.png?raw=true) diff --git a/layers/poi/class.sql b/layers/poi/class.sql new file mode 100644 index 0000000..0f4323b --- /dev/null +++ b/layers/poi/class.sql @@ -0,0 +1,42 @@ +CREATE OR REPLACE FUNCTION poi_class_rank(class text) + RETURNS int AS +$$ +SELECT CASE class + WHEN 'hospital' THEN 20 + WHEN 'railway' THEN 40 + WHEN 'bus' THEN 50 + WHEN 'attraction' THEN 70 + WHEN 'harbor' THEN 75 + WHEN 'college' THEN 80 + WHEN 'school' THEN 85 + WHEN 'stadium' THEN 90 + WHEN 'zoo' THEN 95 + WHEN 'town_hall' THEN 100 + WHEN 'campsite' THEN 110 + WHEN 'cemetery' THEN 115 + WHEN 'park' THEN 120 + WHEN 'library' THEN 130 + WHEN 'police' THEN 135 + WHEN 'post' THEN 140 + WHEN 'golf' THEN 150 + WHEN 'shop' THEN 400 + WHEN 'grocery' THEN 500 + WHEN 'fast_food' THEN 600 + WHEN 'clothing_store' THEN 700 + WHEN 'bar' THEN 800 + ELSE 1000 + END; +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION poi_class(subclass text, mapping_key text) + RETURNS text AS +$$ +SELECT CASE + -- Special case subclass collision between office=university and amenity=university + WHEN mapping_key = 'amenity' AND subclass = 'university' THEN 'college' + %%FIELD_MAPPING: class %% + ELSE subclass + END; +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; diff --git a/layers/poi/etl_diagram.png b/layers/poi/etl_diagram.png new file mode 100644 index 0000000..db51621 Binary files /dev/null and b/layers/poi/etl_diagram.png differ diff --git a/layers/poi/mapping.yaml b/layers/poi/mapping.yaml new file mode 100644 index 0000000..95d7b5f --- /dev/null +++ b/layers/poi/mapping.yaml @@ -0,0 +1,479 @@ +# imposm3 mapping file for https://github.com/osm2vectortiles/imposm3 +# Warning: this is not the official imposm3 + +# aerialway values , see http://taginfo.openstreetmap.org/keys/aerialway#values +def_poi_mapping_aerialway: &poi_mapping_aerialway + - station + +# amenity values , see http://taginfo.openstreetmap.org/keys/amenity#values +def_poi_mapping_amenity: &poi_mapping_amenity + - arts_centre + - atm + - bank + - bar + - bbq + - bench + - bicycle_parking + - bicycle_rental + - biergarten + - bus_station + - cafe + - charging_station + - cinema + - clinic + - college + - community_centre + - courthouse + - dentist + - doctors + - drinking_water + - fast_food + - ferry_terminal + - fire_station + - food_court + - fuel + - grave_yard + - grit_bin + - hospital + - ice_cream + - kindergarten + - library + - marketplace + - motorcycle_parking + - nightclub + - nursing_home + - parking + - pharmacy + - place_of_worship + - police + - parcel_locker + - post_box + - post_office + - prison + - pub + - public_building + - recycling + - restaurant + - school + - shelter + - swimming_pool + - taxi + - telephone + - theatre + - toilets + - townhall + - university + - veterinary + - waste_basket + +# barrier values , see http://taginfo.openstreetmap.org/keys/barrier#values +def_poi_mapping_barrier: &poi_mapping_barrier + - bollard + - border_control + - cycle_barrier + - gate + - lift_gate + - sally_port + - stile + - toll_booth + +# building values , see http://taginfo.openstreetmap.org/keys/building#values +def_poi_mapping_building: &poi_mapping_building + - dormitory + +# highway values , see http://taginfo.openstreetmap.org/keys/highway#values +def_poi_mapping_highway: &poi_mapping_highway + - bus_stop + +# historic values , see http://taginfo.openstreetmap.org/keys/historic#values +def_poi_mapping_historic: &poi_mapping_historic + - monument + - castle + - ruins + +# landuse values , see http://taginfo.openstreetmap.org/keys/landuse#values +def_poi_mapping_landuse: &poi_mapping_landuse + - basin + - brownfield + - cemetery + - reservoir + - winter_sports + +# leisure values , see http://taginfo.openstreetmap.org/keys/leisure#values +def_poi_mapping_leisure: &poi_mapping_leisure + - dog_park + - escape_game + - garden + - golf_course + - ice_rink + - hackerspace + - marina + - miniature_golf + - park + - pitch + - playground + - sports_centre + - stadium + - swimming_area + - swimming_pool + - water_park + +# office values , see http://taginfo.openstreetmap.org/keys/office#values +def_poi_mapping_office: &poi_mapping_office + - accountant + - advertising_agency + - architect + - association + - bail_bond_agent + - charity + - company + - construction_company + - consulting + - cooperative + - courier + - coworking + - diplomatic + - educational_institution + - employment_agency + - energy_supplier + - engineer + - estate_agent + - financial + - financial_advisor + - forestry + - foundation + - geodesist + - government + - graphic_design + - guide + - harbour_master + - health_insurance + - insurance + - interior_design + - it + - lawyer + - logistics + - marketing + - moving_company + - newspaper + - ngo + - notary + - physician + - political_party + - private_investigator + - property_management + - publisher + - quango + - religion + - research + - security + - surveyor + - tax_advisor + - taxi + - telecommunication + - therapist + - translator + - travel_agent + - tutoring + - union + - university + - water_utility + - web_design + - wedding_planner + +# railway values , see http://taginfo.openstreetmap.org/keys/railway#values +def_poi_mapping_railway: &poi_mapping_railway + - halt + - station + - subway_entrance + - train_station_entrance + - tram_stop + +# shop values , see http://taginfo.openstreetmap.org/keys/shop#values +def_poi_mapping_shop: &poi_mapping_shop + - accessories + - alcohol + - antiques + - art + - bag + - bakery + - beauty + - bed + - beverages + - bicycle + - books + - boutique + - butcher + - camera + - car + - car_repair + - car_parts + - carpet + - charity + - chemist + - chocolate + - clothes + - coffee + - computer + - confectionery + - convenience + - copyshop + - cosmetics + - deli + - delicatessen + - department_store + - doityourself + - dry_cleaning + - electronics + - erotic + - fabric + - florist + - frozen_food + - furniture + - garden_centre + - general + - gift + - greengrocer + - hairdresser + - hardware + - hearing_aids + - hifi + - ice_cream + - interior_decoration + - jewelry + - kiosk + - lamps + - laundry + - locksmith + - mall + - massage + - mobile_phone + - motorcycle + - music + - musical_instrument + - newsagent + - optician + - outdoor + - paint + - perfume + - perfumery + - pet + - photo + - second_hand + - shoes + - sports + - stationery + - supermarket + - tailor + - tattoo + - ticket + - tobacco + - toys + - travel_agency + - video + - video_games + - watches + - weapons + - wholesale + - wine + +# sport values , see http://taginfo.openstreetmap.org/keys/sport#values +def_poi_mapping_sport: &poi_mapping_sport + - american_football + - archery + - athletics + - australian_football + - badminton + - baseball + - basketball + - beachvolleyball + - billiards + - bmx + - boules + - bowls + - boxing + - canadian_football + - canoe + - chess + - climbing + - climbing_adventure + - cricket + - cricket_nets + - croquet + - curling + - cycling + - disc_golf + - diving + - dog_racing + - equestrian + - fatsal + - field_hockey + - free_flying + - gaelic_games + - golf + - gymnastics + - handball + - hockey + - horse_racing + - horseshoes + - ice_hockey + - ice_stock + - judo + - karting + - korfball + - long_jump + - model_aerodrome + - motocross + - motor + - multi + - netball + - orienteering + - paddle_tennis + - paintball + - paragliding + - pelota + - racquet + - rc_car + - rowing + - rugby + - rugby_league + - rugby_union + - running + - sailing + - scuba_diving + - shooting + - shooting_range + - skateboard + - skating + - skiing + - soccer + - surfing + - swimming + - table_soccer + - table_tennis + - team_handball + - tennis + - toboggan + - volleyball + - water_ski + - yoga + +# tourism values , see http://taginfo.openstreetmap.org/keys/tourism#values +def_poi_mapping_tourism: &poi_mapping_tourism + - alpine_hut + - aquarium + - artwork + - attraction + - bed_and_breakfast + - camp_site + - caravan_site + - chalet + - gallery + - guest_house + - hostel + - hotel + - information + - motel + - museum + - picnic_site + - theme_park + - viewpoint + - zoo + +# waterway values , see http://taginfo.openstreetmap.org/keys/waterway#values +def_poi_mapping_waterway: &poi_mapping_waterway + - dock + +def_poi_fields: &poi_fields + - name: osm_id + type: id + - name: geometry + type: geometry + - name: area + type: area + - name: name + key: name + type: string + - name: name_en + key: name:en + type: string + - name: name_de + key: name:de + type: string + - name: tags + type: hstore_tags + - name: subclass + type: mapping_value + - name: mapping_key + type: mapping_key + - name: station + key: station + type: string + - name: funicular + key: funicular + type: string + - name: information + key: information + type: string + - name: uic_ref + key: uic_ref + type: string + - name: ref + key: ref + type: string + - name: religion + key: religion + type: string + - name: level + key: level + type: integer + - name: indoor + key: indoor + type: bool + - name: layer + key: layer + type: integer + - name: sport + key: sport + type: string + - name: operator + key: operator + type: string + - name: network + key: network + type: string + - name: brand + key: brand + type: string + - name: vending + key: vending + type: string + +def_poi_mapping: &poi_mapping + aerialway: *poi_mapping_aerialway + amenity: *poi_mapping_amenity + barrier: *poi_mapping_barrier + building: *poi_mapping_building + highway: *poi_mapping_highway + historic: *poi_mapping_historic + landuse: *poi_mapping_landuse + leisure: *poi_mapping_leisure + office: *poi_mapping_office + railway: *poi_mapping_railway + shop: *poi_mapping_shop + sport: *poi_mapping_sport + tourism: *poi_mapping_tourism + waterway: *poi_mapping_waterway + +tables: + # etldoc: imposm3 -> osm_poi_point + poi_point: + type: point + columns: *poi_fields + mapping: *poi_mapping + + # etldoc: imposm3 -> osm_poi_polygon + poi_polygon: + type: polygon + columns: *poi_fields + mapping: *poi_mapping diff --git a/layers/poi/mapping_diagram.png b/layers/poi/mapping_diagram.png new file mode 100644 index 0000000..1b5726f Binary files /dev/null and b/layers/poi/mapping_diagram.png differ diff --git a/layers/poi/poi.sql b/layers/poi/poi.sql new file mode 100644 index 0000000..2a55fdc --- /dev/null +++ b/layers/poi/poi.sql @@ -0,0 +1,102 @@ +-- etldoc: layer_poi[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_poi | z12 | z13 | z14+" ] ; + +CREATE OR REPLACE FUNCTION layer_poi(bbox geometry, zoom_level integer, pixel_width numeric) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + name text, + name_en text, + name_de text, + tags hstore, + class text, + subclass text, + agg_stop integer, + layer integer, + level integer, + indoor integer, + "rank" int + ) +AS +$$ +SELECT osm_id_hash AS osm_id, + geometry, + NULLIF(name, '') AS name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + poi_class(subclass, mapping_key) AS class, + CASE + WHEN subclass = 'information' + THEN NULLIF(information, '') + WHEN subclass = 'place_of_worship' + THEN NULLIF(religion, '') + WHEN subclass = 'pitch' + THEN NULLIF(sport, '') + WHEN subclass = 'waste_basket' + THEN + CASE + WHEN vending ILIKE '%excrement_bags%' + THEN 'waste_basket_with_excrement_bags' + ELSE 'waste_basket' + END + ELSE subclass + END AS subclass, + agg_stop, + NULLIF(layer, 0) AS layer, + "level", + CASE WHEN indoor = TRUE THEN 1 END AS indoor, + CASE WHEN name = '' THEN 2000 ELSE poi_class_rank(poi_class(subclass, mapping_key)) END as "rank" +FROM ( + -- etldoc: osm_poi_point -> layer_poi:z12 + -- etldoc: osm_poi_point -> layer_poi:z13 + SELECT *, + osm_id * 10 AS osm_id_hash + FROM osm_poi_point + WHERE geometry && bbox + AND zoom_level BETWEEN 12 AND 13 + AND ((subclass = 'station' AND mapping_key = 'railway') + OR subclass IN ('halt', 'ferry_terminal')) + + UNION ALL + + -- etldoc: osm_poi_point -> layer_poi:z14_ + SELECT *, + osm_id * 10 AS osm_id_hash + FROM osm_poi_point + WHERE geometry && bbox + AND zoom_level >= 14 + + UNION ALL + + -- etldoc: osm_poi_polygon -> layer_poi:z12 + -- etldoc: osm_poi_polygon -> layer_poi:z13 + -- etldoc: osm_poi_polygon -> layer_poi:z14_ + SELECT *, + NULL::integer AS agg_stop, + CASE + WHEN osm_id < 0 THEN -osm_id * 10 + 4 + ELSE osm_id * 10 + 1 + END AS osm_id_hash + FROM osm_poi_polygon + WHERE geometry && bbox AND + CASE + WHEN zoom_level >= 14 THEN TRUE + WHEN zoom_level >= 12 AND + ((subclass = 'station' AND mapping_key = 'railway') + OR subclass IN ('halt', 'ferry_terminal')) THEN TRUE + WHEN zoom_level BETWEEN 10 AND 14 THEN + subclass IN ('university', 'college') AND + POWER(4,zoom_level) + -- Compute percentage of the earth's surface covered by this feature (approximately) + -- The constant below is 111,842^2 * 180 * 180, where 111,842 is the length of one degree of latitude at the equator in meters. + * area / (405279708033600 * COS(ST_Y(ST_Transform(geometry,4326))*PI()/180)) + -- Match features that are at least 10% of a tile at this zoom + > 0.10 + ELSE FALSE END + ) AS poi_union +ORDER BY "rank" +$$ LANGUAGE SQL STABLE + PARALLEL SAFE; +-- TODO: Check if the above can be made STRICT -- i.e. if pixel_width could be NULL diff --git a/layers/poi/poi.yaml b/layers/poi/poi.yaml new file mode 100644 index 0000000..42f0c1b --- /dev/null +++ b/layers/poi/poi.yaml @@ -0,0 +1,171 @@ +layer: + id: "poi" + description: | + [Points of interests](http://wiki.openstreetmap.org/wiki/Points_of_interest) containing + a of a variety of OpenStreetMap tags. Mostly contains amenities, sport, shop and tourist POIs. + buffer_size: 64 + srs: +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over + fields: + name: The OSM [`name`](http://wiki.openstreetmap.org/wiki/Key:name) value of the POI. Language-specific values are in `name:xx`. + name_en: English name `name:en` if available, otherwise `name`. This is deprecated and will be removed in a future release in favor of `name:en`. + name_de: German name `name:de` if available, otherwise `name` or `name:en`. This is deprecated and will be removed in a future release in favor of `name:de`. + class: + description: | + More general classes of POIs. If there is no more general `class` for the `subclass` + this field will contain the same value as `subclass`. + But for example for schools you only need to style the class `school` to filter the subclasses `school` + and `kindergarten`. Or use the class `shop` to style all shops. + values: + shop: + subclass: ['accessories', 'antiques', 'beauty', 'bed', 'boutique', 'camera', 'carpet', 'charity', 'chemist', 'chocolate', + 'coffee', 'computer', 'convenience', 'confectionery', 'copyshop', 'cosmetics', 'garden_centre', 'doityourself', + 'erotic', 'electronics', 'fabric', 'florist', 'frozen_food', 'furniture', 'video_games', 'video', + 'general', 'gift', 'hardware', 'hearing_aids', 'hifi', 'interior_decoration', + 'jewelry', 'kiosk', 'locksmith', 'lamps', 'mall', 'massage', 'motorcycle', 'mobile_phone', + 'newsagent', 'optician', 'outdoor', 'paint', 'perfumery', 'perfume', 'pet', 'photo', 'second_hand', + 'shoes', 'sports', 'stationery', 'tailor', 'tattoo', 'ticket', 'tobacco', 'toys', 'travel_agency', + 'watches', 'weapons', 'wholesale'] + office: + subclass: ['accountant', 'advertising_agency', 'architect', 'association', 'bail_bond_agent', 'charity', + 'company', 'construction_company', 'consulting', 'cooperative', 'courier', 'coworking', 'diplomatic', + 'educational_institution', 'employment_agency', 'energy_supplier', 'engineer', 'estate_agent', 'financial', + 'financial_advisor', 'forestry', 'foundation', 'geodesist', 'government', 'graphic_design', 'guide', + 'harbour_master', 'health_insurance', 'insurance', 'interior_design', 'it', 'lawyer', 'logistics', 'marketing', + 'moving_company', 'newspaper', 'ngo', 'notary', 'physician', 'political_party', 'private_investigator', + 'property_management', 'publisher', 'quango', 'religion', 'research', 'security', 'surveyor', 'tax_advisor', + 'taxi', 'telecommunication', 'therapist', 'translator', 'travel_agent', 'tutoring', 'union', 'university', + 'water_utility', 'web_design', 'wedding_planner'] + town_hall: + subclass: ['townhall', 'public_building', 'courthouse', 'community_centre'] + golf: + subclass: ['golf', 'golf_course', 'miniature_golf'] + fast_food: + subclass: ['fast_food', 'food_court'] + park: + subclass: ['park', 'bbq'] + bus: + subclass: ['bus_stop', 'bus_station'] + railway: + - __AND__: + subclass: 'station' + mapping_key: 'railway' + - subclass: ['halt', 'tram_stop', 'subway'] + aerialway: + __AND__: + subclass: 'station' + mapping_key: 'aerialway' + entrance: + subclass: ['subway_entrance', 'train_station_entrance'] + campsite: + subclass: ['camp_site', 'caravan_site'] + laundry: + subclass: ['laundry', 'dry_cleaning'] + grocery: + subclass: ['supermarket', 'deli', 'delicatessen', 'department_store', 'greengrocer', 'marketplace'] + library: + subclass: ['books', 'library'] + college: + subclass: ['university', 'college'] + lodging: + subclass: ['hotel', 'motel', 'bed_and_breakfast', 'guest_house', 'hostel', 'chalet', 'alpine_hut', 'dormitory'] + ice_cream: + subclass: ['ice_cream'] + post: + subclass: ['post_box', 'post_office', 'parcel_locker'] + cafe: + subclass: ['cafe'] + school: + subclass: ['school', 'kindergarten'] + alcohol_shop: + subclass: ['alcohol', 'beverages', 'wine'] + bar: + subclass: ['bar', 'nightclub'] + harbor: + subclass: ['marina', 'dock'] + car: + subclass: ['car', 'car_repair', 'car_parts', 'taxi'] + hospital: + subclass: ['hospital', 'nursing_home', 'clinic'] + cemetery: + subclass: ['grave_yard', 'cemetery'] + attraction: + subclass: ['attraction', 'viewpoint'] + beer: + subclass: ['biergarten', 'pub'] + music: + subclass: ['music', 'musical_instrument'] + stadium: + subclass: ['american_football', 'stadium', 'soccer'] + art_gallery: + subclass: ['art', 'artwork', 'gallery', 'arts_centre'] + clothing_store: + subclass: ['bag', 'clothes'] + swimming: + subclass: ['swimming_area', 'swimming'] + castle: + subclass: ['castle', 'ruins'] + atm: + subclass: ['atm'] + fuel: + subclass: ['fuel', 'charging_station'] + subclass: + description: | + Original value of either the + [`amenity`](http://wiki.openstreetmap.org/wiki/Key:amenity), + [`barrier`](http://wiki.openstreetmap.org/wiki/Key:barrier), + [`historic`](http://wiki.openstreetmap.org/wiki/Key:historic), + [`information`](http://wiki.openstreetmap.org/wiki/Key:information), + [`landuse`](http://wiki.openstreetmap.org/wiki/Key:landuse), + [`leisure`](http://wiki.openstreetmap.org/wiki/Key:leisure), + [`railway`](http://wiki.openstreetmap.org/wiki/Key:railway), + [`shop`](http://wiki.openstreetmap.org/wiki/Key:shop), + [`sport`](http://wiki.openstreetmap.org/wiki/Key:sport), + [`station`](http://wiki.openstreetmap.org/wiki/Key:station), + [`religion`](http://wiki.openstreetmap.org/wiki/Key:religion), + [`tourism`](http://wiki.openstreetmap.org/wiki/Key:tourism), + [`aerialway`](http://wiki.openstreetmap.org/wiki/Key:aerialway), + [`building`](http://wiki.openstreetmap.org/wiki/Key:building), + [`highway`](http://wiki.openstreetmap.org/wiki/Key:highway), + [`office`](https://wiki.openstreetmap.org/wiki/Key:office) + or [`waterway`](http://wiki.openstreetmap.org/wiki/Key:waterway) + tag. Use this to do more precise styling. + rank: | + The POIs are ranked ascending according to their importance within a grid. The `rank` value shows the + local relative importance of a POI within it's cell in the grid. This can be used to reduce label density at *z14*. + Since all POIs already need to be contained at *z14* you can use `less than rank=10` epxression to limit + POIs. At some point like *z17* you can show all POIs. + agg_stop: + description: | + Experimental feature! Indicates main platform of public transport + stops (buses, trams, and subways). Grouping of platforms is + implemented using + [`uic_ref`](http://wiki.openstreetmap.org/wiki/Key:uic_ref) tag that + is not used worldwide. + values: [1] + level: + description: | + Original value of [`level`](http://wiki.openstreetmap.org/wiki/Key:level) tag. + layer: + description: | + Original value of [`layer`](http://wiki.openstreetmap.org/wiki/Key:layer) tag. + indoor: + description: | + Original value of [`indoor`](http://wiki.openstreetmap.org/wiki/Key:indoor) tag. + values: + - 1 + datasource: + geometry_field: geometry + key_field: osm_id + key_field_as_attribute: no + srid: 900913 + query: (SELECT osm_id, geometry, name, name_en, name_de, {name_languages}, class, subclass, agg_stop, layer, level, indoor, rank FROM layer_poi(!bbox!, z(!scale_denominator!), !pixel_width!)) AS t +schema: + - ./public_transport_stop_type.sql + - ./class.sql + - ./poi_stop_agg.sql + - ./update_poi_polygon.sql + - ./update_poi_point.sql + - ./poi.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/poi/poi_stop_agg.sql b/layers/poi/poi_stop_agg.sql new file mode 100644 index 0000000..39fb170 --- /dev/null +++ b/layers/poi/poi_stop_agg.sql @@ -0,0 +1,34 @@ +-- etldoc: osm_poi_point -> osm_poi_stop_centroid +DROP MATERIALIZED VIEW IF EXISTS osm_poi_stop_centroid CASCADE; +CREATE MATERIALIZED VIEW osm_poi_stop_centroid AS +( +SELECT uic_ref, + count(*) AS count, + CASE WHEN count(*) > 2 THEN ST_Centroid(ST_UNION(geometry)) END AS centroid +FROM osm_poi_point +WHERE uic_ref <> '' + AND subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') +GROUP BY uic_ref +HAVING count(*) > 1 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */; + +-- etldoc: osm_poi_stop_centroid -> osm_poi_stop_rank +-- etldoc: osm_poi_point -> osm_poi_stop_rank +DROP MATERIALIZED VIEW IF EXISTS osm_poi_stop_rank CASCADE; +CREATE MATERIALIZED VIEW osm_poi_stop_rank AS +( +SELECT p.osm_id, +-- p.uic_ref, +-- p.subclass, + ROW_NUMBER() + OVER ( + PARTITION BY p.uic_ref + ORDER BY + p.subclass :: public_transport_stop_type NULLS LAST, + ST_Distance(c.centroid, p.geometry) + ) AS rk +FROM osm_poi_point p + INNER JOIN osm_poi_stop_centroid c ON (p.uic_ref = c.uic_ref) +WHERE subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') +ORDER BY p.uic_ref, rk + ) /* DELAY_MATERIALIZED_VIEW_CREATION */; diff --git a/layers/poi/public_transport_stop_type.sql b/layers/poi/public_transport_stop_type.sql new file mode 100644 index 0000000..bb3239d --- /dev/null +++ b/layers/poi/public_transport_stop_type.sql @@ -0,0 +1,11 @@ +DO +$$ + BEGIN + PERFORM 'public_transport_stop_type'::regtype; + EXCEPTION + WHEN undefined_object THEN + CREATE TYPE public_transport_stop_type AS enum ( + 'subway', 'tram_stop', 'bus_station', 'bus_stop' + ); + END +$$; diff --git a/layers/poi/style.json b/layers/poi/style.json new file mode 100644 index 0000000..fc76a5a --- /dev/null +++ b/layers/poi/style.json @@ -0,0 +1,1749 @@ +{ + "layers": [ + { + "id": "poi_shop-z17", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 17, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": [ + "coalesce", + [ + "image", + [ + "match", + [ + "get", + "subclass" + ], + "chocolate", + "confectionery", + "wine", + "alcohol", + "kiosk", + "newsagent", + "perfume", + "perfumery", + "wholesale", + "trade", + [ + "get", + "subclass" + ] + ] + ], + [ + "image", + "shop" + ] + ], + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.1 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": true + }, + "paint": { + "text-color": "#939", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "shop", + "clothing_store", + "library", + "art_gallery", + "music", + "alcohol_shop", + "bakery" + ], + [ + "!in", + "subclass", + "mall", + "library", + "artwork", + "arts_centre", + "gallery" + ] + ], + "order": 155 + }, + { + "id": "poi_shop-z15", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 15, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": [ + "match", + [ + "get", + "subclass" + ], + "chocolate", + "confectionery", + [ + "get", + "subclass" + ] + ], + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.1 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": true + }, + "paint": { + "text-color": "#939", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "subclass", + "supermarket" + ] + ], + "order": 156 + }, + { + "id": "poi_waste", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 18, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 0.8 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#734a08", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "recycling", + "waste_basket", + "drinking_water", + "toilets" + ] + ], + "order": 157 + }, + { + "id": "poi_cemetery", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 14, + "layout": { + "icon-size": 1, + "text-font": [ + "Noto Sans Regular" + ], + "text-size": 11, + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "icon-anchor": "bottom", + "icon-offset": [ + 0, + 0 + ], + "text-anchor": "center", + "text-offset": [ + 0, + 0 + ], + "text-padding": 2, + "icon-text-fit": "none", + "text-max-width": 14, + "icon-allow-overlap": false, + "icon-pitch-alignment": "viewport" + }, + "paint": { + "text-color": "#2d4931", + "icon-translate": [ + 0, + 0 + ], + "text-translate": [ + 0, + 5 + ], + "text-halo-color": "rgba(255, 255, 255, 1)", + "text-halo-width": 0.8, + "icon-translate-anchor": "map", + "text-translate-anchor": "viewport" + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "cemetery" + ] + ], + "order": 158 + }, + { + "id": "poi_school", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 16, + "layout": { + "icon-size": 1, + "text-font": [ + "Noto Sans Italic", + "Noto Sans Regular" + ], + "text-size": 11, + "icon-image": "{subclass}", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "icon-anchor": "bottom", + "icon-offset": [ + 0, + 0 + ], + "text-anchor": "center", + "text-offset": [ + 0, + 0 + ], + "text-padding": 2, + "icon-text-fit": "none", + "text-max-width": 9, + "icon-allow-overlap": false, + "icon-pitch-alignment": "viewport" + }, + "paint": { + "text-color": "#4d4d00", + "icon-translate": [ + 0, + 0 + ], + "text-translate": [ + 0, + 5 + ], + "text-halo-color": "rgba(255, 255, 255, 1)", + "text-halo-width": 0.8, + "icon-translate-anchor": "map", + "text-translate-anchor": "viewport" + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "college", + "school" + ] + ], + "order": 159 + }, + { + "id": "poi_outdoor", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 16, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.2 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#008c0d", + "icon-opacity": 1, + "text-halo-blur": 0, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "playground", + "stile", + "garden", + "gate" + ] + ], + "order": 160 + }, + { + "id": "poi_parking", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 15, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": { + "stops": [ + [ + 15, + "" + ], + [ + 16, + "{name:latin}\n{name:nonlatin}" + ] + ] + }, + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 0.9 + ], + "text-padding": 2, + "text-max-width": 6, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#0066ff", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "parking" + ] + ], + "order": 161 + }, + { + "id": "poi_golf", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 16, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "golf", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.2 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#008c0d", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "golf" + ] + ], + "order": 162 + }, + { + "id": "poi_sport", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 16, + "layout": { + "text-font": [ + "Noto Sans Italic" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.2 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#008c0d", + "icon-opacity": 1, + "icon-halo-blur": 0, + "text-halo-blur": 1, + "text-halo-color": "#ffffff", + "text-halo-width": 0.2 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "stadium", + "swimming_pool", + "sports_centre", + "water_park" + ] + ], + "order": 163 + }, + { + "id": "poi_ferry", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 15, + "layout": { + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "ferry", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 0.7 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#5e3b9e", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "subclass", + "ferry_terminal" + ], + [ + "==", + "class", + "ferry_terminal" + ] + ], + "order": 164 + }, + { + "id": "poi_food", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 16, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#C77400", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "ice_cream", + "cafe", + "beer", + "bar", + "fast_food", + "restaurant" + ] + ], + "order": 165 + }, + { + "id": "poi_water", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 16, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.2 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#4d80b3", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "ice_rink" + ] + ], + "order": 166 + }, + { + "id": "poi_public", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 16, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": [ + "coalesce", + [ + "image", + [ + "get", + "subclass" + ] + ], + [ + "image", + [ + "get", + "class" + ] + ], + [ + "image", + "" + ] + ], + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 0.8 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#734a08", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "town_hall", + "post", + "library", + "police", + "information", + "cinema", + "theatre", + "fire_station", + "office" + ], + [ + "!=", + "subclass", + "books" + ] + ], + "order": 167 + }, + { + "id": "poi_cultural", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 15, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 0.8 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#734a08", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "cinema", + "library", + "museum", + "castle", + "monument", + "art_gallery" + ], + [ + "!in", + "subclass", + "books", + "art" + ] + ], + "order": 168 + }, + { + "id": "poi_attraction", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 15, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.2 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#660033", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "attraction" + ] + ], + "order": 169 + }, + { + "id": "poi_car", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 17, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": { + "stops": [ + [ + 15, + "" + ], + [ + 16, + "{name:latin}\n{name:nonlatin}" + ] + ] + }, + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.2 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false, + "icon-ignore-placement": false + }, + "paint": { + "text-color": "#0066ff", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "car", + "bicycle_parking", + "fuel", + "charging_station" + ] + ], + "order": 170 + }, + { + "id": "poi_health", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 16, + "layout": { + "text-font": [ + "Noto Sans Regular", + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 14, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.2 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#BF0000", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "pharmacy", + "dentist", + "veterinary" + ] + ], + "order": 171 + }, + { + "id": "poi_hospital", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 14, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 14, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": { + "stops": [ + [ + 14, + "" + ], + [ + 16, + "{name:latin}\n{name:nonlatin}" + ] + ] + }, + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.2 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#BF0000", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "hospital" + ] + ], + "order": 172 + }, + { + "id": "poi_campsite", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 16, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "camping", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.2 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#0066ff", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "campsite" + ] + ], + "order": 173 + }, + { + "id": "poi_accommodation", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 17, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": { + "stops": [ + [ + 17, + "" + ], + [ + 18, + "{name:latin}\n{name:nonlatin}" + ] + ] + }, + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 0.6 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#0066ff", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "lodging", + "campsite" + ] + ], + "order": 174 + }, + { + "id": "poi_place_of_worship", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 16, + "layout": { + "icon-size": 1, + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 12 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": { + "stops": [ + [ + 15, + "" + ], + [ + 16, + "{name:latin}\n{name:nonlatin}" + ] + ] + }, + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.2 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false, + "text-allow-overlap": false + }, + "paint": { + "text-color": "rgba(56, 56, 71, 1)", + "text-halo-blur": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "place_of_worship" + ] + ], + "order": 175 + }, + { + "id": "poi_busstop", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 17, + "layout": { + "icon-size": 1, + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 11 + ] + ] + }, + "icon-image": "bus_stop.12", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "icon-anchor": "bottom", + "text-anchor": "top", + "text-padding": 2, + "icon-text-fit": "none", + "text-max-width": 14, + "icon-keep-upright": true, + "icon-allow-overlap": false, + "icon-pitch-alignment": "viewport" + }, + "paint": { + "text-color": "#0066ff", + "icon-translate": [ + 0, + 0 + ], + "text-translate": [ + 0, + 4 + ], + "text-halo-color": "rgba(255, 255, 255, 1)", + "text-halo-width": 0.8, + "icon-translate-anchor": "map", + "text-translate-anchor": "viewport" + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "bus" + ] + ], + "order": 176 + }, + { + "id": "poi_bus", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 17, + "layout": { + "icon-size": 1, + "text-font": [ + "Noto Sans Bold", + "Noto Sans Regular" + ], + "text-size": 11, + "icon-image": "{subclass}", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "icon-anchor": "bottom", + "icon-offset": [ + 0, + 0 + ], + "text-anchor": "center", + "text-padding": 2, + "icon-text-fit": "none", + "text-max-width": 14, + "icon-keep-upright": true, + "icon-allow-overlap": false, + "icon-pitch-alignment": "viewport" + }, + "paint": { + "text-color": "#0066ff", + "icon-translate": [ + 0, + 0 + ], + "text-translate": [ + 0, + 5 + ], + "text-halo-color": "rgba(255, 255, 255, 1)", + "text-halo-width": 0.8, + "icon-translate-anchor": "map", + "text-translate-anchor": "viewport" + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "bus" + ], + [ + "!in", + "subclass", + "bus_stop" + ] + ], + "order": 177 + }, + { + "id": "poi_harbor", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 16, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 14, + 13 + ], + [ + 20, + 16 + ] + ] + }, + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-padding": 2, + "text-max-width": 6, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#576ddf", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.1, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.2, + "text-halo-color": "#ffffff", + "text-halo-width": 0.3 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "harbor" + ] + ], + "order": 178 + }, + { + "id": "poi_mall", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 15, + "layout": { + "text-font": [ + "Noto Sans Italic" + ], + "text-size": { + "stops": [ + [ + 15, + 12 + ], + [ + 20, + 16 + ] + ] + }, + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.1 + ], + "text-padding": 2, + "text-max-width": 9, + "icon-allow-overlap": false + }, + "paint": { + "text-color": "#d11700", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "shop" + ], + [ + "==", + "subclass", + "mall" + ] + ], + "order": 179 + }, + { + "id": "poi_train", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 10, + "layout": { + "icon-size": 1, + "text-font": [ + "Noto Sans Bold", + "Noto Sans Regular" + ], + "text-size": 11, + "icon-image": "square_train", + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "icon-anchor": "bottom", + "icon-offset": [ + 0, + 0 + ], + "text-anchor": "center", + "text-offset": [ + 0, + 0.5 + ], + "text-padding": 2, + "icon-text-fit": "none", + "text-max-width": 12, + "icon-allow-overlap": false, + "text-allow-overlap": false, + "icon-pitch-alignment": "viewport" + }, + "paint": { + "text-color": "#4957ad", + "icon-translate": [ + 0, + 0 + ], + "text-translate": [ + 0, + 1 + ], + "text-halo-color": "rgba(255, 255, 255, 1)", + "text-halo-width": 0.8, + "icon-translate-anchor": "map", + "text-translate-anchor": "viewport" + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "railway" + ] + ], + "order": 180 + }, + { + "id": "park-local", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 15, + "layout": { + "text-font": [ + "Noto Sans Italic" + ], + "text-size": { + "stops": [ + [ + 15, + 10 + ], + [ + 20, + 13 + ] + ] + }, + "text-field": "{name:latin}{name:nonlatin}", + "visibility": "visible", + "symbol-spacing": 150, + "text-max-width": { + "stops": [ + [ + 12, + 5 + ], + [ + 18, + 8 + ] + ] + }, + "text-allow-overlap": false + }, + "paint": { + "text-color": "#0c8416", + "text-halo-blur": 0.5, + "text-halo-color": "rgba(255, 255, 255, 1)", + "text-halo-width": 1 + }, + "filter": [ + "all", + [ + "==", + "class", + "park" + ], + [ + "==", + "subclass", + "park" + ] + ], + "order": 194 + }, + { + "id": "poi_zoo", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 11, + "layout": { + "text-font": [ + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 15, + 12 + ], + [ + 20, + 16 + ] + ] + }, + "icon-image": "{subclass}", + "text-field": "{name}", + "visibility": "visible", + "text-anchor": "top", + "text-offset": [ + 0, + 1.2 + ], + "text-padding": 2, + "text-max-width": 6, + "icon-allow-overlap": true + }, + "paint": { + "text-color": "#660033", + "icon-opacity": 1, + "icon-halo-blur": 1, + "text-halo-blur": 0.5, + "icon-halo-color": "rgba(255, 255, 255, 1)", + "icon-halo-width": 0.5, + "text-halo-color": "#ffffff", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "zoo" + ], + [ + "==", + "subclass", + "zoo" + ] + ], + "order": 196 + } + ] +} \ No newline at end of file diff --git a/layers/poi/update_poi_point.sql b/layers/poi/update_poi_point.sql new file mode 100644 index 0000000..ddabb77 --- /dev/null +++ b/layers/poi/update_poi_point.sql @@ -0,0 +1,171 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_poi_point; +DROP TRIGGER IF EXISTS trigger_refresh ON poi_point.updates; +DROP TRIGGER IF EXISTS trigger_store ON osm_poi_point; + +CREATE SCHEMA IF NOT EXISTS poi_point; + +CREATE TABLE IF NOT EXISTS poi_point.osm_ids +( + osm_id bigint PRIMARY KEY +); + +-- etldoc: osm_poi_point -> osm_poi_point +CREATE OR REPLACE FUNCTION update_osm_poi_point(full_update bool) RETURNS void AS +$$ +BEGIN + UPDATE osm_poi_point + SET subclass = 'subway' + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_point.osm_ids)) + AND station = 'subway' + AND subclass = 'station'; + + UPDATE osm_poi_point + SET subclass = 'halt' + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_point.osm_ids)) + AND funicular = 'yes' + AND subclass = 'station'; + + -- ATM without name + -- use either operator or network + -- (using name for ATM is discouraged, see osm wiki) + UPDATE osm_poi_point + SET (name, tags) = ( + COALESCE(tags -> 'operator', tags -> 'network'), + tags || hstore('name', COALESCE(tags -> 'operator', tags -> 'network')) + ) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_point.osm_ids)) + AND subclass = 'atm' + AND name = '' + AND COALESCE(tags -> 'operator', tags -> 'network') IS NOT NULL; + + -- Parcel locker without name + -- use either brand or operator and add ref if present + -- (using name for parcel lockers is discouraged, see osm wiki) + UPDATE osm_poi_point + SET (name, tags) = ( + TRIM(CONCAT(COALESCE(tags -> 'brand', tags -> 'operator'), concat(' ', tags -> 'ref'))), + tags || hstore('name', TRIM(CONCAT(COALESCE(tags -> 'brand', tags -> 'operator'), concat(' ', tags -> 'ref')))) + ) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_point.osm_ids)) + AND subclass IN ('parcel_locker', 'charging_station') + AND name = '' + AND COALESCE(tags -> 'brand', tags -> 'operator') IS NOT NULL; + + UPDATE osm_poi_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_point.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); + +END; +$$ LANGUAGE plpgsql; + +SELECT update_osm_poi_point(TRUE); + +-- etldoc: osm_poi_stop_rank -> osm_poi_point +CREATE OR REPLACE FUNCTION update_osm_poi_point_agg() RETURNS void AS +$$ +BEGIN + UPDATE osm_poi_point p + SET + agg_stop = CASE + WHEN p.subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') + THEN 1 + END + WHERE + agg_stop IS DISTINCT FROM CASE + WHEN p.subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') + THEN 1 + END; + + UPDATE osm_poi_point p + SET + agg_stop = ( + CASE + WHEN p.subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') + AND (r.rk IS NULL OR r.rk = 1) + THEN 1 + END) + FROM osm_poi_stop_rank r + WHERE p.osm_id = r.osm_id AND + agg_stop IS DISTINCT FROM ( + CASE + WHEN p.subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') + AND (r.rk IS NULL OR r.rk = 1) + THEN 1 + END); + +END; +$$ LANGUAGE plpgsql; + +ALTER TABLE osm_poi_point + ADD COLUMN IF NOT EXISTS agg_stop integer DEFAULT NULL; +SELECT update_osm_poi_point_agg(); + +-- Handle updates + +CREATE OR REPLACE FUNCTION poi_point.store() RETURNS trigger AS +$$ +BEGIN + INSERT INTO poi_point.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS poi_point.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION poi_point.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO poi_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION poi_point.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh poi_point'; + + -- Analyze tracking and source tables before performing update + ANALYZE poi_point.osm_ids; + ANALYZE osm_poi_point; + + PERFORM update_osm_poi_point(FALSE); + REFRESH MATERIALIZED VIEW osm_poi_stop_centroid; + REFRESH MATERIALIZED VIEW osm_poi_stop_rank; + PERFORM update_osm_poi_point_agg(); + -- noinspection SqlWithoutWhere + DELETE FROM poi_point.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM poi_point.updates; + + RAISE LOG 'Refresh poi_point done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE + ON osm_poi_point + FOR EACH ROW +EXECUTE PROCEDURE poi_point.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE + ON osm_poi_point + FOR EACH STATEMENT +EXECUTE PROCEDURE poi_point.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON poi_point.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE poi_point.refresh(); diff --git a/layers/poi/update_poi_polygon.sql b/layers/poi/update_poi_polygon.sql new file mode 100644 index 0000000..fc8db96 --- /dev/null +++ b/layers/poi/update_poi_polygon.sql @@ -0,0 +1,125 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_poi_polygon; +DROP TRIGGER IF EXISTS trigger_store ON osm_poi_polygon; +DROP TRIGGER IF EXISTS trigger_refresh ON poi_polygon.updates; + +CREATE SCHEMA IF NOT EXISTS poi_polygon; + +CREATE TABLE IF NOT EXISTS poi_polygon.osm_ids +( + osm_id bigint PRIMARY KEY +); + +-- etldoc: osm_poi_polygon -> osm_poi_polygon + +CREATE OR REPLACE FUNCTION update_poi_polygon(full_update boolean) RETURNS void AS +$$ + UPDATE osm_poi_polygon + SET geometry = + CASE + WHEN ST_NPoints(ST_ConvexHull(geometry)) = ST_NPoints(geometry) + THEN ST_Centroid(geometry) + ELSE ST_PointOnSurface(geometry) + END + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_polygon.osm_ids)) + AND ST_GeometryType(geometry) <> 'ST_Point' + AND ST_IsValid(geometry); + + UPDATE osm_poi_polygon + SET subclass = 'subway' + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_polygon.osm_ids)) + AND station = 'subway' + AND subclass = 'station'; + + UPDATE osm_poi_polygon + SET subclass = 'halt' + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_polygon.osm_ids)) + AND funicular = 'yes' + AND subclass = 'station'; + + -- Parcel locker and charging_station without name + -- use either brand or operator and add ref if present + -- (using name for parcel lockers is discouraged, see osm wiki) + UPDATE osm_poi_polygon + SET (name, tags) = ( + TRIM(CONCAT(COALESCE(tags -> 'brand', tags -> 'operator'), concat(' ', tags -> 'ref'))), + tags || hstore('name', TRIM(CONCAT(COALESCE(tags -> 'brand', tags -> 'operator'), concat(' ', tags -> 'ref')))) + ) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_polygon.osm_ids)) + AND subclass IN ('parcel_locker', 'charging_station') + AND name = '' + AND COALESCE(tags -> 'brand', tags -> 'operator') IS NOT NULL; + + UPDATE osm_poi_polygon + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_polygon.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); + +$$ LANGUAGE SQL; + +SELECT update_poi_polygon(true); + +-- Handle updates + +CREATE OR REPLACE FUNCTION poi_polygon.store() RETURNS trigger AS +$$ +BEGIN + INSERT INTO poi_polygon.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS poi_polygon.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION poi_polygon.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO poi_polygon.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION poi_polygon.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh poi_polygon'; + + -- Analyze tracking and source tables before performing update + ANALYZE poi_polygon.osm_ids; + ANALYZE osm_poi_polygon; + + PERFORM update_poi_polygon(false); + -- noinspection SqlWithoutWhere + DELETE FROM poi_polygon.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM poi_polygon.updates; + + RAISE LOG 'Refresh poi_polygon done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE + ON osm_poi_polygon + FOR EACH ROW +EXECUTE PROCEDURE poi_polygon.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE + ON osm_poi_polygon + FOR EACH STATEMENT +EXECUTE PROCEDURE poi_polygon.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON poi_polygon.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE poi_polygon.refresh(); diff --git a/layers/transportation/README.md b/layers/transportation/README.md new file mode 100644 index 0000000..ee844c8 --- /dev/null +++ b/layers/transportation/README.md @@ -0,0 +1,10 @@ +## transportation + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#transportation** + +### Mapping Diagram +![Mapping diagram for transportation](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for transportation](etl_diagram.png?raw=true) diff --git a/layers/transportation/class.sql b/layers/transportation/class.sql new file mode 100644 index 0000000..00b8c38 --- /dev/null +++ b/layers/transportation/class.sql @@ -0,0 +1,93 @@ +CREATE OR REPLACE FUNCTION brunnel(is_bridge bool, is_tunnel bool, is_ford bool) RETURNS text AS +$$ +SELECT CASE + WHEN is_bridge THEN 'bridge' + WHEN is_tunnel THEN 'tunnel' + WHEN is_ford THEN 'ford' + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; + +-- The classes for highways are derived from the classes used in ClearTables +-- https://github.com/ClearTables/ClearTables/blob/master/transportation.lua +CREATE OR REPLACE FUNCTION highway_class(highway text, public_transport text, construction text) RETURNS text AS +$$ +SELECT CASE + %%FIELD_MAPPING: class %% + END; +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; + +-- The classes for railways are derived from the classes used in ClearTables +-- https://github.com/ClearTables/ClearTables/blob/master/transportation.lua +CREATE OR REPLACE FUNCTION railway_class(railway text) RETURNS text AS +$$ +SELECT CASE + WHEN railway IN ('rail', 'narrow_gauge', 'preserved', 'funicular') THEN 'rail' + WHEN railway IN ('subway', 'light_rail', 'monorail', 'tram') THEN 'transit' + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; + +-- Limit service to only the most important values to ensure +-- we always know the values of service +CREATE OR REPLACE FUNCTION service_value(service text) RETURNS text AS +$$ +SELECT CASE + WHEN service IN ('spur', 'yard', 'siding', 'crossover', 'driveway', 'alley', 'parking_aisle') THEN service + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; + +-- Limit surface to only the most important values to ensure +-- we always know the values of surface +CREATE OR REPLACE FUNCTION surface_value(surface text) RETURNS text AS +$$ +SELECT CASE + WHEN surface IN ('paved', 'asphalt', 'cobblestone', 'concrete', 'concrete:lanes', 'concrete:plates', 'metal', + 'paving_stones', 'sett', 'unhewn_cobblestone', 'wood', 'grade1') THEN 'paved' + WHEN surface IN ('unpaved', 'compacted', 'dirt', 'earth', 'fine_gravel', 'grass', 'grass_paver', 'gravel', + 'gravel_turf', 'ground', 'ice', 'mud', 'pebblestone', 'salt', 'sand', 'snow', 'woodchips') + THEN 'unpaved' + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; + +-- Determine which transportation features are shown at zoom 12 +CREATE OR REPLACE FUNCTION transportation_filter_z12(highway text, construction text) RETURNS boolean AS +$$ +SELECT CASE + WHEN highway IN ('unclassified', 'residential') THEN TRUE + WHEN highway_class(highway, '', construction) IN + ( + 'motorway', 'trunk', 'primary', 'secondary', 'tertiary', 'raceway', + 'motorway_construction', 'trunk_construction', 'primary_construction', + 'secondary_construction', 'tertiary_construction', 'raceway_construction', + 'busway', 'bus_guideway' + ) THEN TRUE --includes ramps + ELSE FALSE + END +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; + +-- Determine which transportation features are shown at zoom 13 +-- Assumes that piers have already been excluded +CREATE OR REPLACE FUNCTION transportation_filter_z13(highway text, + public_transport text, + construction text, + service text) RETURNS boolean AS +$$ +SELECT CASE + WHEN transportation_filter_z12(highway, construction) THEN TRUE + WHEN highway = 'service' OR construction = 'service' THEN service NOT IN ('driveway', 'parking_aisle') + WHEN highway_class(highway, public_transport, construction) IN ('minor', 'minor_construction') THEN TRUE + ELSE FALSE + END +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; diff --git a/layers/transportation/etl_diagram.png b/layers/transportation/etl_diagram.png new file mode 100644 index 0000000..f960a37 Binary files /dev/null and b/layers/transportation/etl_diagram.png differ diff --git a/layers/transportation/highway_name.sql b/layers/transportation/highway_name.sql new file mode 100644 index 0000000..0588645 --- /dev/null +++ b/layers/transportation/highway_name.sql @@ -0,0 +1,11 @@ +CREATE OR REPLACE FUNCTION transportation_name_tags(geometry geometry, tags hstore, name text, name_en text, name_de text) RETURNS hstore AS +$$ +SELECT hstore(string_agg(nullif(slice_language_tags(tags || + hstore(ARRAY [ + 'name', CASE WHEN length(name) > 15 THEN osml10n_street_abbrev_all(name) ELSE NULLIF(name, '') END, + 'name:en', CASE WHEN length(name_en) > 15 THEN osml10n_street_abbrev_en(name_en) ELSE NULLIF(name_en, '') END, + 'name:de', CASE WHEN length(name_de) > 15 THEN osml10n_street_abbrev_de(name_de) ELSE NULLIF(name_de, '') END + ]))::text, + ''), ',')); +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; diff --git a/layers/transportation/mapping.yaml b/layers/transportation/mapping.yaml new file mode 100644 index 0000000..a2513bd --- /dev/null +++ b/layers/transportation/mapping.yaml @@ -0,0 +1,492 @@ +generalized_tables: +# etldoc: osm_railway_linestring_gen_z9 -> osm_railway_linestring_gen_z8 + railway_linestring_gen_z8: + source: railway_linestring_gen_z9 + tolerance: ZRES9 + +# etldoc: osm_railway_linestring_gen_z10 -> osm_railway_linestring_gen_z9 + railway_linestring_gen_z9: + source: railway_linestring_gen_z10 + tolerance: ZRES10 + +# etldoc: osm_railway_linestring_gen_z11 -> osm_railway_linestring_gen_z10 + railway_linestring_gen_z10: + source: railway_linestring_gen_z11 + tolerance: ZRES11 + +# etldoc: osm_railway_linestring_gen_z12 -> osm_railway_linestring_gen_z11 + railway_linestring_gen_z11: + source: railway_linestring_gen_z12 + tolerance: ZRES12 + +# etldoc: osm_railway_linestring -> osm_railway_linestring_gen_z12 + railway_linestring_gen_z12: + source: railway_linestring + sql_filter: railway IN ('rail', 'narrow_gauge', 'light_rail') AND service='' AND ST_IsValid(geometry) + tolerance: ZRES13 + +# etldoc: osm_aerialway_linestring -> osm_aerialway_linestring_gen_z12 + aerialway_linestring_gen_z12: + source: aerialway_linestring + sql_filter: ST_IsValid(geometry) + tolerance: ZRES13 + +# etldoc: osm_shipway_linestring_gen_z5 -> osm_shipway_linestring_gen_z4 + shipway_linestring_gen_z4: + source: shipway_linestring_gen_z5 + sql_filter: ST_Length(geometry)>2*ZRES0 + tolerance: ZRES5 + +# etldoc: osm_shipway_linestring_gen_z6 -> osm_shipway_linestring_gen_z5 + shipway_linestring_gen_z5: + source: shipway_linestring_gen_z6 + sql_filter: ST_Length(geometry)>ZRES0 + tolerance: ZRES6 + +# etldoc: osm_shipway_linestring_gen_z7 -> osm_shipway_linestring_gen_z6 + shipway_linestring_gen_z6: + source: shipway_linestring_gen_z7 + sql_filter: ST_Length(geometry)>ZRES1 + tolerance: ZRES7 + +# etldoc: osm_shipway_linestring_gen_z8 -> osm_shipway_linestring_gen_z7 + shipway_linestring_gen_z7: + source: shipway_linestring_gen_z8 + sql_filter: ST_Length(geometry)>ZRES2 + tolerance: ZRES8 + +# etldoc: osm_shipway_linestring_gen_z9 -> osm_shipway_linestring_gen_z8 + shipway_linestring_gen_z8: + source: shipway_linestring_gen_z9 + sql_filter: ST_Length(geometry)>ZRES3 + tolerance: ZRES9 + +# etldoc: osm_shipway_linestring_gen_z10 -> osm_shipway_linestring_gen_z9 + shipway_linestring_gen_z9: + source: shipway_linestring_gen_z10 + sql_filter: ST_Length(geometry)>ZRES4 + tolerance: ZRES10 + +# etldoc: osm_shipway_linestring_gen_z11 -> osm_shipway_linestring_gen_z10 + shipway_linestring_gen_z10: + source: shipway_linestring_gen_z11 + sql_filter: ST_Length(geometry)>ZRES5 + tolerance: ZRES11 + +# etldoc: osm_shipway_linestring_gen_z12 -> osm_shipway_linestring_gen_z11 + shipway_linestring_gen_z11: + source: shipway_linestring_gen_z12 + tolerance: ZRES12 + +# etldoc: osm_shipway_linestring -> osm_shipway_linestring_gen_z12 + shipway_linestring_gen_z12: + source: shipway_linestring + sql_filter: ST_IsValid(geometry) + tolerance: ZRES13 + +# etldoc: osm_highway_linestring -> osm_highway_linestring_gen_z11 + highway_linestring_gen_z11: + source: highway_linestring + sql_filter: (highway IN ('motorway', 'trunk', 'primary', 'secondary', 'tertiary', 'motorway_link', 'trunk_link', 'primary_link', 'secondary_link', 'tertiary_link', 'busway', 'bus_guideway') OR highway = 'construction' AND construction IN ('motorway', 'trunk', 'primary', 'secondary', 'tertiary', 'motorway_link', 'trunk_link', 'primary_link', 'secondary_link', 'tertiary_link', 'busway', 'bus_guideway')) AND NOT is_area AND ST_IsValid(geometry) + tolerance: ZRES12 + +name_field: &name + name: name + key: name + type: string +name_en_field: &name_en + name: name_en + key: name:en + type: string +name_de_field: &name_de + name: name_de + key: name:de + type: string +tunnel_field: &tunnel + key: tunnel + name: is_tunnel + type: bool +bridge_field: &bridge + key: bridge + name: is_bridge + type: bool +ramp_field: &ramp + key: ramp + name: is_ramp + type: bool +ford_field: &ford + key: ford + name: is_ford + type: bool +oneway_field: &oneway + key: oneway + name: is_oneway + type: direction +area_field: &area + name: is_area + key: area + type: bool +service_field: &service + key: service + name: service + type: string +access_field: &access + key: access + name: access + type: string +toll_field: &toll + key: toll + name: toll + type: bool +usage_field: &usage + key: usage + name: usage + type: string +public_transport_field: &public_transport + key: public_transport + name: public_transport + type: string +ref_field: &ref + key: ref + name: ref + type: string +network_field: &network + key: network + name: network + type: string +layer_field: &layer + key: layer + name: layer + type: integer +level_field: &level + key: level + name: level + type: integer +indoor_field: &indoor + key: indoor + name: indoor + type: bool +man_made_field: &man_made + key: man_made + name: man_made + type: string +z_order_field: &z_order + name: z_order + type: wayzorder +bicycle_field: &bicycle + key: bicycle + name: bicycle + type: string +foot_field: &foot + key: foot + name: foot + type: string +horse_field: &horse + key: horse + name: horse + type: string +mtb_scale_field: &mtb_scale + key: mtb:scale + name: mtb_scale + type: string +sac_scale_field: &sac_scale + key: sac_scale + name: sac_scale + type: string +surface_field: &surface + key: surface + name: surface + type: string +expressway_field: &expressway + key: expressway + name: expressway + type: bool + +tables: +# etldoc: imposm3 -> osm_highway_linestring + highway_linestring: + type: linestring + _resolve_wikidata: false + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - name: highway + key: highway + type: string + - name: construction + key: construction + type: string + - name: tracktype + key: tracktype + type: string + - *ref + - *network + - *z_order + - *layer + - *level + - *indoor + - *name + - *name_en + - *name_de + - name: tags + type: hstore_tags + - *tunnel + - *bridge + - *ramp + - *ford + - *oneway + - *area + - *service + - *access + - *toll + - *usage + - *public_transport + - *man_made + - *bicycle + - *foot + - *horse + - *mtb_scale + - *sac_scale + - *surface + - *expressway + mapping: + highway: + - motorway + - motorway_link + - trunk + - trunk_link + - primary + - primary_link + - secondary + - secondary_link + - tertiary + - tertiary_link + - unclassified + - residential + - living_street + - road + - pedestrian + - path + - footway + - cycleway + - steps + - bridleway + - corridor + - service + - track + - raceway + - busway + - bus_guideway + - construction + public_transport: + - platform + man_made: + - pier + service: + - driveway + - parking_aisle + +# etldoc: imposm3 -> osm_railway_linestring + railway_linestring: + type: linestring + _resolve_wikidata: false + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - key: railway + name: railway + type: string + - *ref + - *network + - *z_order + - *layer + - *level + - *indoor + - *name + - *name_en + - *name_de + - name: tags + type: hstore_tags + - *tunnel + - *bridge + - *ramp + - *ford + - *area + - *service + - *usage + mapping: + railway: + - rail + - narrow_gauge + - preserved + - funicular + - subway + - light_rail + - monorail + - tram + +# etldoc: imposm3 -> osm_aerialway_linestring + aerialway_linestring: + type: linestring + _resolve_wikidata: false + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - key: aerialway + name: aerialway + type: string + - *z_order + - *layer + - *name + - *name_en + - *name_de + - name: tags + type: hstore_tags + - *tunnel + - *bridge + - *ramp + - *ford + - *oneway + - *area + - *service + - *usage + mapping: + aerialway: + - chair_lift + - drag_lift + - platter + - t-bar + - gondola + - cable_car + - j-bar + - mixed_lift + +# etldoc: imposm3 -> osm_shipway_linestring + shipway_linestring: + type: linestring + _resolve_wikidata: false + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - key: route + name: shipway + type: string + - *z_order + - *layer + - *name + - *name_en + - *name_de + - name: tags + type: hstore_tags + - *tunnel + - *bridge + - *ramp + - *ford + - *area + - *service + - *usage + mapping: + route: + - ferry + +# etldoc: imposm3 -> osm_highway_polygon + highway_polygon: + type: polygon + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - name: highway + key: highway + type: string + - name: z_order + type: wayzorder + - *layer + - *level + - *indoor + - name: is_area + key: area + type: bool + - *public_transport + - *man_made + - *service + mapping: + highway: + - path + - cycleway + - bridleway + - footway + - corridor + - pedestrian + - steps + public_transport: + - platform + man_made: + - bridge + - pier + + # etldoc: imposm3 -> highway_point + highway_point: + type: point + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - name: highway + key: highway + type: string + - name: z_order + type: wayzorder + - *layer + - *level + - *name + - *name_en + - *name_de + - name: tags + type: hstore_tags + - name: ref + key: ref + type: string + mapping: + highway: + - motorway_junction + +# TODO: Future table for joining networks +# etldoc: imposm3 -> osm_route_member + route_member: + type: relation_member + columns: + - name: osm_id + type: id + - name: member + type: member_id + - name: role + type: member_role + - name: type + type: member_type + - *ref + - *network + - *name + - name: osmc_symbol + key: osmc:symbol + type: string + - name: colour + key: colour + type: string + - name: ref_colour + key: ref:colour + type: string + mapping: + route: + - road + - hiking diff --git a/layers/transportation/mapping_diagram.png b/layers/transportation/mapping_diagram.png new file mode 100644 index 0000000..c47f739 Binary files /dev/null and b/layers/transportation/mapping_diagram.png differ diff --git a/layers/transportation/network_type.sql b/layers/transportation/network_type.sql new file mode 100644 index 0000000..b85196e --- /dev/null +++ b/layers/transportation/network_type.sql @@ -0,0 +1,57 @@ +DROP TRIGGER IF EXISTS trigger_store_transportation_route_member ON osm_route_member; +DROP TRIGGER IF EXISTS trigger_store_transportation_highway_linestring ON osm_highway_linestring; +DROP TRIGGER IF EXISTS trigger_flag_transportation_name ON transportation_name.network_changes; +DROP TRIGGER IF EXISTS trigger_refresh_network ON transportation_name.updates_network; + +DROP TRIGGER IF EXISTS trigger_store_transportation_name_network ON osm_transportation_name_network; +DROP TRIGGER IF EXISTS trigger_flag_name ON transportation_name.name_changes; +DROP TRIGGER IF EXISTS trigger_refresh_name ON transportation_name.updates_name; + +DO +$$ + BEGIN + PERFORM 'route_network_type'::regtype; + EXCEPTION + WHEN undefined_object THEN + CREATE TYPE route_network_type AS enum ( + 'us-interstate', 'us-highway', 'us-state', + 'ca-transcanada', 'ca-provincial-arterial', 'ca-provincial', + 'gb-motorway', 'gb-trunk', 'gb-primary', + 'ie-motorway', 'ie-national', 'ie-regional', + 'e-road', + 'a-road' + ); + END +$$; + +-- Top-level national route networks that should display at the lowest zooms +CREATE OR REPLACE FUNCTION osm_national_network(network text) RETURNS boolean AS +$$ + SELECT network <> '' AND network IN ( + -- Canada + 'ca-transcanada', 'ca-provincial-arterial', + -- United States + 'us-interstate', 'us-highway', + -- UK + 'gb-motorway', 'gb-trunk', + -- Ireland + 'ie-motorway', 'ie-national', + -- Europe + 'e-road', + -- Asia + 'a-road' + ); +$$ LANGUAGE sql IMMUTABLE + PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION create_route_hstore(network TEXT, ref TEXT, name TEXT, colour TEXT, ref_colour TEXT) +RETURNS hstore AS $$ +SELECT CASE + WHEN network = '' THEN hstore('') + ELSE hstore( + ARRAY['network', 'ref', 'name', 'colour'], + ARRAY[network, NULLIF(ref, ''), NULLIF(name, ''), COALESCE(NULLIF(colour, ''), NULLIF(ref_colour, ''))] + ) + END; +$$ LANGUAGE sql IMMUTABLE + PARALLEL SAFE; diff --git a/layers/transportation/style.json b/layers/transportation/style.json new file mode 100644 index 0000000..48c16eb --- /dev/null +++ b/layers/transportation/style.json @@ -0,0 +1,7805 @@ +{ + "layers": [ + { + "id": "ferry", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 4, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#66f", + "line-width": { + "stops": [ + [ + 10, + 0.5 + ], + [ + 14, + 1.1 + ] + ] + }, + "line-dasharray": [ + 6, + 6 + ] + }, + "filter": [ + "all", + [ + "in", + "class", + "ferry" + ] + ], + "order": 24 + }, + { + "id": "tunnel_motorway_link_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 11, + "#c24e6b" + ], + [ + 12, + "#dc2a67" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 1.5 + ], + [ + 12, + 4 + ], + [ + 14, + 7.8 + ], + [ + 16, + 12 + ], + [ + 17, + 13 + ], + [ + 18, + 16 + ], + [ + 19, + 17 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "motorway_link" + ], + [ + "==", + "ramp", + 1 + ], + [ + "==", + "brunnel", + "tunnel" + ] + ], + "order": 28 + }, + { + "id": "tunnel_service_track_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#cfcdca", + "line-width": { + "base": 1.2, + "stops": [ + [ + 15, + 1 + ], + [ + 16, + 4 + ], + [ + 20, + 11 + ] + ] + }, + "line-dasharray": [ + 0.5, + 0.25 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "service", + "track" + ] + ], + "order": 29 + }, + { + "id": "tunnel_link_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#e9ac77", + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1 + ], + [ + 13, + 3 + ], + [ + 14, + 4 + ], + [ + 20, + 15 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "ramp", + "1" + ], + [ + "==", + "brunnel", + "tunnel" + ], + [ + ">", + "layer", + 0 + ] + ], + "order": 30 + }, + { + "id": "tunnel_street_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#cfcdca", + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 0.5 + ], + [ + 13, + 1 + ], + [ + 14, + 4 + ], + [ + 20, + 15 + ] + ] + }, + "line-opacity": { + "stops": [ + [ + 12, + 0 + ], + [ + 12.5, + 1 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "street", + "street_limited" + ] + ], + "order": 31 + }, + { + "id": "tunnel_tertiary_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#8f8f8f", + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 2.5 + ], + [ + 12, + 5 + ], + [ + 13, + 5 + ], + [ + 14, + 9 + ], + [ + 15, + 10 + ], + [ + 16, + 18 + ], + [ + 17, + 21 + ], + [ + 18, + 27 + ] + ] + }, + "line-opacity": 1, + "line-dasharray": [ + 0.5, + 0.25 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "tertiary" + ] + ], + "order": 32 + }, + { + "id": "tunnel_secondary_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#707d05", + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 3.5 + ], + [ + 12, + 5 + ], + [ + 13, + 5 + ], + [ + 14, + 9 + ], + [ + 15, + 10 + ], + [ + 16, + 18 + ], + [ + 17, + 21 + ], + [ + 18, + 27 + ] + ] + }, + "line-opacity": 1, + "line-dasharray": [ + 0.5, + 0.25 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "secondary" + ] + ], + "order": 33 + }, + { + "id": "tunnel_trunk_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 11, + "#cf6649" + ], + [ + 12, + "#c84e2f" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 3.5 + ], + [ + 12, + 6 + ], + [ + 15, + 10 + ], + [ + 16, + 18 + ], + [ + 17, + 21 + ], + [ + 18, + 27 + ] + ] + }, + "line-dasharray": [ + 0.5, + 0.25 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "trunk" + ] + ], + "order": 34 + }, + { + "id": "tunnel_trunk_construction_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(255, 255, 255, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 3.5 + ], + [ + 12, + 6 + ], + [ + 15, + 10 + ], + [ + 16, + 18 + ], + [ + 17, + 21 + ], + [ + 18, + 25 + ] + ] + }, + "line-dasharray": [ + 1, + 0 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "trunk_construction" + ] + ], + "order": 35 + }, + { + "id": "tunnel_primary_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 6, + "#a06b00" + ], + [ + 10, + "rgba(160, 116, 0, 1)" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 3.5 + ], + [ + 12, + 6 + ], + [ + 15, + 10 + ], + [ + 16, + 18 + ], + [ + 17, + 21 + ], + [ + 18, + 27 + ] + ] + }, + "line-dasharray": [ + 0.5, + 0.25 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "primary" + ] + ], + "order": 36 + }, + { + "id": "tunnel_motorway_construction_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 6, + "#dc2a67" + ], + [ + 10, + "#c24e6b" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0 + ], + [ + 7, + 1.75 + ], + [ + 18, + 27 + ] + ] + }, + "line-dasharray": [ + 0.5, + 0.25 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "motorway_construction" + ], + [ + "==", + "brunnel", + "tunnel" + ] + ], + "order": 37 + }, + { + "id": "tunnel_motorway_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 6, + "#dc2a67" + ], + [ + 10, + "#c24e6b" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0 + ], + [ + 7, + 1.75 + ], + [ + 18, + 27 + ] + ] + }, + "line-dasharray": [ + 0.5, + 0.25 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "motorway" + ], + [ + "==", + "brunnel", + "tunnel" + ] + ], + "order": 38 + }, + { + "id": "tunnel_path_casing_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(50, 50, 50, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.7 + ], + [ + 14, + 4 + ], + [ + 15, + 4.3 + ], + [ + 17, + 4.3 + ], + [ + 18, + 4.6 + ] + ] + }, + "line-dasharray": [ + 1, + 0.5 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "brunnel", + "tunnel" + ], + [ + "==", + "class", + "path" + ] + ], + "order": 39 + }, + { + "id": "tunnel_path_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(255, 255, 255, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 2.7 + ], + [ + 14, + 3 + ], + [ + 15, + 3.3 + ], + [ + 17, + 3.3 + ], + [ + 18, + 3.6 + ] + ] + }, + "line-dasharray": [ + 1, + 0 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "brunnel", + "tunnel" + ], + [ + "==", + "class", + "path" + ] + ], + "order": 40 + }, + { + "id": "tunnel_path_cycleway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "paint": { + "line-color": "#0000ff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 0.8 + ], + [ + 14, + 1 + ], + [ + 15, + 1.3 + ], + [ + 17, + 1.3 + ], + [ + 18, + 1.6 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "path" + ], + [ + "any", + [ + "==", + "subclass", + "cycleway" + ], + [ + "==", + "bicycle", + "designated" + ] + ] + ], + "order": 41 + }, + { + "id": "tunnel_path_bridleway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "paint": { + "line-color": "#008000", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 0.8 + ], + [ + 14, + 1 + ], + [ + 15, + 1.3 + ], + [ + 17, + 1.3 + ], + [ + 18, + 1.6 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "path" + ], + [ + "any", + [ + "==", + "subclass", + "bridleway" + ], + [ + "==", + "horse", + "designated" + ] + ], + [ + "!=", + "bicycle", + "designated" + ] + ], + "order": 42 + }, + { + "id": "tunnel_path_footway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "paint": { + "line-color": "#fa8072", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 0.8 + ], + [ + 14, + 1 + ], + [ + 15, + 1.3 + ], + [ + 17, + 1.3 + ], + [ + 18, + 1.6 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "path" + ], + [ + "in", + "subclass", + "footway", + "path" + ], + [ + "!=", + "bicycle", + "designated" + ], + [ + "!=", + "horse", + "designated" + ] + ], + "order": 43 + }, + { + "id": "tunnel_motorway_link", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 8, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#f1bcc6", + "line-width": { + "base": 1.2, + "stops": [ + [ + 8, + 0.7 + ], + [ + 11, + 0.9 + ], + [ + 12, + 3 + ], + [ + 14, + 6.6 + ], + [ + 16, + 10.4 + ], + [ + 17, + 11.4 + ], + [ + 18, + 14.4 + ], + [ + 19, + 15.4 + ] + ] + }, + "line-opacity": 1, + "line-dasharray": [ + 0.5, + 0.25 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "motorway" + ], + [ + "==", + "ramp", + 1 + ], + [ + "==", + "brunnel", + "tunnel" + ] + ], + "order": 44 + }, + { + "id": "tunnel_service_track", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 15, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#f2f2f2", + "line-width": { + "base": 1.2, + "stops": [ + [ + 15, + 0.8 + ], + [ + 16, + 1.9 + ], + [ + 17, + 3.1 + ], + [ + 18, + 3.9 + ], + [ + 19, + 6.9 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "service", + "track" + ] + ], + "order": 45 + }, + { + "id": "tunnel_service_track_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 15, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 15, + 0.8 + ], + [ + 16, + 1.9 + ], + [ + 17, + 3.1 + ], + [ + 18, + 3.9 + ], + [ + 19, + 6.9 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "service_construction", + "track_construction" + ] + ], + "order": 46 + }, + { + "id": "tunnel_link", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fff4c6", + "line-width": { + "base": 1.2, + "stops": [ + [ + 12.5, + 0 + ], + [ + 13, + 1.5 + ], + [ + 14, + 2.5 + ], + [ + 20, + 11.5 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "ramp", + "1" + ], + [ + "==", + "brunnel", + "tunnel" + ], + [ + ">", + "layer", + 0 + ] + ], + "order": 47 + }, + { + "id": "tunnel_minor", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13.5, + 0 + ], + [ + 14, + 2.5 + ], + [ + 20, + 11.5 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "minor" + ] + ], + "order": 48 + }, + { + "id": "tunnel_minor_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13.5, + 0 + ], + [ + 14, + 2.5 + ], + [ + 20, + 11.5 + ] + ] + }, + "line-opacity": 1, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "minor_construction" + ] + ], + "order": 49 + }, + { + "id": "tunnel_tertiary", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#ffffff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 1.9 + ], + [ + 12, + 3 + ], + [ + 13, + 3.9 + ], + [ + 14, + 7.8 + ], + [ + 15, + 8.8 + ], + [ + 16, + 16.4 + ], + [ + 17, + 19.4 + ], + [ + 18, + 25.4 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "tertiary" + ] + ], + "order": 50 + }, + { + "id": "tunnel_tertiary_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#ffffff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 1.9 + ], + [ + 12, + 3 + ], + [ + 13, + 3.9 + ], + [ + 14, + 7.8 + ], + [ + 15, + 8.8 + ], + [ + 16, + 16.4 + ], + [ + 17, + 19.4 + ], + [ + 18, + 25.4 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "tertiary_construction" + ] + ], + "order": 51 + }, + { + "id": "tunnel_secondary", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 8, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fafcd7", + "line-width": { + "base": 1.2, + "stops": [ + [ + 8, + 1 + ], + [ + 9, + 1.1 + ], + [ + 10, + 1.1 + ], + [ + 11, + 2.9 + ], + [ + 12, + 4.3 + ], + [ + 12, + 4.3 + ], + [ + 14, + 7.6 + ], + [ + 15, + 8.6 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "secondary" + ] + ], + "order": 52 + }, + { + "id": "tunnel_secondary_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 8, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fafcd7", + "line-width": { + "base": 1.2, + "stops": [ + [ + 8, + 1 + ], + [ + 9, + 1.1 + ], + [ + 10, + 1.1 + ], + [ + 11, + 2.9 + ], + [ + 12, + 4.3 + ], + [ + 12, + 4.3 + ], + [ + 14, + 7.6 + ], + [ + 15, + 8.6 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "secondary_construction" + ] + ], + "order": 53 + }, + { + "id": "tunnel_primary", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#feecd5", + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0.4 + ], + [ + 6, + 0.6 + ], + [ + 7, + 1 + ], + [ + 8, + 1.4 + ], + [ + 9, + 1.9 + ], + [ + 10, + 1.9 + ], + [ + 11, + 4.5 + ], + [ + 12, + 5 + ], + [ + 15, + 8.6 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "primary" + ] + ], + "order": 54 + }, + { + "id": "tunnel_primary_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#feecd5", + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0.4 + ], + [ + 6, + 0.6 + ], + [ + 7, + 1 + ], + [ + 8, + 1.4 + ], + [ + 9, + 1.9 + ], + [ + 10, + 1.9 + ], + [ + 11, + 4.5 + ], + [ + 12, + 5 + ], + [ + 15, + 8.6 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + }, + "line-opacity": 1, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "primary_construction" + ] + ], + "order": 55 + }, + { + "id": "tunnel_trunk", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 5, + "#f5977a" + ], + [ + 11, + "#fcd7cc" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0.4 + ], + [ + 6, + 0.6 + ], + [ + 7, + 1 + ], + [ + 8, + 1.4 + ], + [ + 9, + 1.9 + ], + [ + 10, + 1.9 + ], + [ + 11, + 4.5 + ], + [ + 12, + 5 + ], + [ + 15, + 8.6 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "trunk" + ] + ], + "order": 56 + }, + { + "id": "tunnel_trunk_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#f9b29c", + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0.4 + ], + [ + 6, + 0.6 + ], + [ + 7, + 1 + ], + [ + 8, + 1.4 + ], + [ + 9, + 1.9 + ], + [ + 10, + 1.9 + ], + [ + 11, + 4.5 + ], + [ + 12, + 5 + ], + [ + 15, + 8.6 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + }, + "line-opacity": 1, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "trunk_construction" + ] + ], + "order": 57 + }, + { + "id": "tunnel_motorway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 5, + "#e66e89" + ], + [ + 11, + "#f1bcc6" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0 + ], + [ + 7, + 1 + ], + [ + 18, + 25 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "motorway" + ], + [ + "==", + "brunnel", + "tunnel" + ] + ], + "order": 58 + }, + { + "id": "tunnel_motorway_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 5, + "#e66e89" + ], + [ + 11, + "#f1bcc6" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0 + ], + [ + 7, + 1 + ], + [ + 18, + 25 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "motorway_construction" + ], + [ + "==", + "brunnel", + "tunnel" + ] + ], + "order": 59 + }, + { + "id": "tunnel_major_rail", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(166, 166, 166, 1)", + "line-width": { + "base": 1.4, + "stops": [ + [ + 8, + 0.8 + ], + [ + 15, + 3 + ], + [ + 20, + 5.4 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "rail" + ] + ], + "order": 60 + }, + { + "id": "tunnel_major_rail_hatching", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 9, + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 8, + "rgba(114, 114, 114, 0.44)" + ], + [ + 10, + "rgba(199, 199, 199, 1)" + ] + ] + }, + "line-width": { + "base": 1.4, + "stops": [ + [ + 8, + 0 + ], + [ + 15, + 2 + ], + [ + 20, + 4 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "brunnel", + "tunnel", + "tunnel" + ], + [ + "==", + "class", + "rail" + ] + ], + "order": 61 + }, + { + "id": "road_area_pier", + "type": "fill", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": "rgba(246, 241, 229, 1)", + "fill-antialias": true + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "Polygon" + ], + [ + "==", + "class", + "pier" + ] + ], + "order": 62 + }, + { + "id": "road_pier", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "rgba(246, 241, 229, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 15, + 1 + ], + [ + 17, + 4 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "class", + "pier" + ] + ], + "order": 63 + }, + { + "id": "road_area_bridge", + "type": "fill", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": "#b8b8b8", + "fill-antialias": true + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "Polygon" + ], + [ + "==", + "class", + "bridge" + ] + ], + "order": 64 + }, + { + "id": "road_area_platform", + "type": "fill", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": "#bababa" + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "Polygon" + ], + [ + "!has", + "brunnel" + ], + [ + "==", + "class", + "path" + ], + [ + "==", + "subclass", + "platform" + ] + ], + "order": 65 + }, + { + "id": "road_area_pedestrian", + "type": "fill", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": "#dddde8" + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "Polygon" + ], + [ + "!has", + "brunnel" + ], + [ + "!in", + "class", + "bridge", + "pier" + ] + ], + "order": 66 + }, + { + "id": "road_service_track_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#bbbbbb", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 2 + ], + [ + 15, + 3.5 + ], + [ + 16, + 7 + ], + [ + 17, + 8.5 + ], + [ + 18, + 11 + ], + [ + 19, + 12 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "service", + "track" + ] + ], + "order": 67 + }, + { + "id": "road_primary_link_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-cap": "butt", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 11, + "#c38a27" + ], + [ + 12, + "#a06b00" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 1.5 + ], + [ + 12, + 4 + ], + [ + 13, + 4 + ], + [ + 14, + 7.8 + ], + [ + 16, + 12 + ], + [ + 17, + 13 + ], + [ + 18, + 16 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "==", + "ramp", + 1 + ], + [ + "in", + "class", + "primary" + ] + ], + "order": 68 + }, + { + "id": "road_trunk_link_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-cap": "butt", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 11, + "#cf6649" + ], + [ + 12, + "#c84e2f" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 1.5 + ], + [ + 12, + 4 + ], + [ + 14, + 7.8 + ], + [ + 16, + 12 + ], + [ + 17, + 13 + ], + [ + 18, + 16 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "==", + "ramp", + 1 + ], + [ + "in", + "class", + "trunk" + ] + ], + "order": 69 + }, + { + "id": "road_motorway_link_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-cap": "butt", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 11, + "#c24e6b" + ], + [ + 12, + "#dc2a67" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 1.5 + ], + [ + 12, + 4 + ], + [ + 14, + 7.8 + ], + [ + 16, + 12 + ], + [ + 17, + 13 + ], + [ + 18, + 16 + ], + [ + 19, + 17 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "motorway" + ], + [ + "==", + "ramp", + 1 + ] + ], + "order": 70 + }, + { + "id": "road_minor_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#bbbbbb", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3 + ], + [ + 14, + 5 + ], + [ + 15, + 6 + ], + [ + 16, + 12 + ], + [ + 17, + 13 + ], + [ + 18, + 17 + ] + ] + }, + "line-opacity": { + "stops": [ + [ + 10, + 0 + ], + [ + 12.5, + 1 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "minor" + ], + [ + "!=", + "ramp", + "1" + ] + ], + "order": 71 + }, + { + "id": "road_tertiary_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#8f8f8f", + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 2.5 + ], + [ + 12, + 4 + ], + [ + 13, + 5 + ], + [ + 14, + 9 + ], + [ + 15, + 10 + ], + [ + 16, + 18 + ], + [ + 17, + 21 + ], + [ + 18, + 27 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "tertiary" + ], + [ + "!=", + "ramp", + 1 + ] + ], + "order": 72 + }, + { + "id": "road_secondary_link_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 11, + "#9eae23" + ], + [ + 12, + "#707d05" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 3.5 + ], + [ + 12, + 5 + ], + [ + 13, + 5 + ], + [ + 14, + 9 + ], + [ + 15, + 10 + ], + [ + 16, + 18 + ], + [ + 17, + 21 + ], + [ + 18, + 27 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "secondary" + ], + [ + "==", + "ramp", + 1 + ] + ], + "order": 73 + }, + { + "id": "road_secondary_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 11, + "#9eae23" + ], + [ + 12, + "#707d05" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 3.5 + ], + [ + 12, + 5 + ], + [ + 13, + 5 + ], + [ + 14, + 9 + ], + [ + 15, + 10 + ], + [ + 16, + 18 + ], + [ + 17, + 21 + ], + [ + 18, + 27 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "secondary" + ], + [ + "!=", + "ramp", + 1 + ] + ], + "order": 74 + }, + { + "id": "road_trunk_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-cap": "butt", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 11, + "rgba(160, 116, 0, 1)" + ], + [ + 12, + "#c84e2f" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 9, + 6 + ], + [ + 11, + 3.5 + ], + [ + 12, + 5 + ], + [ + 13, + 5 + ], + [ + 15, + 10 + ], + [ + 16, + 18 + ], + [ + 17, + 21 + ], + [ + 18, + 27 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "!=", + "ramp", + 1 + ], + [ + "in", + "class", + "trunk" + ], + [ + "!=", + "ramp", + 1 + ] + ], + "order": 75 + }, + { + "id": "road_primary_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 9, + "layout": { + "line-cap": "butt", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 10.9, + "rgba(255, 250, 234, 0.47)" + ], + [ + 11, + "rgba(160, 116, 0, 1)" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 9, + 6 + ], + [ + 11, + 3.5 + ], + [ + 12, + 5 + ], + [ + 13, + 5 + ], + [ + 15, + 10 + ], + [ + 16, + 18 + ], + [ + 17, + 21 + ], + [ + 18, + 27 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "!=", + "ramp", + 1 + ], + [ + "in", + "class", + "primary" + ], + [ + "!=", + "ramp", + 1 + ] + ], + "order": 76 + }, + { + "id": "road_motorway_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 5, + "layout": { + "line-cap": "butt", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 11, + "#c24e6b" + ], + [ + 12, + "#dc2a67" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0 + ], + [ + 7, + 1.75 + ], + [ + 18, + 27 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "motorway" + ], + [ + "!=", + "ramp", + 1 + ] + ], + "order": 77 + }, + { + "id": "road_pedestrian_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "#999999", + "line-width": { + "stops": [ + [ + 13, + 3 + ], + [ + 14, + 5 + ], + [ + 15, + 6 + ], + [ + 16, + 12 + ], + [ + 17, + 13 + ], + [ + 18, + 17 + ] + ] + } + }, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "class", + "path" + ], + [ + "==", + "subclass", + "pedestrian" + ], + [ + "!=", + "brunel", + "tunnel" + ] + ], + "order": 78 + }, + { + "id": "road_pedestrian", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "#dddde8", + "line-width": { + "stops": [ + [ + 13, + 1.9 + ], + [ + 14, + 3.8 + ], + [ + 15, + 4.8 + ], + [ + 16, + 10.4 + ], + [ + 17, + 11.4 + ], + [ + 18, + 15.4 + ] + ] + } + }, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "class", + "path" + ], + [ + "==", + "subclass", + "pedestrian" + ], + [ + "!=", + "brunel", + "tunnel" + ] + ], + "order": 79 + }, + { + "id": "road_path_steps_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 15, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(236, 236, 236, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 14, + 4 + ], + [ + 15, + 4.3 + ], + [ + 17, + 4.3 + ], + [ + 18, + 4.6 + ] + ] + }, + "line-opacity": 0.6 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "path" + ], + [ + "==", + "subclass", + "steps" + ] + ], + "order": 80 + }, + { + "id": "road_path_bridleway_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 15, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(236, 236, 236, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 14, + 3 + ], + [ + 15, + 3.3 + ], + [ + 17, + 3.3 + ], + [ + 18, + 3.6 + ] + ] + }, + "line-opacity": 0.6 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "path" + ], + [ + "any", + [ + "==", + "subclass", + "bridleway" + ], + [ + "==", + "horse", + "designated" + ] + ], + [ + "!=", + "bicycle", + "designated" + ] + ], + "order": 81 + }, + { + "id": "road_path_cycleway_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 15, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(236, 236, 236, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 14, + 3 + ], + [ + 15, + 3.3 + ], + [ + 17, + 3.3 + ], + [ + 18, + 3.6 + ] + ] + }, + "line-opacity": 0.6 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "path" + ], + [ + "any", + [ + "==", + "subclass", + "cycleway" + ], + [ + "==", + "bicycle", + "designated" + ] + ] + ], + "order": 82 + }, + { + "id": "road_path_footway_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 15, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(236, 236, 236, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 14, + 2.7 + ], + [ + 15, + 3 + ], + [ + 17, + 3.3 + ], + [ + 18, + 3.6 + ] + ] + }, + "line-opacity": 0.6 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "path" + ], + [ + "in", + "subclass", + "footway", + "path" + ], + [ + "!=", + "bicycle", + "designated" + ], + [ + "!=", + "horse", + "designated" + ] + ], + "order": 83 + }, + { + "id": "road_path_cycleway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 12, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#0000ff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 0.7 + ], + [ + 14, + 0.9 + ], + [ + 17, + 1 + ], + [ + 18, + 1.3 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "path" + ], + [ + "any", + [ + "==", + "subclass", + "cycleway" + ], + [ + "==", + "bicycle", + "designated" + ] + ] + ], + "order": 84 + }, + { + "id": "road_path_steps", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 12, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fa8072", + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1.7 + ], + [ + 14, + 1.9 + ], + [ + 17, + 2 + ], + [ + 18, + 2.3 + ] + ] + }, + "line-dasharray": [ + 0.5, + 0.5 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "path" + ], + [ + "==", + "subclass", + "steps" + ] + ], + "order": 85 + }, + { + "id": "road_path_bridleway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 12, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#008000", + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 0.7 + ], + [ + 14, + 0.9 + ], + [ + 17, + 1 + ], + [ + 18, + 1.3 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "path" + ], + [ + "any", + [ + "==", + "subclass", + "bridleway" + ], + [ + "==", + "horse", + "designated" + ] + ], + [ + "!=", + "bicycle", + "designated" + ] + ], + "order": 86 + }, + { + "id": "road_path_footway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 12, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fa8072", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 0.6 + ], + [ + 14, + 1 + ], + [ + 15, + 1.3 + ], + [ + 17, + 1.3 + ], + [ + 18, + 1.6 + ] + ] + }, + "line-dasharray": [ + 2, + 1 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "path" + ], + [ + "in", + "subclass", + "footway", + "path" + ], + [ + "!=", + "bicycle", + "designated" + ], + [ + "!=", + "horse", + "designated" + ] + ], + "order": 87 + }, + { + "id": "road_primary_link", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fcd6a4", + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 1.5 + ], + [ + 12, + 3 + ], + [ + 13, + 3 + ], + [ + 14, + 6.6 + ], + [ + 16, + 10.4 + ], + [ + 17, + 11.4 + ], + [ + 18, + 14.4 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "==", + "ramp", + 1 + ], + [ + "in", + "class", + "primary" + ] + ], + "order": 88 + }, + { + "id": "road_trunk_link", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 6, + "#fdb59e" + ], + [ + 11, + "#f9b29c" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 0.9 + ], + [ + 12, + 3 + ], + [ + 14, + 6.6 + ], + [ + 16, + 10.4 + ], + [ + 17, + 11.4 + ], + [ + 18, + 14.4 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "==", + "ramp", + 1 + ], + [ + "in", + "class", + "trunk" + ] + ], + "order": 89 + }, + { + "id": "road_motorway_link", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 8, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 6, + "#e66e89" + ], + [ + 10, + "#e892a2" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 8, + 0.7 + ], + [ + 11, + 0.9 + ], + [ + 12, + 3 + ], + [ + 14, + 6.6 + ], + [ + 16, + 10.4 + ], + [ + 17, + 11.4 + ], + [ + 18, + 14.4 + ], + [ + 19, + 15.4 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "motorway" + ], + [ + "==", + "ramp", + 1 + ] + ], + "order": 90 + }, + { + "id": "road_service_track", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 12, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 0.9 + ], + [ + 15, + 2.3 + ], + [ + 15, + 0.5 + ], + [ + 16, + 5.4 + ], + [ + 17, + 6.9 + ], + [ + 18, + 9.4 + ], + [ + 19, + 10.4 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "service", + "track" + ] + ], + "order": 91 + }, + { + "id": "road_service_track_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 12, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 0.9 + ], + [ + 15, + 2.3 + ], + [ + 15, + 0.5 + ], + [ + 16, + 5.4 + ], + [ + 17, + 6.9 + ], + [ + 18, + 9.4 + ], + [ + 19, + 10.4 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "service_construction", + "track_construction" + ] + ], + "order": 92 + }, + { + "id": "road_raceway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 9, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(254, 190, 200, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 0.7 + ], + [ + 12, + 1.9 + ], + [ + 13, + 3.9 + ], + [ + 14, + 5.1 + ], + [ + 15, + 5.1 + ], + [ + 16, + 11.5 + ], + [ + 17, + 11.5 + ], + [ + 18, + 12.7 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "in", + "class", + "raceway" + ] + ], + "order": 93 + }, + { + "id": "road_minor", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 12, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 12, + "#d3d3d3" + ], + [ + 13, + "rgba(255, 255, 255, 1)" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1 + ], + [ + 13, + 1.9 + ], + [ + 14, + 3.8 + ], + [ + 15, + 4.8 + ], + [ + 16, + 10.4 + ], + [ + 17, + 11.4 + ], + [ + 18, + 15.4 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "minor" + ] + ] + ], + "order": 94 + }, + { + "id": "road_minor_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 12, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 12, + "#d3d3d3" + ], + [ + 13, + "rgba(255, 255, 255, 1)" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1 + ], + [ + 13, + 1.9 + ], + [ + 14, + 3.8 + ], + [ + 15, + 4.8 + ], + [ + 16, + 10.4 + ], + [ + 17, + 11.4 + ], + [ + 18, + 15.4 + ] + ] + }, + "line-opacity": 1, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "minor_construction" + ] + ] + ], + "order": 95 + }, + { + "id": "road_secondary", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 8, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 10.5, + "#bbbbbb" + ], + [ + 10.6, + "#f7fabf" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 8, + 1 + ], + [ + 9, + 1.1 + ], + [ + 10, + 1.1 + ], + [ + 11, + 2.9 + ], + [ + 12, + 4.3 + ], + [ + 13, + 4.3 + ], + [ + 14, + 7.6 + ], + [ + 15, + 8.6 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "secondary" + ] + ], + "order": 96 + }, + { + "id": "road_tertiary", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 9, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 10.5, + "#bbbbbb" + ], + [ + 10.6, + "#FFFFFF" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 9, + 0.7 + ], + [ + 10, + 0.7 + ], + [ + 11, + 1.9 + ], + [ + 12, + 3 + ], + [ + 13, + 3.9 + ], + [ + 14, + 7.8 + ], + [ + 15, + 8.8 + ], + [ + 16, + 16.4 + ], + [ + 17, + 19.4 + ], + [ + 18, + 25.4 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "tertiary" + ] + ], + "order": 97 + }, + { + "id": "road_tertiary_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 9, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 10.5, + "#bbbbbb" + ], + [ + 10.6, + "#fff" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 9, + 0.7 + ], + [ + 11, + 1.9 + ], + [ + 12, + 3 + ], + [ + 13, + 3.9 + ], + [ + 14, + 7.8 + ], + [ + 15, + 8.8 + ], + [ + 16, + 16.4 + ], + [ + 17, + 19.4 + ], + [ + 18, + 25.4 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "tertiary_construction" + ] + ], + "order": 98 + }, + { + "id": "road_secondary_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 8, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 10.5, + "#bbbbbb" + ], + [ + 10.6, + "#f7fabf" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 8, + 1 + ], + [ + 9, + 1.1 + ], + [ + 10, + 1.1 + ], + [ + 11, + 2.9 + ], + [ + 12, + 4.3 + ], + [ + 13, + 4.3 + ], + [ + 14, + 7.6 + ], + [ + 15, + 8.6 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "secondary_construction" + ] + ], + "order": 99 + }, + { + "id": "road_primary_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 7, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 7, + "#f3c380" + ], + [ + 11, + "#fcd6a4" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 7, + 1 + ], + [ + 8, + 1.4 + ], + [ + 9, + 1.8 + ], + [ + 10, + 1.8 + ], + [ + 11, + 2.5 + ], + [ + 12, + 4 + ], + [ + 13, + 4 + ], + [ + 15, + 8.6 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "!=", + "ramp", + 1 + ], + [ + "in", + "class", + "primary_construction" + ] + ], + "order": 100 + }, + { + "id": "road_primary", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 7, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 7, + "#f3c380" + ], + [ + 11, + "#fcd6a4" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 7, + 1 + ], + [ + 8, + 1.4 + ], + [ + 9, + 1.8 + ], + [ + 10, + 1.8 + ], + [ + 11, + 2.5 + ], + [ + 12, + 4 + ], + [ + 13, + 4 + ], + [ + 15, + 8.6 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "!=", + "ramp", + 1 + ], + [ + "in", + "class", + "primary" + ] + ], + "order": 101 + }, + { + "id": "road_trunk", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 7, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 5, + "#fdb59e" + ], + [ + 11, + "#f9b29c" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 7, + 1 + ], + [ + 8, + 1.4 + ], + [ + 9, + 1.8 + ], + [ + 10, + 1.8 + ], + [ + 11, + 2.5 + ], + [ + 12, + 4 + ], + [ + 13, + 4 + ], + [ + 15, + 8.6 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "!=", + "ramp", + 1 + ], + [ + "in", + "class", + "trunk" + ] + ], + "order": 102 + }, + { + "id": "road_trunk_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 7, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 6, + "#fdb59e" + ], + [ + 11, + "#f9b29c" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 7, + 1 + ], + [ + 8, + 1.4 + ], + [ + 9, + 1.8 + ], + [ + 10, + 1.8 + ], + [ + 11, + 2.5 + ], + [ + 12, + 4 + ], + [ + 13, + 4 + ], + [ + 15, + 8.6 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "!=", + "ramp", + 1 + ], + [ + "in", + "class", + "trunk_construction" + ] + ], + "order": 103 + }, + { + "id": "road_motorway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 5, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 6, + "#e66e89" + ], + [ + 10, + "#e892a2" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0 + ], + [ + 7, + 1 + ], + [ + 18, + 25 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "motorway" + ], + [ + "!=", + "ramp", + 1 + ] + ], + "order": 104 + }, + { + "id": "road_motorway_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 5, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "base": 1, + "stops": [ + [ + 6, + "#e892a2" + ], + [ + 10, + "#e66e89" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0 + ], + [ + 7, + 1 + ], + [ + 20, + 18 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "motorway_construction" + ], + [ + "!=", + "ramp", + 1 + ] + ], + "order": 105 + }, + { + "id": "rail_subway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "#81817f", + "line-width": { + "stops": [ + [ + 14, + 1 + ], + [ + 18, + 3 + ] + ] + }, + "line-opacity": 1, + "line-dasharray": [ + 1, + 1.2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "transit" + ], + [ + "==", + "subclass", + "subway" + ] + ], + "order": 106 + }, + { + "id": "rail_major", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 8, + "#787878" + ], + [ + 14, + "rgba(129, 129, 129, 1)" + ] + ] + }, + "line-width": { + "base": 1.4, + "stops": [ + [ + 8, + 0.8 + ], + [ + 11, + 1.5 + ], + [ + 15, + 3 + ], + [ + 20, + 5.4 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "rail" + ] + ], + "order": 107 + }, + { + "id": "rail_minor", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(129, 129, 129, 1)", + "line-width": { + "base": 1.4, + "stops": [ + [ + 12, + 0.8 + ], + [ + 15, + 1.2 + ], + [ + 20, + 4 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "subclass", + "tram", + "light_rail" + ] + ], + "order": 108 + }, + { + "id": "rail_major_hatching", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 8, + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 8, + "rgba(255, 255, 255, 0.44)" + ], + [ + 10, + "rgba(242, 242, 242, 0.44)" + ] + ] + }, + "line-width": { + "base": 1.4, + "stops": [ + [ + 9, + 0 + ], + [ + 15, + 2 + ], + [ + 20, + 4 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "rail" + ] + ], + "order": 109 + }, + { + "id": "rail_minor_hatching", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "#bbb", + "line-width": { + "base": 1.4, + "stops": [ + [ + 14.5, + 0 + ], + [ + 15, + 2 + ], + [ + 20, + 6 + ] + ] + }, + "line-dasharray": [ + 0.2, + 4 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "subclass", + "tram", + "light_rail" + ] + ], + "order": 110 + }, + { + "id": "bridge_motorway_link_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 11, + "#c24e6b" + ], + [ + 12, + "#dc2a67" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 4 + ], + [ + 14, + 7 + ], + [ + 18, + 16 + ], + [ + 19, + 17 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "motorway" + ], + [ + "==", + "ramp", + 1 + ], + [ + "==", + "brunnel", + "bridge" + ] + ], + "order": 113 + }, + { + "id": "bridge_service_track_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#cfcdca", + "line-width": { + "base": 1.2, + "stops": [ + [ + 15, + 1 + ], + [ + 16, + 4 + ], + [ + 20, + 11 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "service", + "track" + ] + ], + "order": 114 + }, + { + "id": "bridge_link_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#e9ac77", + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 1 + ], + [ + 13, + 3 + ], + [ + 14, + 4 + ], + [ + 20, + 15 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "link" + ], + [ + "==", + "brunnel", + "bridge" + ] + ], + "order": 115 + }, + { + "id": "bridge_street_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "hsl(36, 6%, 74%)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 0.5 + ], + [ + 13, + 1 + ], + [ + 14, + 4 + ], + [ + 20, + 25 + ] + ] + }, + "line-opacity": { + "stops": [ + [ + 12, + 0 + ], + [ + 12.5, + 1 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "street", + "street_limited" + ] + ], + "order": 116 + }, + { + "id": "bridge_path_casing_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "layout": { + "line-join": "miter", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(0, 0, 0, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 3.7 + ], + [ + 14, + 4 + ], + [ + 15, + 4.3 + ], + [ + 17, + 4.3 + ], + [ + 18, + 4.6 + ] + ] + }, + "line-dasharray": [ + 1, + 0 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "path" + ] + ], + "order": 117 + }, + { + "id": "bridge_path_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "layout": { + "line-join": "miter", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(255, 255, 255, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 2.7 + ], + [ + 14, + 3 + ], + [ + 15, + 3.3 + ], + [ + 17, + 3.3 + ], + [ + 18, + 3.6 + ] + ] + }, + "line-dasharray": [ + 1, + 0 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "path" + ] + ], + "order": 118 + }, + { + "id": "bridge_secondary_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 12, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 5 + ], + [ + 13, + 5 + ], + [ + 14, + 9 + ], + [ + 15, + 10 + ], + [ + 16, + 18 + ], + [ + 17, + 21 + ], + [ + 18, + 27 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "secondary", + "tertiary" + ] + ], + "order": 119 + }, + { + "id": "bridge_tertiary_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "rgba(195, 189, 187, 1)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 8, + 1.5 + ], + [ + 20, + 17 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "secondary", + "tertiary" + ] + ], + "order": 120 + }, + { + "id": "bridge_trunk_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": { + "stops": [ + [ + 11, + "#cf6649" + ], + [ + 12, + "#c84e2f" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 3.5 + ], + [ + 12, + 6 + ], + [ + 15, + 10 + ], + [ + 16, + 18 + ], + [ + 17, + 21 + ], + [ + 18, + 27 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "trunk" + ] + ], + "order": 121 + }, + { + "id": "bridge_primary_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 12, + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#000000", + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 5 + ], + [ + 13, + 5 + ], + [ + 15, + 10 + ], + [ + 16, + 18 + ], + [ + 17, + 21 + ], + [ + 18, + 27 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "primary" + ] + ], + "order": 122 + }, + { + "id": "bridge_motorway_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#000000", + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0 + ], + [ + 7, + 1.75 + ], + [ + 18, + 27 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "motorway" + ], + [ + "==", + "brunnel", + "bridge" + ], + [ + "!=", + "ramp", + 1 + ] + ], + "order": 123 + }, + { + "id": "bridge_path_cycleway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "paint": { + "line-color": "#0000ff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 0.8 + ], + [ + 14, + 1 + ], + [ + 15, + 1.3 + ], + [ + 17, + 1.3 + ], + [ + 18, + 1.6 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "path" + ], + [ + "any", + [ + "==", + "subclass", + "cycleway" + ], + [ + "==", + "bicycle", + "designated" + ] + ] + ], + "order": 124 + }, + { + "id": "bridge_path_bridleway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "paint": { + "line-color": "#008000", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 0.8 + ], + [ + 14, + 1 + ], + [ + 15, + 1.3 + ], + [ + 17, + 1.3 + ], + [ + 18, + 1.6 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "path" + ], + [ + "any", + [ + "==", + "subclass", + "bridleway" + ], + [ + "==", + "horse", + "designated" + ] + ], + [ + "!=", + "bicycle", + "designated" + ] + ], + "order": 125 + }, + { + "id": "bridge_path_footway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "paint": { + "line-color": "#fa8072", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13, + 0.8 + ], + [ + 14, + 1 + ], + [ + 15, + 1.3 + ], + [ + 17, + 1.3 + ], + [ + 18, + 1.6 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "path" + ], + [ + "in", + "subclass", + "footway", + "path" + ], + [ + "!=", + "bicycle", + "designated" + ], + [ + "!=", + "horse", + "designated" + ] + ], + "order": 126 + }, + { + "id": "bridge_motorway_link", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 6, + "#e66e89" + ], + [ + 10, + "#e892a2" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 12, + 3 + ], + [ + 14, + 6 + ], + [ + 18, + 15 + ], + [ + 19, + 16 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "motorway" + ], + [ + "==", + "ramp", + 1 + ], + [ + "==", + "brunnel", + "bridge" + ] + ], + "order": 127 + }, + { + "id": "bridge_service_track", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 15.5, + 0 + ], + [ + 16, + 2 + ], + [ + 20, + 7.5 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "service", + "track" + ] + ], + "order": 128 + }, + { + "id": "bridge_service_track_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 15.5, + 0 + ], + [ + 16, + 2 + ], + [ + 20, + 7.5 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "service_construction", + "track_construction" + ] + ], + "order": 129 + }, + { + "id": "bridge_link", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fea", + "line-width": { + "base": 1.2, + "stops": [ + [ + 12.5, + 0 + ], + [ + 13, + 1.5 + ], + [ + 14, + 2.5 + ], + [ + 20, + 11.5 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "link" + ], + [ + "==", + "brunnel", + "bridge" + ] + ], + "order": 130 + }, + { + "id": "bridge_minor", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13.5, + 0 + ], + [ + 14, + 2.5 + ], + [ + 20, + 18 + ] + ] + }, + "line-opacity": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "minor" + ] + ], + "order": 131 + }, + { + "id": "bridge_minor_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 13.5, + 0 + ], + [ + 14, + 2.5 + ], + [ + 20, + 18 + ] + ] + }, + "line-opacity": 1, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "minor_construction" + ] + ], + "order": 132 + }, + { + "id": "bridge_tertiary", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 11, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#ffffff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 2.3 + ], + [ + 12, + 3 + ], + [ + 13, + 4 + ], + [ + 14, + 7.5 + ], + [ + 15, + 8.5 + ], + [ + 16, + 16.4 + ], + [ + 17, + 19.4 + ], + [ + 18, + 25.4 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "tertiary" + ] + ], + "order": 133 + }, + { + "id": "bridge_secondary", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 7, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#f7fabf", + "line-width": { + "base": 1.2, + "stops": [ + [ + 7, + 1.5 + ], + [ + 11, + 3.3 + ], + [ + 12, + 4 + ], + [ + 13, + 3.8 + ], + [ + 14, + 7.5 + ], + [ + 15, + 8.5 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "secondary" + ] + ], + "order": 134 + }, + { + "id": "bridge_secondary_construction-copy", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 6.5, + 0 + ], + [ + 8, + 0.5 + ], + [ + 20, + 13 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "secondary_construction" + ] + ], + "order": 135 + }, + { + "id": "bridge_tertiary_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.2, + "stops": [ + [ + 6.5, + 0 + ], + [ + 8, + 0.5 + ], + [ + 20, + 13 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "tertiary_construction" + ] + ], + "order": 136 + }, + { + "id": "bridge_primary", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fcd6a4", + "line-width": { + "base": 1.2, + "stops": [ + [ + 7, + 1.5 + ], + [ + 11, + 3.3 + ], + [ + 12, + 4 + ], + [ + 13, + 4 + ], + [ + 15, + 8.5 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "primary" + ] + ], + "order": 137 + }, + { + "id": "bridge_trunk", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#f9b29c", + "line-width": { + "base": 1.2, + "stops": [ + [ + 7, + 1.5 + ], + [ + 11, + 4.5 + ], + [ + 12, + 5 + ], + [ + 15, + 8.5 + ], + [ + 16, + 16 + ], + [ + 17, + 19 + ], + [ + 18, + 25 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "trunk" + ] + ], + "order": 138 + }, + { + "id": "bridge_trunk_primary_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fea", + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0 + ], + [ + 7, + 1 + ], + [ + 20, + 18 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "primary_construction", + "trunk_construction" + ] + ], + "order": 139 + }, + { + "id": "bridge_motorway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": { + "stops": [ + [ + 6, + "#e66e89" + ], + [ + 10, + "#e892a2" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0 + ], + [ + 7, + 1 + ], + [ + 18, + 25 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "motorway" + ], + [ + "==", + "brunnel", + "bridge" + ], + [ + "!=", + "ramp", + 1 + ] + ], + "order": 140 + }, + { + "id": "bridge_motorway_construction", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "line-join": "round" + }, + "paint": { + "line-color": { + "stops": [ + [ + 6, + "#e892a2" + ], + [ + 10, + "#e66e89" + ] + ] + }, + "line-width": { + "base": 1.2, + "stops": [ + [ + 5, + 0 + ], + [ + 7, + 1 + ], + [ + 18, + 25 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "motorway_construction" + ], + [ + "==", + "brunnel", + "bridge" + ], + [ + "!=", + "ramp", + 1 + ] + ], + "order": 141 + }, + { + "id": "bridge_major_rail", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 6, + "rgba(147, 147, 147, 1)" + ], + [ + 12, + "rgba(139, 139, 139, 1)" + ], + [ + 14, + "rgba(129, 129, 129, 1)" + ] + ] + }, + "line-width": { + "base": 1.4, + "stops": [ + [ + 8, + 0.5 + ], + [ + 15, + 3 + ], + [ + 20, + 5.4 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "in", + "class", + "rail" + ] + ], + "order": 142 + }, + { + "id": "bridge_major_rail_hatching", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 8, + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": { + "stops": [ + [ + 8, + "rgba(255, 255, 255, 0.44)" + ], + [ + 10, + "rgba(201, 201, 201, 1)" + ] + ] + }, + "line-width": { + "base": 1.4, + "stops": [ + [ + 9, + 0 + ], + [ + 15, + 2 + ], + [ + 20, + 4 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "brunnel", + "bridge" + ], + [ + "==", + "class", + "rail" + ] + ], + "order": 143 + }, + { + "id": "cablecar", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "layout": { + "line-cap": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#808080", + "line-width": { + "base": 1, + "stops": [ + [ + 11, + 1 + ], + [ + 19, + 2.5 + ] + ] + } + }, + "filter": [ + "==", + "class", + "aerialway" + ], + "order": 144 + }, + { + "id": "cablecar-dash", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "layout": { + "line-cap": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(67, 67, 67, 1)", + "line-width": { + "base": 1, + "stops": [ + [ + 11, + 1 + ], + [ + 19, + 3 + ] + ] + }, + "line-dasharray": [ + 0.5, + 10 + ] + }, + "filter": [ + "==", + "class", + "aerialway" + ], + "order": 145 + }, + { + "id": "road_path_oneway", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 15, + "layout": { + "icon-size": { + "stops": [ + [ + 15, + 0.7 + ], + [ + 20, + 1 + ] + ] + }, + "icon-image": "oneway-path", + "visibility": "visible", + "icon-padding": 2, + "symbol-spacing": 125, + "symbol-placement": "line", + "icon-rotation-alignment": "map" + }, + "paint": { + "icon-opacity": 1 + }, + "filter": [ + "all", + [ + "==", + "oneway", + 1 + ], + [ + "==", + "class", + "path" + ] + ], + "order": 181 + }, + { + "id": "road_oneway", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 15, + "layout": { + "icon-size": { + "stops": [ + [ + 15, + 0.8 + ], + [ + 20, + 1 + ] + ] + }, + "icon-image": "oneway", + "visibility": "visible", + "icon-padding": 2, + "symbol-spacing": 95, + "symbol-placement": "line", + "icon-rotation-alignment": "map" + }, + "paint": { + "icon-opacity": 1 + }, + "filter": [ + "all", + [ + "==", + "oneway", + 1 + ], + [ + "in", + "class", + "motorway", + "trunk", + "primary", + "secondary", + "tertiary", + "minor", + "service" + ] + ], + "order": 182 + }, + { + "id": "road_oneway_opposite", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 15, + "layout": { + "icon-size": { + "stops": [ + [ + 15, + 0.5 + ], + [ + 19, + 1 + ] + ] + }, + "icon-image": "oneway", + "visibility": "visible", + "icon-rotate": -90, + "icon-padding": 2, + "symbol-spacing": 75, + "symbol-placement": "line", + "icon-rotation-alignment": "map" + }, + "paint": { + "icon-opacity": 0.5 + }, + "filter": [ + "all", + [ + "==", + "oneway", + -1 + ], + [ + "in", + "class", + "motorway", + "trunk", + "primary", + "secondary", + "tertiary", + "minor", + "service" + ] + ], + "order": 183 + } + ] +} \ No newline at end of file diff --git a/layers/transportation/transportation.sql b/layers/transportation/transportation.sql new file mode 100644 index 0000000..54c734c --- /dev/null +++ b/layers/transportation/transportation.sql @@ -0,0 +1,1057 @@ +CREATE OR REPLACE FUNCTION highway_is_link(highway text) RETURNS boolean AS +$$ +SELECT highway LIKE '%_link'; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; + + +-- etldoc: layer_transportation[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label=" layer_transportation | z4 | z5 | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; +CREATE OR REPLACE FUNCTION layer_transportation(bbox geometry, zoom_level int) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + class text, + subclass text, + network text, + ramp int, + oneway int, + brunnel text, + service text, + access text, + toll int, + expressway int, + layer int, + level int, + indoor int, + bicycle text, + foot text, + horse text, + mtb_scale text, + surface text + ) +AS +$$ +SELECT osm_id, + geometry, + CASE + WHEN highway <> '' OR public_transport <> '' + THEN highway_class(highway, public_transport, construction) + WHEN railway <> '' THEN railway_class(railway) + WHEN aerialway <> '' THEN 'aerialway' + WHEN shipway <> '' THEN shipway + WHEN man_made <> '' THEN man_made + END AS class, + CASE + WHEN railway IS NOT NULL THEN railway + WHEN (highway IS NOT NULL OR public_transport IS NOT NULL) + AND highway_class(highway, public_transport, construction) = 'path' + THEN COALESCE(NULLIF(public_transport, ''), highway) + WHEN aerialway IS NOT NULL THEN aerialway + END AS subclass, + NULLIF(network, '') AS network, + -- All links are considered as ramps as well + CASE + WHEN highway_is_link(highway) + OR is_ramp + THEN 1 END AS ramp, + CASE WHEN is_oneway <> 0 THEN is_oneway::int END AS oneway, + brunnel(is_bridge, is_tunnel, is_ford) AS brunnel, + NULLIF(service, '') AS service, + access, + CASE WHEN toll = TRUE THEN 1 END AS toll, + CASE WHEN highway NOT IN ('', 'motorway') AND NOT is_ramp AND expressway = TRUE THEN 1 END AS expressway, + NULLIF(layer, 0) AS layer, + "level", + CASE WHEN indoor = TRUE THEN 1 END AS indoor, + NULLIF(bicycle, '') AS bicycle, + NULLIF(foot, '') AS foot, + NULLIF(horse, '') AS horse, + NULLIF(mtb_scale, '') AS mtb_scale, + NULLIF(surface, '') AS surface +FROM ( + -- etldoc: osm_transportation_merge_linestring_gen_z4 -> layer_transportation:z4 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + NULL AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z4 + WHERE zoom_level = 4 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z5 -> layer_transportation:z5 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + NULL AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z5 + WHERE zoom_level = 5 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z6 -> layer_transportation:z6 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + NULL AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z6 + WHERE zoom_level = 6 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z7 -> layer_transportation:z7 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + NULL AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z7 + WHERE zoom_level = 7 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z8 -> layer_transportation:z8 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + NULL AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z8 + WHERE zoom_level = 8 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z9 -> layer_transportation:z9 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + access, + toll, + is_bridge, + is_tunnel, + is_ford, + expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + bicycle, + foot, + horse, + mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z9 + WHERE zoom_level = 9 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z10 -> layer_transportation:z10 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + access, + toll, + is_bridge, + is_tunnel, + is_ford, + expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + bicycle, + foot, + horse, + mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z10 + WHERE zoom_level = 10 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z11 -> layer_transportation:z11 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + access, + toll, + is_bridge, + is_tunnel, + is_ford, + expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + bicycle, + foot, + horse, + mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z11 + WHERE zoom_level = 11 + UNION ALL + + -- etldoc: osm_highway_linestring -> layer_transportation:z12 + -- etldoc: osm_highway_linestring -> layer_transportation:z13 + -- etldoc: osm_highway_linestring -> layer_transportation:z14_ + -- etldoc: osm_transportation_name_network -> layer_transportation:z12 + -- etldoc: osm_transportation_name_network -> layer_transportation:z13 + -- etldoc: osm_transportation_name_network -> layer_transportation:z14_ + SELECT hl.osm_id, + hl.geometry, + hl.highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + public_transport, + service_value(service) AS service, + CASE WHEN access IN ('private', 'no') THEN 'no' END AS access, + toll, + is_bridge, + is_tunnel, + is_ford, + expressway, + is_ramp, + is_oneway, + man_made, + hl.layer, + CASE WHEN hl.highway IN ('footway', 'steps') THEN hl.level END AS level, + CASE WHEN hl.highway IN ('footway', 'steps') THEN hl.indoor END AS indoor, + bicycle, + foot, + horse, + mtb_scale, + surface_value(COALESCE(NULLIF(surface, ''), tracktype)) AS "surface", + hl.z_order + FROM osm_highway_linestring hl + LEFT OUTER JOIN osm_transportation_name_network n ON hl.osm_id = n.osm_id + WHERE NOT is_area + AND + CASE WHEN zoom_level = 12 THEN + CASE WHEN transportation_filter_z12(hl.highway, hl.construction) THEN TRUE + WHEN hl.highway IN ('track', 'path') THEN n.route_rank = 1 + END + WHEN zoom_level = 13 THEN + CASE WHEN man_made='pier' THEN NOT ST_IsClosed(hl.geometry) + WHEN hl.highway IN ('track', 'path') THEN (hl.name <> '' + OR n.route_rank BETWEEN 1 AND 2 + OR hl.sac_scale <> '' + ) + ELSE transportation_filter_z13(hl.highway, public_transport, hl.construction, service) + END + WHEN zoom_level >= 14 THEN + CASE WHEN man_made='pier' THEN NOT ST_IsClosed(hl.geometry) + ELSE TRUE + END + END + UNION ALL + + -- etldoc: osm_railway_linestring_gen_z8 -> layer_transportation:z8 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_ford, + NULL::boolean AS expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_railway_linestring_gen_z8 + WHERE zoom_level = 8 + AND railway = 'rail' + AND service = '' + AND usage = 'main' + UNION ALL + + -- etldoc: osm_railway_linestring_gen_z9 -> layer_transportation:z9 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_ford, + NULL::boolean AS expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_railway_linestring_gen_z9 + WHERE zoom_level = 9 + AND railway = 'rail' + AND service = '' + AND usage = 'main' + UNION ALL + + -- etldoc: osm_railway_linestring_gen_z10 -> layer_transportation:z10 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_railway_linestring_gen_z10 + WHERE zoom_level = 10 + AND railway IN ('rail', 'narrow_gauge') + AND service = '' + UNION ALL + + -- etldoc: osm_railway_linestring_gen_z11 -> layer_transportation:z11 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_railway_linestring_gen_z11 + WHERE zoom_level = 11 + AND railway IN ('rail', 'narrow_gauge', 'light_rail') + AND service = '' + UNION ALL + + -- etldoc: osm_railway_linestring_gen_z12 -> layer_transportation:z12 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_railway_linestring_gen_z12 + WHERE zoom_level = 12 + AND railway IN ('rail', 'narrow_gauge', 'light_rail') + AND service = '' + UNION ALL + + -- etldoc: osm_railway_linestring -> layer_transportation:z13 + -- etldoc: osm_railway_linestring -> layer_transportation:z14_ + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_railway_linestring + WHERE zoom_level = 13 + AND railway IN ('rail', 'narrow_gauge', 'light_rail') + AND service = '' + OR zoom_level >= 14 + UNION ALL + + -- etldoc: osm_aerialway_linestring_gen_z12 -> layer_transportation:z12 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_aerialway_linestring_gen_z12 + WHERE zoom_level = 12 + UNION ALL + + -- etldoc: osm_aerialway_linestring -> layer_transportation:z13 + -- etldoc: osm_aerialway_linestring -> layer_transportation:z14_ + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_aerialway_linestring + WHERE zoom_level >= 13 + UNION ALL + + -- etldoc: osm_shipway_linestring_gen_z4 -> layer_transportation:z4 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_shipway_linestring_gen_z4 + WHERE zoom_level = 4 + UNION ALL + + -- etldoc: osm_shipway_linestring_gen_z5 -> layer_transportation:z5 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_shipway_linestring_gen_z5 + WHERE zoom_level = 5 + UNION ALL + + -- etldoc: osm_shipway_linestring_gen_z6 -> layer_transportation:z6 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_shipway_linestring_gen_z6 + WHERE zoom_level = 6 + UNION ALL + + -- etldoc: osm_shipway_linestring_gen_z7 -> layer_transportation:z7 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_shipway_linestring_gen_z7 + WHERE zoom_level = 7 + UNION ALL + + -- etldoc: osm_shipway_linestring_gen_z8 -> layer_transportation:z8 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_shipway_linestring_gen_z8 + WHERE zoom_level = 8 + UNION ALL + + -- etldoc: osm_shipway_linestring_gen_z9 -> layer_transportation:z9 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_shipway_linestring_gen_z9 + WHERE zoom_level = 9 + UNION ALL + + -- etldoc: osm_shipway_linestring_gen_z10 -> layer_transportation:z10 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_shipway_linestring_gen_z10 + WHERE zoom_level = 10 + UNION ALL + + -- etldoc: osm_shipway_linestring_gen_z11 -> layer_transportation:z11 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_shipway_linestring_gen_z11 + WHERE zoom_level = 11 + UNION ALL + + -- etldoc: osm_shipway_linestring_gen_z12 -> layer_transportation:z12 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_shipway_linestring_gen_z12 + WHERE zoom_level = 12 + UNION ALL + + -- etldoc: osm_shipway_linestring -> layer_transportation:z13 + -- etldoc: osm_shipway_linestring -> layer_transportation:z14_ + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_shipway_linestring + WHERE zoom_level >= 13 + UNION ALL + + -- NOTE: We limit the selection of polys because we need to be + -- careful to net get false positives here because + -- it is possible that closed linestrings appear both as + -- highway linestrings and as polygon + -- etldoc: osm_highway_polygon -> layer_transportation:z13 + -- etldoc: osm_highway_polygon -> layer_transportation:z14_ + SELECT osm_id, + geometry, + highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + public_transport, + NULL AS service, + NULL::text AS access, + NULL::boolean AS toll, + CASE + WHEN man_made IN ('bridge') THEN TRUE + ELSE FALSE + END AS is_bridge, + FALSE AS is_tunnel, + FALSE AS is_ford, + NULL::boolean AS expressway, + FALSE AS is_ramp, + FALSE::int AS is_oneway, + man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_highway_polygon + -- We do not want underground pedestrian areas for now + WHERE zoom_level >= 13 + AND ( + man_made IN ('bridge', 'pier') + OR (is_area AND COALESCE(layer, 0) >= 0) + ) + ) AS zoom_levels +WHERE geometry && bbox +ORDER BY z_order ASC; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/transportation/transportation.yaml b/layers/transportation/transportation.yaml new file mode 100644 index 0000000..4e0caf8 --- /dev/null +++ b/layers/transportation/transportation.yaml @@ -0,0 +1,220 @@ +layer: + id: "transportation" + requires: + tables: + - ne_10m_admin_0_countries + description: | + **transportation** contains roads, railways, aerial ways, and shipping + lines. + This layer is directly derived from the OSM road hierarchy. + At lower zoom levels major highways from Natural Earth are used. + It contains all roads from motorways to primary, secondary and + tertiary roads to residential roads and + foot paths. Styling the roads is the most essential part of the map. + The `transportation` layer also contains polygons for features like plazas. + buffer_size: 4 + srs: +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over + fields: + class: + description: | + Distinguish between more and less important roads or railways and roads under construction. + Class is derived from the value of the + [`highway`](http://wiki.openstreetmap.org/wiki/Key:highway), + [`construction`](http://wiki.openstreetmap.org/wiki/Key:construction), + [`railway`](http://wiki.openstreetmap.org/wiki/Key:railway), + [`aerialway`](http://wiki.openstreetmap.org/wiki/Key:aerialway), + [`route`](http://wiki.openstreetmap.org/wiki/Key:route) tag (for + shipping ways), + [`busway`](https://wiki.openstreetmap.org/wiki/Key:busway), or + [`man_made`](http://wiki.openstreetmap.org/wiki/Key:man_made). + values: + motorway: + highway: ['motorway', 'motorway_link'] + trunk: + highway: ['trunk', 'trunk_link'] + primary: + highway: ['primary', 'primary_link'] + secondary: + highway: ['secondary', 'secondary_link'] + tertiary: + highway: ['tertiary', 'tertiary_link'] + minor: + highway: ['unclassified', 'residential', 'living_street', 'road'] + path: + highway: ['pedestrian', 'path', 'footway', 'cycleway', 'steps', 'bridleway', 'corridor'] + public_transport: 'platform' + service: + highway: service + track: + highway: track + raceway: + highway: raceway + busway: + highway: busway + bus_guideway: + highway: bus_guideway + ferry: + highway: shipway + motorway_construction: + __AND__: + highway: construction + construction: ['motorway', 'motorway_link'] + trunk_construction: + __AND__: + highway: construction + construction: ['trunk', 'trunk_link'] + primary_construction: + __AND__: + highway: construction + construction: ['primary', 'primary_link'] + secondary_construction: + __AND__: + highway: construction + construction: ['secondary', 'secondary_link'] + tertiary_construction: + __AND__: + highway: construction + construction: ['tertiary', 'tertiary_link'] + minor_construction: + __AND__: + highway: construction + construction: ['', 'unclassified', 'residential', 'living_street', 'road'] + path_construction: + __AND__: + - highway: construction + - __OR__: + construction: ['pedestrian', 'path', 'footway', 'cycleway', 'steps', 'bridleway', 'corridor'] + public_transport: platform + service_construction: + __AND__: + highway: construction + construction: service + track_construction: + __AND__: + highway: construction + construction: track + raceway_construction: + __AND__: + highway: construction + construction: raceway + subclass: + description: | + Distinguish more specific classes of railway and path: + Subclass is value of the + [`railway`](http://wiki.openstreetmap.org/wiki/Key:railway), + [`highway`](http://wiki.openstreetmap.org/wiki/Key:highway) (for paths), or + [`public_transport`](http://wiki.openstreetmap.org/wiki/Key:public_transport) (for platforms) tag. + values: + - rail + - narrow_gauge + - preserved + - funicular + - subway + - light_rail + - monorail + - tram + - pedestrian + - path + - footway + - cycleway + - steps + - bridleway + - corridor + - platform + - ferry (DEPRECATED - use class) + network: + description: | + The network type derived mainly from [`network`](http://wiki.openstreetmap.org/wiki/Key:network) tag of the road. + See more info about [`us-*`](http://wiki.openstreetmap.org/wiki/Road_signs_in_the_United_States), + [`ca-transcanada`](https://en.wikipedia.org/wiki/Trans-Canada_Highway), + [`gb-*`](http://wiki.openstreetmap.org/wiki/United_Kingdom_Tagging_Guidelines#UK_roads), + or [`ie-*`](http://wiki.openstreetmap.org/wiki/Ireland/Roads). + brunnel: + description: | + Mark whether way is a tunnel or bridge. + values: + - bridge + - tunnel + - ford + oneway: + description: | + Mark with `1` whether way is a oneway in the direction of the way, + with `-1` whether way is a oneway in the opposite direction of the way + or not a oneway with `0`. + values: [1, -1] + ramp: + description: | + Mark with `1` whether way is a ramp (link or steps) + or not with `0`. + values: [1] + service: + description: | + Original value of the [`service`](http://wiki.openstreetmap.org/wiki/Key:service) tag. + values: + - spur + - yard + - siding + - crossover + - driveway + - alley + - parking_aisle + access: + description: | + Access restrictions on this road. Supported values of the + [`access`](http://wiki.openstreetmap.org/wiki/Key:access) tag are `no` and `private`, + which resolve to `no`. + values: + - no + toll: + description: | + Whether this is a toll road, based on the [`toll`](http://wiki.openstreetmap.org/wiki/Key:toll) tag. + values: [0, 1] + expressway: + description: | + Whether this is an expressway, based on the [`expressway`](http://wiki.openstreetmap.org/wiki/Key:expressway) tag. + values: [1] + layer: + description: | + Original value of the [`layer`](http://wiki.openstreetmap.org/wiki/Key:layer) tag. + level: + description: | + Experimental feature! Filled only for steps and footways. Original + value of the [`level`](http://wiki.openstreetmap.org/wiki/Key:level) tag. + indoor: + description: | + Experimental feature! Filled only for steps and footways. Original + value of the [`indoor`](http://wiki.openstreetmap.org/wiki/Key:indoor) tag. + values: + - 1 + bicycle: + description: | + Original value of the [`bicycle`](http://wiki.openstreetmap.org/wiki/Key:bicycle) tag (highways only). + foot: + description: | + Original value of the [`foot`](http://wiki.openstreetmap.org/wiki/Key:foot) tag (highways only). + horse: + description: | + Original value of the [`horse`](http://wiki.openstreetmap.org/wiki/Key:horse) tag (highways only). + mtb_scale: + description: | + Original value of the [`mtb:scale`](http://wiki.openstreetmap.org/wiki/Key:mtb:scale) tag (highways only). + surface: + description: | + Values of [`surface`](https://wiki.openstreetmap.org/wiki/Key:surface) tag devided into 2 groups `paved` (paved, asphalt, cobblestone, concrete, concrete:lanes, concrete:plates, metal, paving_stones, sett, unhewn_cobblestone, wood) and `unpaved` (unpaved, compacted, dirt, earth, fine_gravel, grass, grass_paver, gravel, gravel_turf, ground, ice, mud, pebblestone, salt, sand, snow, woodchips). + values: + - paved + - unpaved + datasource: + geometry_field: geometry + srid: 900913 + query: (SELECT geometry, class, subclass, network, oneway, ramp, brunnel, service, access, toll, expressway, layer, level, indoor, bicycle, foot, horse, mtb_scale, surface FROM layer_transportation(!bbox!, z(!scale_denominator!))) AS t +schema: + - ./network_type.sql + - ./class.sql + - ./highway_name.sql + - ./update_route_member.sql + - ./update_transportation_merge.sql + - ./transportation.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/transportation/update_route_member.sql b/layers/transportation/update_route_member.sql new file mode 100644 index 0000000..52bc7c4 --- /dev/null +++ b/layers/transportation/update_route_member.sql @@ -0,0 +1,196 @@ +DROP TRIGGER IF EXISTS trigger_store_transportation_highway_linestring ON osm_highway_linestring; + +-- Create bounding windows for country-specific processing + +-- etldoc: ne_10m_admin_0_countries -> ne_10m_admin_0_gb_buffer +CREATE TABLE IF NOT EXISTS ne_10m_admin_0_gb_buffer AS +SELECT ST_Buffer(geometry, 10000) +FROM ne_10m_admin_0_countries +WHERE iso_a2 = 'GB'; + +-- etldoc: ne_10m_admin_0_countries -> ne_10m_admin_0_ie_buffer +CREATE TABLE IF NOT EXISTS ne_10m_admin_0_ie_buffer AS +SELECT ST_Buffer(geometry, 10000) +FROM ne_10m_admin_0_countries +WHERE iso_a2 = 'IE'; + +-- Assign pseudo-networks based highway classification +-- etldoc: osm_highway_linestring -> gbr_route_members_view +-- etldoc: ne_10m_admin_0_gb_buffer -> gbr_route_members_view +CREATE OR REPLACE VIEW gbr_route_members_view AS +SELECT osm_id AS member, + substring(ref FROM E'^[ABM][0-9ABM()]+') AS ref, + -- See https://wiki.openstreetmap.org/wiki/Roads_in_the_United_Kingdom + CASE WHEN highway = 'motorway' THEN 'omt-gb-motorway' + WHEN highway = 'trunk' THEN 'omt-gb-trunk' + WHEN highway IN ('primary','secondary') THEN 'omt-gb-primary' END AS network +FROM osm_highway_linestring +WHERE length(ref) > 1 + AND ST_Intersects(geometry, (SELECT * FROM ne_10m_admin_0_gb_buffer)) + AND highway IN ('motorway', 'trunk', 'primary', 'secondary') +; + +-- etldoc: osm_highway_linestring -> ire_route_members_view +-- etldoc: ne_10m_admin_0_ie_buffer -> ire_route_members_view +CREATE OR REPLACE VIEW ire_route_members_view AS +SELECT osm_id AS member, + substring(ref FROM E'^[MNRL][0-9]+') AS ref, + -- See https://wiki.openstreetmap.org/wiki/Ireland/Roads + CASE WHEN highway = 'motorway' THEN 'omt-ie-motorway' + WHEN highway IN ('trunk','primary') THEN 'omt-ie-national' + ELSE 'omt-ie-regional' END AS network +FROM osm_highway_linestring +WHERE length(ref) > 1 + AND ST_Intersects(geometry, (SELECT * FROM ne_10m_admin_0_ie_buffer)) + AND highway IN ('motorway', 'trunk', 'primary', 'secondary', 'unclassified') +; + +CREATE OR REPLACE FUNCTION osm_route_member_network_type(network text, ref text) RETURNS route_network_type AS +$$ +SELECT CASE + -- https://wiki.openstreetmap.org/wiki/United_States_roads_tagging + WHEN network = 'US:I' THEN 'us-interstate'::route_network_type + WHEN network = 'US:US' THEN 'us-highway'::route_network_type + WHEN network LIKE 'US:__' THEN 'us-state'::route_network_type + -- https://en.wikipedia.org/wiki/Trans-Canada_Highway + WHEN network LIKE 'CA:transcanada%' THEN 'ca-transcanada'::route_network_type + WHEN network = 'CA:QC:A' THEN 'ca-provincial-arterial'::route_network_type + WHEN network = 'CA:ON:primary' THEN + CASE + WHEN ref LIKE '4__' THEN 'ca-provincial-arterial'::route_network_type + WHEN ref = 'QEW' THEN 'ca-provincial-arterial'::route_network_type + ELSE 'ca-provincial'::route_network_type + END + WHEN network = 'CA:MB:PTH' AND ref = '75' THEN 'ca-provincial-arterial'::route_network_type + WHEN network = 'CA:AB:primary' AND ref IN ('2','3','4') THEN 'ca-provincial-arterial'::route_network_type + WHEN network = 'CA:BC' AND ref IN ('3','5','99') THEN 'ca-provincial-arterial'::route_network_type + WHEN network LIKE 'CA:__' OR network LIKE 'CA:__:%' THEN 'ca-provincial'::route_network_type + WHEN network = 'omt-gb-motorway' THEN 'gb-motorway'::route_network_type + WHEN network = 'omt-gb-trunk' THEN 'gb-trunk'::route_network_type + WHEN network = 'omt-gb-primary' THEN 'gb-primary'::route_network_type + WHEN network = 'omt-ie-motorway' THEN 'ie-motorway'::route_network_type + WHEN network = 'omt-ie-national' THEN 'ie-national'::route_network_type + WHEN network = 'omt-ie-regional' THEN 'ie-regional'::route_network_type + END; +$$ LANGUAGE sql IMMUTABLE + PARALLEL SAFE; + +CREATE TABLE IF NOT EXISTS transportation_route_member_coalesced +( + member bigint, + network varchar, + ref varchar, + osm_id bigint not null, + role varchar, + type smallint, + name varchar, + osmc_symbol varchar, + colour varchar, + ref_colour varchar, + network_type route_network_type, + concurrency_index integer, + rank integer, + PRIMARY KEY (member, network, ref) +); + +CREATE OR REPLACE FUNCTION update_osm_route_member(full_update bool) RETURNS void AS +$$ +BEGIN + -- Analyze tracking and source tables before performing update + ANALYZE transportation_name.network_changes; + ANALYZE osm_highway_linestring; + ANALYZE osm_route_member; + + DELETE + FROM transportation_route_member_coalesced + USING transportation_name.network_changes c + WHERE c.is_old IS TRUE AND transportation_route_member_coalesced.member = c.osm_id; + + -- etldoc: osm_route_member -> transportation_route_member_coalesced + INSERT INTO transportation_route_member_coalesced + SELECT + osm_route_member_filtered.*, + osm_route_member_network_type(network, ref) AS network_type, + DENSE_RANK() OVER ( + PARTITION BY member + ORDER BY osm_route_member_network_type(network, ref), network, LENGTH(ref), ref + ) AS concurrency_index, + CASE + WHEN network IN ('iwn', 'nwn', 'rwn') THEN 1 + WHEN network = 'lwn' THEN 2 + WHEN osmc_symbol || colour <> '' THEN 2 + END AS rank + FROM ( + -- etldoc: osm_route_member -> osm_route_member + -- etldoc: gbr_route_members_view -> osm_route_member + -- etldoc: ire_route_members_view -> osm_route_member + -- see http://wiki.openstreetmap.org/wiki/Relation:route#Road_routes + SELECT DISTINCT ON (member, COALESCE(rel.network, ''), COALESCE(rel.ref, '')) + rel.member, + COALESCE(NULLIF(rel.network,''), gb_way.network, ir_way.network, '') AS network, + COALESCE(rel.ref, '') AS ref, + osm_id, + role, + type, + name, + osmc_symbol, + colour, + ref_colour + FROM osm_route_member rel + LEFT JOIN gbr_route_members_view gb_way ON (gb_way.member=rel.member) + LEFT JOIN ire_route_members_view ir_way ON (ir_way.member=rel.member) + WHERE full_update OR EXISTS( + SELECT NULL + FROM transportation_name.network_changes c + WHERE c.is_old IS FALSE AND c.osm_id = rel.member + ) + ) osm_route_member_filtered + ON CONFLICT (member, network, ref) DO UPDATE SET osm_id = EXCLUDED.osm_id, role = EXCLUDED.role, + type = EXCLUDED.type, name = EXCLUDED.name, + osmc_symbol = EXCLUDED.osmc_symbol, colour = EXCLUDED.colour, ref_colour = EXCLUDED.ref_colour, + concurrency_index = EXCLUDED.concurrency_index, + rank = EXCLUDED.rank; +END; +$$ LANGUAGE plpgsql; + +-- Indexes which can be utilized during full-update for queries originating from update_osm_route_member() function +CREATE INDEX IF NOT EXISTS osm_route_member_member_network_ref_idx + ON osm_route_member (member, COALESCE(network, ''), COALESCE(ref, '')); + +-- Analyze created index +ANALYZE osm_route_member; + +-- Ensure transportation_name.network_changes table exists since it is required by update_osm_route_member +CREATE SCHEMA IF NOT EXISTS transportation_name; +CREATE TABLE IF NOT EXISTS transportation_name.network_changes +( + is_old bool, + osm_id bigint, + PRIMARY KEY (is_old, osm_id) +); + +-- Fill transportation_route_member_coalesced table +TRUNCATE transportation_route_member_coalesced; +SELECT update_osm_route_member(TRUE); + +-- Index for queries against transportation_route_member_coalesced during transportation-name-network updates +CREATE INDEX IF NOT EXISTS transportation_route_member_member_idx ON + transportation_route_member_coalesced ("member", "concurrency_index"); + +-- Analyze populated table with indexes +ANALYZE transportation_route_member_coalesced; + +-- Ensure OSM-ID index exists on osm_highway_linestring +CREATE UNIQUE INDEX IF NOT EXISTS osm_highway_linestring_osm_id_idx ON osm_highway_linestring ("osm_id"); + +-- etldoc: osm_route_member -> osm_highway_linestring +UPDATE osm_highway_linestring hl + SET network = rm.network_type + FROM transportation_route_member_coalesced rm + WHERE hl.osm_id=rm.member AND rm.concurrency_index=1; + +-- etldoc: osm_route_member -> osm_highway_linestring_gen_z11 +UPDATE osm_highway_linestring_gen_z11 hl + SET network = rm.network_type + FROM transportation_route_member_coalesced rm + WHERE hl.osm_id=rm.member AND rm.concurrency_index=1; diff --git a/layers/transportation/update_transportation_merge.sql b/layers/transportation/update_transportation_merge.sql new file mode 100644 index 0000000..6a9701f --- /dev/null +++ b/layers/transportation/update_transportation_merge.sql @@ -0,0 +1,1510 @@ +DROP TRIGGER IF EXISTS trigger_store_osm_transportation_merge_linestring_gen_z8 ON osm_transportation_merge_linestring_gen_z8; +DROP TRIGGER IF EXISTS trigger_store_transportation_highway_linestring_gen_z9 ON osm_transportation_merge_linestring_gen_z9; +DROP TRIGGER IF EXISTS trigger_flag_transportation_z9 ON osm_transportation_merge_linestring_gen_z9; +DROP TRIGGER IF EXISTS trigger_refresh_z8 ON transportation.updates_z9; +DROP TRIGGER IF EXISTS trigger_store_transportation_highway_linestring_gen_z11 ON osm_highway_linestring_gen_z11; +DROP TRIGGER IF EXISTS trigger_store_osm_transportation_merge_linestring_gen_z11 ON osm_transportation_merge_linestring_gen_z11; +DROP TRIGGER IF EXISTS trigger_flag_transportation_z11 ON osm_highway_linestring_gen_z11; +DROP TRIGGER IF EXISTS trigger_refresh_z11 ON transportation.updates_z11; +DROP TRIGGER IF EXISTS trigger_store_transportation_name_network ON osm_transportation_name_network; + +-- Determine whether a segment is long enough to have bridge/tunnel attributes +-- Dropping small brunnel sections allow for generalization as distinct segments get too small +CREATE OR REPLACE FUNCTION visible_brunnel(g geometry, brunnel boolean, zoom_level integer) + RETURNS boolean AS +$$ +SELECT + brunnel AND + -- Width of a tile in meters (111,842 is the length of one degree of latitude at the equator in meters) + -- 111,842 * 180 / 2^zoom_level + -- = 20131560 / POW(2, zoom_level) + -- Drop brunnel if length of way < 2% of tile width (less than 3 pixels) + ST_Length(g) * + COS(RADIANS(ST_Y(ST_Centroid(ST_Transform(g, 4326))))) * + POW(2, zoom_level) / 20131560 > 0.02 +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; + +-- Determine whether a segment is long enough to have layer attributes +CREATE OR REPLACE FUNCTION visible_layer(g geometry, layer int, zoom_level integer) + RETURNS int AS +$$ +SELECT + CASE WHEN + -- Width of a tile in meters (111,842 is the length of one degree of latitude at the equator in meters) + -- 111,842 * 180 / 2^zoom_level + -- = 20131560 / POW(2, zoom_level) + -- Drop brunnel if length of way < 2% of tile width (less than 3 pixels) + ST_Length(g) * + COS(RADIANS(ST_Y(ST_Centroid(ST_Transform(g, 4326))))) * + POW(2, zoom_level) / 20131560 > 0.02 + THEN layer END +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; + +-- Determine whether a segment is long enough to have an attribute +CREATE OR REPLACE FUNCTION visible_text(g geometry, attr text, zoom_level integer) + RETURNS text AS +$$ +SELECT + CASE WHEN + -- Width of a tile in meters (111,842 is the length of one degree of latitude at the equator in meters) + -- 111,842 * 180 / 2^zoom_level + -- = 20131560 / POW(2, zoom_level) + -- Drop brunnel if length of way < 2% of tile width (less than 3 pixels) + ST_Length(g) * + COS(RADIANS(ST_Y(ST_Centroid(ST_Transform(g, 4326))))) * + POW(2, zoom_level) / 20131560 > 0.02 + THEN attr END +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; + +-- Instead of using relations to find out the road names we +-- stitch together the touching ways with the same name +-- to allow for nice label rendering +-- Because this works well for roads that do not have relations as well + +-- etldoc: osm_highway_linestring -> osm_transportation_name_network +-- etldoc: transportation_route_member_coalesced -> osm_transportation_name_network +DROP TABLE IF EXISTS osm_transportation_name_network; +CREATE TABLE osm_transportation_name_network AS +SELECT + geometry, + osm_id, + tags || get_basic_names(tags, geometry) AS tags, + ref, + highway, + subclass, + brunnel, + "level", + sac_scale, + layer, + indoor, + network_type, + route_1, route_2, route_3, route_4, route_5, route_6, + z_order, + route_rank +FROM ( + SELECT DISTINCT ON (hl.osm_id) + hl.geometry, + hl.osm_id, + transportation_name_tags(hl.geometry, hl.tags, hl.name, hl.name_en, hl.name_de) AS tags, + rm1.network_type, + CASE + WHEN rm1.network_type IS NOT NULL AND rm1.ref::text <> '' + THEN rm1.ref::text + ELSE NULLIF(hl.ref, '') + END AS ref, + hl.highway, + NULLIF(hl.construction, '') AS subclass, + brunnel(hl.is_bridge, hl.is_tunnel, hl.is_ford) AS brunnel, + sac_scale, + CASE WHEN highway IN ('footway', 'steps') THEN layer END AS layer, + CASE WHEN highway IN ('footway', 'steps') THEN level END AS level, + CASE WHEN highway IN ('footway', 'steps') THEN indoor END AS indoor, + create_route_hstore(rm1.network, rm1.ref, rm1.name, rm1.colour, rm1.ref_colour) AS route_1, + create_route_hstore(rm2.network, rm2.ref, rm2.name, rm2.colour, rm2.ref_colour) AS route_2, + create_route_hstore(rm3.network, rm3.ref, rm3.name, rm3.colour, rm3.ref_colour) AS route_3, + create_route_hstore(rm4.network, rm4.ref, rm4.name, rm4.colour, rm4.ref_colour) AS route_4, + create_route_hstore(rm5.network, rm5.ref, rm5.name, rm5.colour, rm5.ref_colour) AS route_5, + create_route_hstore(rm6.network, rm6.ref, rm6.name, rm6.colour, rm6.ref_colour) AS route_6, + hl.z_order, + LEAST(rm1.rank, rm2.rank, rm3.rank, rm4.rank, rm5.rank, rm6.rank) AS route_rank + FROM osm_highway_linestring hl + LEFT OUTER JOIN transportation_route_member_coalesced rm1 ON rm1.member = hl.osm_id AND rm1.concurrency_index=1 + LEFT OUTER JOIN transportation_route_member_coalesced rm2 ON rm2.member = hl.osm_id AND rm2.concurrency_index=2 + LEFT OUTER JOIN transportation_route_member_coalesced rm3 ON rm3.member = hl.osm_id AND rm3.concurrency_index=3 + LEFT OUTER JOIN transportation_route_member_coalesced rm4 ON rm4.member = hl.osm_id AND rm4.concurrency_index=4 + LEFT OUTER JOIN transportation_route_member_coalesced rm5 ON rm5.member = hl.osm_id AND rm5.concurrency_index=5 + LEFT OUTER JOIN transportation_route_member_coalesced rm6 ON rm6.member = hl.osm_id AND rm6.concurrency_index=6 + WHERE (hl.name <> '' OR hl.ref <> '' OR rm1.ref <> '' OR rm1.network <> '') + AND hl.highway <> '' +) AS t; + +-- Create Primary-Key for osm_transportation_name_network table +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_name_network' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_name_network ADD PRIMARY KEY (osm_id); + END IF; +END; +$$ LANGUAGE plpgsql; + +-- Geometry Index +CREATE INDEX IF NOT EXISTS osm_transportation_name_network_geometry_idx + ON osm_transportation_name_network USING gist (geometry); + +-- etldoc: osm_highway_linestring_gen_z11 -> osm_transportation_merge_linestring_gen_z11 +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z11( + geometry geometry('LineString'), + id SERIAL, + osm_id bigint, + source_ids bigint[], + highway character varying, + network character varying, + construction character varying, + is_bridge boolean, + is_tunnel boolean, + is_ford boolean, + expressway boolean, + z_order integer, + bicycle character varying, + foot character varying, + horse character varying, + mtb_scale character varying, + sac_scale character varying, + access text, + toll boolean, + layer integer +); + +ALTER TABLE osm_transportation_merge_linestring_gen_z11 ADD COLUMN IF NOT EXISTS source_ids bigint[]; + +-- Create osm_transportation_merge_linestring_gen_z10 as a copy of osm_transportation_merge_linestring_gen_z11 but +-- drop the "source_ids" column. This can be done because z10 and z9 tables are only simplified and not merged, +-- therefore relations to sources are direct via the id column. +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z10 + (LIKE osm_transportation_merge_linestring_gen_z11); +ALTER TABLE osm_transportation_merge_linestring_gen_z10 DROP COLUMN IF EXISTS source_ids; + +-- Create osm_transportation_merge_linestring_gen_z9 as a copy of osm_transportation_merge_linestring_gen_z10 +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z9 + (LIKE osm_transportation_merge_linestring_gen_z10); + +-- Create OneToMany-Relation-Table storing relations of a Merged-LineString in table +-- osm_transportation_merge_linestring_gen_z11 to Source-LineStrings from table osm_highway_linestring_gen_z11 +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z11_source_ids( + id int, + source_id bigint, + PRIMARY KEY (id, source_id) +); + +-- Index for storing OSM-IDs of Source-LineStrings +CREATE UNIQUE INDEX IF NOT EXISTS osm_highway_linestring_gen_z11_osm_id_idx ON osm_highway_linestring_gen_z11 ("osm_id"); + +-- Analyze created indexes +ANALYZE osm_highway_linestring_gen_z11; + +-- Ensure tables are emtpy if they haven't been created +TRUNCATE osm_transportation_merge_linestring_gen_z11; +TRUNCATE osm_transportation_merge_linestring_gen_z11_source_ids; + +-- Merge LineStrings from osm_highway_linestring_gen_z11 by grouping them and creating intersecting clusters of +-- each group via ST_ClusterDBSCAN +INSERT INTO osm_transportation_merge_linestring_gen_z11 (geometry, source_ids, highway, network, construction, + is_bridge, is_tunnel, is_ford, expressway, z_order, + bicycle, foot, horse, mtb_scale, sac_scale, access, toll, + layer) +SELECT (ST_Dump(ST_LineMerge(ST_Union(geometry)))).geom AS geometry, + -- We use St_Union instead of St_Collect to ensure no overlapping points exist within the geometries to + -- merge. https://postgis.net/docs/ST_Union.html + -- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString if + -- more than two LineStrings form an intersection or no intersection could be found. + -- https://postgis.net/docs/ST_LineMerge.html + -- In order to not end up with a mixture of LineStrings and MultiLineStrings we dump eventual + -- MultiLineStrings via ST_Dump. https://postgis.net/docs/ST_Dump.html + array_agg(osm_id) as source_ids, + -- Temporary Merged-LineString to Source-LineStrings-ID column to store relations before they have been + -- intersected + highway, + network, + construction, + is_bridge, + is_tunnel, + is_ford, + expressway, + min(z_order) as z_order, + bicycle, + foot, + horse, + mtb_scale, + sac_scale, + CASE + WHEN access IN ('private', 'no') THEN 'no' + ELSE NULL::text END AS access, + toll, + layer +FROM ( + SELECT osm_highway_linestring_normalized_brunnel_z11.*, + -- Get intersecting clusters by setting minimum distance to 0 and minimum intersecting points to 1 + -- https://postgis.net/docs/ST_ClusterDBSCAN.html + ST_ClusterDBSCAN(geometry, 0, 1) OVER ( + PARTITION BY highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, bicycle, + foot, horse, mtb_scale, sac_scale, access, toll, layer + ) AS cluster, + -- ST_ClusterDBSCAN returns an increasing integer as the cluster-ids within each partition starting at 0. + -- This leads to clusters having the same ID across multiple partitions therefore we generate a + -- Cluster-Group-ID by utilizing the DENSE_RANK function sorted over the partition columns. + DENSE_RANK() OVER ( + ORDER BY highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, bicycle, + foot, horse, mtb_scale, sac_scale, access, toll, layer + ) as cluster_group + FROM ( + -- Remove bridge/tunnel/ford attributes from short sections of road so they can be merged + SELECT geometry, + osm_id, + highway, + network, + construction, + visible_brunnel(geometry, is_bridge, 11) AS is_bridge, + visible_brunnel(geometry, is_tunnel, 11) AS is_tunnel, + visible_brunnel(geometry, is_ford, 11) AS is_ford, + expressway, + z_order, + bicycle, + foot, + horse, + mtb_scale, + sac_scale, + access, + toll, + visible_layer(geometry, layer, 11) AS layer + FROM osm_highway_linestring_gen_z11 + ) osm_highway_linestring_normalized_brunnel_z11 +) q +GROUP BY cluster_group, cluster, highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, + bicycle, foot, horse, mtb_scale, sac_scale, access, toll, layer; + +-- Geometry Index +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z11_geometry_idx + ON osm_transportation_merge_linestring_gen_z11 USING gist (geometry); + +-- Create Primary-Keys for osm_transportation_merge_linestring_gen_z11/z10/z9 tables +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_merge_linestring_gen_z11' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_merge_linestring_gen_z11 ADD PRIMARY KEY (id); + END IF; + + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_merge_linestring_gen_z10' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_merge_linestring_gen_z10 ADD PRIMARY KEY (id); + END IF; + + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_merge_linestring_gen_z9' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_merge_linestring_gen_z9 ADD PRIMARY KEY (id); + END IF; +END; +$$ LANGUAGE plpgsql; + +-- Indexes which can be utilized during full-update for queries originating from +-- insert_transportation_merge_linestring_gen_z10() function +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z11_update_partial_idx + ON osm_transportation_merge_linestring_gen_z11 (id) + WHERE highway NOT IN ('tertiary', 'tertiary_link', 'busway') AND + construction NOT IN ('tertiary', 'tertiary_link', 'busway'); + +-- Analyze populated table with new indexes +ANALYZE osm_transportation_merge_linestring_gen_z11; + +-- Store OSM-IDs of Source-LineStrings by intersecting Merged-LineStrings with their sources. This required because +-- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString if +-- more than two LineStrings form an intersection or no intersection could be found. +-- Execute after indexes have been created on osm_highway_linestring_gen_z11 to improve performance +INSERT INTO osm_transportation_merge_linestring_gen_z11_source_ids (id, source_id) +SELECT m.id, m.source_id +FROM ( + SELECT id, unnest(source_ids) AS source_id, geometry + FROM osm_transportation_merge_linestring_gen_z11 +) m +JOIN osm_highway_linestring_gen_z11 s ON (m.source_id = s.osm_id) +WHERE ST_Intersects(s.geometry, m.geometry) +ON CONFLICT (id, source_id) DO NOTHING; + +-- Drop temporary Merged-LineString to Source-LineStrings-ID column +ALTER TABLE osm_transportation_merge_linestring_gen_z11 DROP COLUMN IF EXISTS source_ids; + +CREATE SCHEMA IF NOT EXISTS transportation; + +CREATE TABLE IF NOT EXISTS transportation.changes_z9_z10 +( + is_old boolean, + id int, + PRIMARY KEY (is_old, id) +); + +CREATE OR REPLACE FUNCTION insert_transportation_merge_linestring_gen_z10(full_update bool) RETURNS void AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh transportation z9 10'; + + -- Analyze tracking and source tables before performing update + ANALYZE transportation.changes_z9_z10; + ANALYZE osm_transportation_merge_linestring_gen_z11; + + -- Remove entries which have been deleted from source table + DELETE FROM osm_transportation_merge_linestring_gen_z10 + USING transportation.changes_z9_z10 + WHERE full_update IS TRUE OR ( + transportation.changes_z9_z10.is_old IS TRUE AND + transportation.changes_z9_z10.id = osm_transportation_merge_linestring_gen_z10.id + ); + + -- etldoc: osm_transportation_merge_linestring_gen_z11 -> osm_transportation_merge_linestring_gen_z10 + INSERT INTO osm_transportation_merge_linestring_gen_z10 + SELECT ST_Simplify(geometry, ZRes(12)) AS geometry, + id, + osm_id, + highway, + network, + construction, + -- Remove bridge/tunnel/ford attributes from short sections of road so they can be merged + visible_brunnel(geometry, is_bridge, 11) AS is_bridge, + visible_brunnel(geometry, is_tunnel, 11) AS is_tunnel, + visible_brunnel(geometry, is_ford, 11) AS is_ford, + expressway, + z_order, + bicycle, + foot, + horse, + mtb_scale, + sac_scale, + access, + toll, + visible_layer(geometry, layer, 11) AS layer + FROM osm_transportation_merge_linestring_gen_z11 + WHERE (full_update IS TRUE OR EXISTS( + SELECT NULL FROM transportation.changes_z9_z10 + WHERE transportation.changes_z9_z10.is_old IS FALSE AND + transportation.changes_z9_z10.id = osm_transportation_merge_linestring_gen_z11.id + )) + AND ( + highway NOT IN ('tertiary', 'tertiary_link', 'busway', 'bus_guideway') + AND construction NOT IN ('tertiary', 'tertiary_link', 'busway', 'bus_guideway') + ) + ON CONFLICT (id) DO UPDATE SET osm_id = excluded.osm_id, highway = excluded.highway, network = excluded.network, + construction = excluded.construction, is_bridge = excluded.is_bridge, + is_tunnel = excluded.is_tunnel, is_ford = excluded.is_ford, + expressway = excluded.expressway, z_order = excluded.z_order, + bicycle = excluded.bicycle, foot = excluded.foot, horse = excluded.horse, + mtb_scale = excluded.mtb_scale, sac_scale = excluded.sac_scale, + access = excluded.access, toll = excluded.toll, layer = excluded.layer; + + -- Remove entries which have been deleted from source table + DELETE FROM osm_transportation_merge_linestring_gen_z9 + USING transportation.changes_z9_z10 + WHERE full_update IS TRUE OR ( + transportation.changes_z9_z10.is_old IS TRUE AND + transportation.changes_z9_z10.id = osm_transportation_merge_linestring_gen_z9.id + ); + + -- Analyze source table + ANALYZE osm_transportation_merge_linestring_gen_z10; + + -- etldoc: osm_transportation_merge_linestring_gen_z10 -> osm_transportation_merge_linestring_gen_z9 + INSERT INTO osm_transportation_merge_linestring_gen_z9 + SELECT ST_Simplify(geometry, ZRes(11)) AS geometry, + id, + osm_id, + highway, + network, + construction, + -- Remove bridge/tunnel/ford attributes from short sections of road so they can be merged + visible_brunnel(geometry, is_bridge, 10) AS is_bridge, + visible_brunnel(geometry, is_tunnel, 10) AS is_tunnel, + visible_brunnel(geometry, is_ford, 10) AS is_ford, + expressway, + z_order, + bicycle, + foot, + horse, + mtb_scale, + sac_scale, + access, + toll, + visible_layer(geometry, layer, 10) AS layer + FROM osm_transportation_merge_linestring_gen_z10 + WHERE full_update IS TRUE OR EXISTS( + SELECT NULL FROM transportation.changes_z9_z10 + WHERE transportation.changes_z9_z10.is_old IS FALSE AND + transportation.changes_z9_z10.id = osm_transportation_merge_linestring_gen_z10.id + ) + ON CONFLICT (id) DO UPDATE SET osm_id = excluded.osm_id, highway = excluded.highway, network = excluded.network, + construction = excluded.construction, is_bridge = excluded.is_bridge, + is_tunnel = excluded.is_tunnel, is_ford = excluded.is_ford, + expressway = excluded.expressway, z_order = excluded.z_order, + bicycle = excluded.bicycle, foot = excluded.foot, horse = excluded.horse, + mtb_scale = excluded.mtb_scale, sac_scale = excluded.sac_scale, + access = excluded.access, toll = excluded.toll, layer = excluded.layer; + + -- noinspection SqlWithoutWhere + DELETE FROM transportation.changes_z9_z10; + + RAISE LOG 'Refresh transportation z9 10 done in %', age(clock_timestamp(), t); +END; +$$ LANGUAGE plpgsql; + +-- Ensure tables are emtpy if they haven't been created +TRUNCATE osm_transportation_merge_linestring_gen_z10; +TRUNCATE osm_transportation_merge_linestring_gen_z9; + +SELECT insert_transportation_merge_linestring_gen_z10(TRUE); + +-- Geometry Indexes +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z10_geometry_idx + ON osm_transportation_merge_linestring_gen_z10 USING gist (geometry); +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z9_geometry_idx + ON osm_transportation_merge_linestring_gen_z9 USING gist (geometry); + +-- etldoc: osm_transportation_merge_linestring_gen_z9 -> osm_transportation_merge_linestring_gen_z8 +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z8( + geometry geometry, + id SERIAL, + osm_id bigint, + source_ids int[], + highway character varying, + network character varying, + construction character varying, + is_bridge boolean, + is_tunnel boolean, + is_ford boolean, + expressway boolean, + z_order integer +); + +ALTER TABLE osm_transportation_merge_linestring_gen_z8 ADD COLUMN IF NOT EXISTS source_ids bigint[]; + +-- Create osm_transportation_merge_linestring_gen_z7 as a copy of osm_transportation_merge_linestring_gen_z8 but +-- drop the "source_ids" column. This can be done because z7 to z5 tables are only simplified and not merged, +-- therefore relations to sources are direct via the id column. +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z7 + (LIKE osm_transportation_merge_linestring_gen_z8); +ALTER TABLE osm_transportation_merge_linestring_gen_z7 DROP COLUMN IF EXISTS source_ids; + +-- Create osm_transportation_merge_linestring_gen_z6 as a copy of osm_transportation_merge_linestring_gen_z7 +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z6 + (LIKE osm_transportation_merge_linestring_gen_z7); + +-- Create osm_transportation_merge_linestring_gen_z5 as a copy of osm_transportation_merge_linestring_gen_z6 +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z5 + (LIKE osm_transportation_merge_linestring_gen_z6); + +-- Create osm_transportation_merge_linestring_gen_z4 as a copy of osm_transportation_merge_linestring_gen_z5 +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z4 + (LIKE osm_transportation_merge_linestring_gen_z5); + +-- Create OneToMany-Relation-Table storing relations of a Merged-LineString in table +-- osm_transportation_merge_linestring_gen_z8 to Source-LineStrings from table +-- osm_transportation_merge_linestring_gen_z9 +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z8_source_ids( + id int, + source_id bigint, + PRIMARY KEY (id, source_id) +); + +-- Ensure tables are emtpy if they haven't been created +TRUNCATE osm_transportation_merge_linestring_gen_z8; +TRUNCATE osm_transportation_merge_linestring_gen_z8_source_ids; + +-- Indexes for filling and updating osm_transportation_merge_linestring_gen_z8 table +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z9_update_partial_idx + ON osm_transportation_merge_linestring_gen_z9 (id) + WHERE ( + highway IN ('motorway', 'trunk', 'primary') OR + construction IN ('motorway', 'trunk', 'primary') + ) AND ST_IsValid(geometry) AND access IS NULL; + +-- Analyze populated table with indexes +ANALYZE osm_transportation_merge_linestring_gen_z9; + +-- Merge LineStrings from osm_transportation_merge_linestring_gen_z9 by grouping them and creating intersecting +-- clusters of each group via ST_ClusterDBSCAN +INSERT INTO osm_transportation_merge_linestring_gen_z8(geometry, source_ids, highway, network, construction, is_bridge, + is_tunnel, is_ford, expressway, z_order) +SELECT (ST_Dump(ST_Simplify(ST_LineMerge(ST_Union(geometry)), ZRes(10)))).geom AS geometry, + -- We use St_Union instead of St_Collect to ensure no overlapping points exist within the geometries to + -- merge. https://postgis.net/docs/ST_Union.html + -- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString if + -- more than two LineStrings form an intersection or no intersection could be found. + -- https://postgis.net/docs/ST_LineMerge.html + -- In order to not end up with a mixture of LineStrings and MultiLineStrings we dump eventual + -- MultiLineStrings via ST_Dump. https://postgis.net/docs/ST_Dump.html + array_agg(id) AS source_ids, + -- Temporary Merged-LineString to Source-LineStrings-ID column to store relations before they have been + -- intersected + highway, + network, + construction, + is_bridge, + is_tunnel, + is_ford, + expressway, + min(z_order) as z_order +FROM ( + SELECT osm_highway_linestring_normalized_brunnel_z9.*, + -- Get intersecting clusters by setting minimum distance to 0 and minimum intersecting points to 1 + -- https://postgis.net/docs/ST_ClusterDBSCAN.html + ST_ClusterDBSCAN(geometry, 0, 1) OVER ( + PARTITION BY highway, network, construction, is_bridge, is_tunnel, is_ford, expressway + ) AS cluster, + -- ST_ClusterDBSCAN returns an increasing integer as the cluster-ids within each partition starting at 0. + -- This leads to clusters having the same ID across multiple partitions therefore we generate a + -- Cluster-Group-ID by utilizing the DENSE_RANK function sorted over the partition columns. + DENSE_RANK() OVER ( + ORDER BY highway, network, construction, is_bridge, is_tunnel, is_ford, expressway + ) as cluster_group + FROM ( + -- Remove bridge/tunnel/ford attributes from short sections of road so they can be merged + SELECT id, + geometry, + highway, + network, + construction, + visible_brunnel(geometry, is_bridge, 9) AS is_bridge, + visible_brunnel(geometry, is_tunnel, 9) AS is_tunnel, + visible_brunnel(geometry, is_ford, 9) AS is_ford, + expressway, + z_order + FROM osm_transportation_merge_linestring_gen_z9 + WHERE ( + highway IN ('motorway', 'trunk', 'primary') OR + construction IN ('motorway', 'trunk', 'primary') + ) AND ST_IsValid(geometry) AND access IS NULL + ) osm_highway_linestring_normalized_brunnel_z9 +) q +GROUP BY cluster_group, cluster, highway, network, construction, is_bridge, is_tunnel, is_ford, expressway; + +-- Geometry Index +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z8_geometry_idx + ON osm_transportation_merge_linestring_gen_z8 USING gist (geometry); + +-- Create Primary-Keys for osm_transportation_merge_linestring_gen_z8/z7/z6/z5/z4 tables +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_merge_linestring_gen_z8' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_merge_linestring_gen_z8 ADD PRIMARY KEY (id); + END IF; + + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_merge_linestring_gen_z7' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_merge_linestring_gen_z7 ADD PRIMARY KEY (id); + END IF; + + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_merge_linestring_gen_z6' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_merge_linestring_gen_z6 ADD PRIMARY KEY (id); + END IF; + + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_merge_linestring_gen_z5' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_merge_linestring_gen_z5 ADD PRIMARY KEY (id); + END IF; + + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_merge_linestring_gen_z4' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_merge_linestring_gen_z4 ADD PRIMARY KEY (id); + END IF; +END; +$$ LANGUAGE plpgsql; + +-- Indexes which can be utilized during full-update for queries originating from +-- insert_transportation_merge_linestring_gen_z7() function +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z8_update_partial_idx + ON osm_transportation_merge_linestring_gen_z8 (id) + WHERE ST_Length(geometry) > 50; + +-- Analyze populated table with indexes +ANALYZE osm_transportation_merge_linestring_gen_z8; + +-- Store OSM-IDs of Source-LineStrings by intersecting Merged-LineStrings with their sources. This required because +-- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString if +-- more than two LineStrings form an intersection or no intersection could be found. +-- Execute after indexes have been created on osm_transportation_merge_linestring_gen_z11 to improve performance +INSERT INTO osm_transportation_merge_linestring_gen_z8_source_ids (id, source_id) +SELECT m.id, m.source_id +FROM ( + SELECT id, unnest(source_ids) AS source_id, geometry + FROM osm_transportation_merge_linestring_gen_z8 +) m +JOIN osm_transportation_merge_linestring_gen_z9 s ON (m.source_id = s.id) +WHERE ST_Intersects(s.geometry, m.geometry) +ON CONFLICT (id, source_id) DO NOTHING; + +-- Drop temporary Merged-LineString to Source-LineStrings-ID column +ALTER TABLE osm_transportation_merge_linestring_gen_z8 DROP COLUMN IF EXISTS source_ids; + +CREATE TABLE IF NOT EXISTS transportation.changes_z4_z5_z6_z7 +( + is_old boolean, + id int, + PRIMARY KEY (is_old, id) +); + +CREATE OR REPLACE FUNCTION insert_transportation_merge_linestring_gen_z7(full_update boolean) RETURNS void AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh transportation z4 z5 z6 z7'; + + -- Analyze tracking and source tables before performing update + ANALYZE transportation.changes_z4_z5_z6_z7; + ANALYZE osm_transportation_merge_linestring_gen_z8; + + -- Remove entries which have been deleted from source table + DELETE FROM osm_transportation_merge_linestring_gen_z7 + USING transportation.changes_z4_z5_z6_z7 + WHERE full_update IS TRUE OR ( + transportation.changes_z4_z5_z6_z7.is_old IS TRUE AND + transportation.changes_z4_z5_z6_z7.id = osm_transportation_merge_linestring_gen_z7.id + ); + + -- etldoc: osm_transportation_merge_linestring_gen_z8 -> osm_transportation_merge_linestring_gen_z7 + INSERT INTO osm_transportation_merge_linestring_gen_z7 + WITH roads_z8 AS ( + SELECT id, + osm_id, + ST_SnapToGrid( + ST_Node( + ST_Collect( + ST_Simplify(geometry, ZRes(9)) + ) + ), ZRes(9) + ) AS geometry, + highway, + NULLIF(network, '') as network, + construction, + is_bridge, + is_tunnel, + is_ford, + expressway, + z_order + FROM osm_transportation_merge_linestring_gen_z8 + GROUP BY id, osm_id, highway, construction, network, is_bridge, is_tunnel, is_ford, expressway, z_order + ), + roads_z8_merge AS ( + SELECT id, + osm_id, + ST_LineMerge( + ST_Union(geometry) + ) AS geometry, + highway, + network, + construction, + is_bridge, + is_tunnel, + is_ford, + expressway, + z_order + FROM roads_z8 + GROUP BY id, osm_id, highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, z_order + ) + + SELECT CASE + WHEN ST_StartPoint(geometry) = ST_EndPoint(geometry) + THEN ST_RemovePoint(geometry, ST_NPoints(geometry) - 1) + ELSE geometry + END AS geometry, + id, + osm_id, + highway, + network, + construction, + -- Remove bridge/tunnel/ford attributes from short sections of road so they can be merged + visible_brunnel(geometry, is_bridge, 8) AS is_bridge, + visible_brunnel(geometry, is_tunnel, 8) AS is_tunnel, + visible_brunnel(geometry, is_ford, 8) AS is_ford, + expressway, + z_order + FROM roads_z8_merge + -- Current view: motorway/trunk/primary + WHERE + (full_update IS TRUE OR EXISTS( + SELECT NULL FROM transportation.changes_z4_z5_z6_z7 + WHERE transportation.changes_z4_z5_z6_z7.is_old IS FALSE AND + transportation.changes_z4_z5_z6_z7.id = roads_z8_merge.id + )) AND + (ST_Length(geometry) > 50) + ON CONFLICT (id) DO UPDATE SET osm_id = excluded.osm_id, highway = excluded.highway, network = excluded.network, + construction = excluded.construction, is_bridge = excluded.is_bridge, + is_tunnel = excluded.is_tunnel, is_ford = excluded.is_ford, + expressway = excluded.expressway, z_order = excluded.z_order; + + -- Analyze source table + ANALYZE osm_transportation_merge_linestring_gen_z7; + + -- Remove entries which have been deleted from source table + DELETE FROM osm_transportation_merge_linestring_gen_z6 + USING transportation.changes_z4_z5_z6_z7 + WHERE full_update IS TRUE OR ( + transportation.changes_z4_z5_z6_z7.is_old IS TRUE AND + transportation.changes_z4_z5_z6_z7.id = osm_transportation_merge_linestring_gen_z6.id + ); + + -- etldoc: osm_transportation_merge_linestring_gen_z7 -> osm_transportation_merge_linestring_gen_z6 + INSERT INTO osm_transportation_merge_linestring_gen_z6 + SELECT ST_Simplify(geometry, ZRes(8)) AS geometry, + id, + osm_id, + highway, + network, + construction, + -- Remove bridge/tunnel/ford attributes from short sections of road so they can be merged + visible_brunnel(geometry, is_bridge, 7) AS is_bridge, + visible_brunnel(geometry, is_tunnel, 7) AS is_tunnel, + visible_brunnel(geometry, is_ford, 7) AS is_ford, + expressway, + z_order + FROM osm_transportation_merge_linestring_gen_z7 + -- Current view: motorway/trunk/primary + WHERE + (full_update IS TRUE OR EXISTS( + SELECT NULL FROM transportation.changes_z4_z5_z6_z7 + WHERE transportation.changes_z4_z5_z6_z7.is_old IS FALSE AND + transportation.changes_z4_z5_z6_z7.id = osm_transportation_merge_linestring_gen_z7.id + )) AND + (highway IN ('motorway', 'trunk') OR construction IN ('motorway', 'trunk')) AND + ST_Length(geometry) > 100 + ON CONFLICT (id) DO UPDATE SET osm_id = excluded.osm_id, highway = excluded.highway, network = excluded.network, + construction = excluded.construction, is_bridge = excluded.is_bridge, + is_tunnel = excluded.is_tunnel, is_ford = excluded.is_ford, + expressway = excluded.expressway, z_order = excluded.z_order; + + -- Analyze source table + ANALYZE osm_transportation_merge_linestring_gen_z6; + + -- Remove entries which have been deleted from source table + DELETE FROM osm_transportation_merge_linestring_gen_z5 + USING transportation.changes_z4_z5_z6_z7 + WHERE full_update IS TRUE OR ( + transportation.changes_z4_z5_z6_z7.is_old IS TRUE AND + transportation.changes_z4_z5_z6_z7.id = osm_transportation_merge_linestring_gen_z5.id + ); + + -- etldoc: osm_transportation_merge_linestring_gen_z6 -> osm_transportation_merge_linestring_gen_z5 + INSERT INTO osm_transportation_merge_linestring_gen_z5 + SELECT ST_Simplify(geometry, ZRes(7)) AS geometry, + id, + osm_id, + highway, + network, + construction, + -- Remove bridge/tunnel/ford attributes from short sections of road so they can be merged + visible_brunnel(geometry, is_bridge, 6) AS is_bridge, + visible_brunnel(geometry, is_tunnel, 6) AS is_tunnel, + visible_brunnel(geometry, is_ford, 6) AS is_ford, + expressway, + z_order + FROM osm_transportation_merge_linestring_gen_z6 + WHERE + (full_update IS TRUE OR EXISTS( + SELECT NULL FROM transportation.changes_z4_z5_z6_z7 + WHERE transportation.changes_z4_z5_z6_z7.is_old IS FALSE AND + transportation.changes_z4_z5_z6_z7.id = osm_transportation_merge_linestring_gen_z6.id + )) AND + -- Current view: all motorways and trunks of national-importance + (highway = 'motorway' + OR construction = 'motorway' + -- Allow trunk roads that are part of a nation's most important route network to show at z5 + OR (highway = 'trunk' AND osm_national_network(network)) + ) AND + ST_Length(geometry) > 500 + ON CONFLICT (id) DO UPDATE SET osm_id = excluded.osm_id, highway = excluded.highway, network = excluded.network, + construction = excluded.construction, is_bridge = excluded.is_bridge, + is_tunnel = excluded.is_tunnel, is_ford = excluded.is_ford, + expressway = excluded.expressway, z_order = excluded.z_order; + + -- Analyze source table + ANALYZE osm_transportation_merge_linestring_gen_z5; + + -- Remove entries which have been deleted from source table + DELETE FROM osm_transportation_merge_linestring_gen_z4 + USING transportation.changes_z4_z5_z6_z7 + WHERE full_update IS TRUE OR ( + transportation.changes_z4_z5_z6_z7.is_old IS TRUE AND + transportation.changes_z4_z5_z6_z7.id = osm_transportation_merge_linestring_gen_z4.id + ); + + -- etldoc: osm_transportation_merge_linestring_gen_z5 -> osm_transportation_merge_linestring_gen_z4 + INSERT INTO osm_transportation_merge_linestring_gen_z4 + SELECT ST_Simplify(geometry, ZRes(6)) AS geometry, + id, + osm_id, + highway, + network, + construction, + visible_brunnel(geometry, is_bridge, 5) AS is_bridge, + visible_brunnel(geometry, is_tunnel, 5) AS is_tunnel, + visible_brunnel(geometry, is_ford, 5) AS is_ford, + expressway, + z_order + FROM osm_transportation_merge_linestring_gen_z5 + WHERE + (full_update IS TRUE OR EXISTS( + SELECT NULL FROM transportation.changes_z4_z5_z6_z7 + WHERE transportation.changes_z4_z5_z6_z7.is_old IS FALSE AND + transportation.changes_z4_z5_z6_z7.id = osm_transportation_merge_linestring_gen_z5.id + )) AND + -- All motorways without network (e.g. EU, Asia, South America) + ((highway = 'motorway' OR construction = 'motorway') AND (network is NULL or network = '') + ) OR + -- All roads in network included in osm_national_network except gb-trunk and us-highway + ( (osm_national_network(network) AND network NOT IN ('gb-trunk', 'us-highway') + )) AND + -- Current view: national-importance motorways and trunks + ST_Length(geometry) > 1000 + ON CONFLICT (id) DO UPDATE SET osm_id = excluded.osm_id, highway = excluded.highway, network = excluded.network, + construction = excluded.construction, is_bridge = excluded.is_bridge, + is_tunnel = excluded.is_tunnel, is_ford = excluded.is_ford, + expressway = excluded.expressway, z_order = excluded.z_order; + + -- noinspection SqlWithoutWhere + DELETE FROM transportation.changes_z4_z5_z6_z7; + + RAISE LOG 'Refresh transportation z4 z5 z6 z7 done in %', age(clock_timestamp(), t); +END; +$$ LANGUAGE plpgsql; + +-- Ensure tables are emtpy if they haven't been created +TRUNCATE osm_transportation_merge_linestring_gen_z7; +TRUNCATE osm_transportation_merge_linestring_gen_z6; +TRUNCATE osm_transportation_merge_linestring_gen_z5; +TRUNCATE osm_transportation_merge_linestring_gen_z4; + +SELECT insert_transportation_merge_linestring_gen_z7(TRUE); + +-- Indexes for queries originating from insert_transportation_merge_linestring_gen_z7() function +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z7_update_partial_idx + ON osm_transportation_merge_linestring_gen_z7 (id) + WHERE (highway IN ('motorway', 'trunk') OR construction IN ('motorway', 'trunk')) AND + ST_Length(geometry) > 100; +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z6_update_partial_idx + ON osm_transportation_merge_linestring_gen_z6 (id) + WHERE (highway = 'motorway' + OR construction = 'motorway' + OR (highway = 'trunk' AND osm_national_network(network)) + ) AND + ST_Length(geometry) > 500; +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z5_update_partial_idx + ON osm_transportation_merge_linestring_gen_z5 (id) + WHERE osm_national_network(network) AND ST_Length(geometry) > 1000; + +-- Geometry Indexes +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z7_geometry_idx + ON osm_transportation_merge_linestring_gen_z7 USING gist (geometry); +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z6_geometry_idx + ON osm_transportation_merge_linestring_gen_z6 USING gist (geometry); +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z5_geometry_idx + ON osm_transportation_merge_linestring_gen_z5 USING gist (geometry); +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z4_geometry_idx + ON osm_transportation_merge_linestring_gen_z4 USING gist (geometry); + + +-- Handle updates on +-- osm_highway_linestring_gen_z11 -> osm_transportation_merge_linestring_gen_z11 +-- osm_transportation_merge_linestring_gen_z11 -> osm_transportation_merge_linestring_gen_z10 +-- osm_transportation_merge_linestring_gen_z11 -> osm_transportation_merge_linestring_gen_z9 +CREATE OR REPLACE AGGREGATE array_cat_agg(anycompatiblearray) ( + SFUNC=array_cat, + STYPE=anycompatiblearray, + INITCOND = '{}' +); + +CREATE TABLE IF NOT EXISTS transportation.changes_z11 +( + is_old boolean NULL, + osm_id bigint, + PRIMARY KEY (is_old, osm_id) +); + +-- Store IDs of changed elements from osm_highway_linestring_gen_z11 table. +CREATE OR REPLACE FUNCTION transportation.store_gen_z11() RETURNS trigger AS +$$ +BEGIN + IF (tg_op = 'INSERT' OR tg_op = 'UPDATE') THEN + INSERT INTO transportation.changes_z11(is_old, osm_id) + VALUES (FALSE, new.osm_id) + ON CONFLICT (is_old, osm_id) DO NOTHING; + END IF; + IF (tg_op = 'DELETE' OR tg_op = 'UPDATE') THEN + INSERT INTO transportation.changes_z11(is_old, osm_id) + VALUES (TRUE, old.osm_id) + ON CONFLICT (is_old, osm_id) DO NOTHING; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +-- Store IDs of changed elements from osm_highway_linestring_gen_z9 table. +CREATE OR REPLACE FUNCTION transportation.store_merge_z11() RETURNS trigger AS +$$ +BEGIN + IF (tg_op = 'INSERT' OR tg_op = 'UPDATE') THEN + INSERT INTO transportation.changes_z9_z10(is_old, id) + VALUES (FALSE, new.id) + ON CONFLICT (is_old, id) DO NOTHING; + END IF; + IF tg_op = 'DELETE' THEN + INSERT INTO transportation.changes_z9_z10(is_old, id) + VALUES (TRUE, old.id) + ON CONFLICT (is_old, id) DO NOTHING; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS transportation.updates_z11 +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION transportation.flag_z11() RETURNS trigger AS +$$ +BEGIN + INSERT INTO transportation.updates_z11(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION transportation.refresh_z11() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh transportation z11'; + + -- Analyze tracking and source tables before performing update + ANALYZE transportation.changes_z11; + ANALYZE osm_highway_linestring_gen_z11; + + -- Fetch updated and deleted Merged-LineString from relation-table filtering for each Merged-LineString which + -- contains an updated Source-LineString. + -- Additionally attach a list of Source-LineString-IDs to each Merged-LineString in order to unnest them later. + CREATE TEMPORARY TABLE affected_merged_linestrings AS + SELECT m.id, array_agg(source_id) AS source_ids + FROM osm_transportation_merge_linestring_gen_z11_source_ids m + WHERE EXISTS( + SELECT NULL + FROM transportation.changes_z11 c + WHERE c.is_old IS TRUE AND c.osm_id = m.source_id + ) + GROUP BY id; + + -- Analyze the created table to speed up subsequent queries + ANALYZE affected_merged_linestrings; + + -- Delete all Merged-LineStrings which contained an updated or deleted Source-LineString + DELETE + FROM osm_transportation_merge_linestring_gen_z11 m + USING affected_merged_linestrings + WHERE affected_merged_linestrings.id = m.id; + DELETE + FROM osm_transportation_merge_linestring_gen_z11_source_ids m + USING affected_merged_linestrings + WHERE affected_merged_linestrings.id = m.id; + + -- Analyze the tables affected by the delete-query in order to speed up subsequent queries + ANALYZE osm_transportation_merge_linestring_gen_z11; + ANALYZE osm_transportation_merge_linestring_gen_z11_source_ids; + + -- Create a table containing all LineStrings which should be merged + CREATE TEMPORARY TABLE linestrings_to_merge AS + -- Add all Source-LineStrings affected by this update + SELECT osm_highway_linestring_gen_z11.osm_id, NULL::INTEGER AS id, NULL::BIGINT[] AS source_ids, + geometry, highway, network, construction, + visible_brunnel(geometry, is_bridge, 11) AS is_bridge, + visible_brunnel(geometry, is_tunnel, 11) AS is_tunnel, + visible_brunnel(geometry, is_ford, 11) AS is_ford, + expressway, bicycle, foot, horse, mtb_scale, sac_scale, + CASE WHEN access IN ('private', 'no') THEN 'no' ELSE NULL::text END AS access, toll, + visible_layer(geometry, layer, 11) AS layer, z_order + -- Table containing the IDs of all Source-LineStrings affected by this update + FROM ( + -- Get Source-LineString-IDs of deleted or updated elements + SELECT unnest(affected_merged_linestrings.source_ids)::bigint AS source_id FROM affected_merged_linestrings + UNION + -- Get Source-LineString-IDs of inserted or updated elements + SELECT osm_id AS source_id FROM transportation.changes_z11 WHERE is_old IS FALSE + ORDER BY source_id + ) affected_source_linestrings + JOIN osm_highway_linestring_gen_z11 ON ( + affected_source_linestrings.source_id = osm_highway_linestring_gen_z11.osm_id + ); + + -- Drop temporary tables early to save resources + DROP TABLE affected_merged_linestrings; + + -- Analyze the created table to speed up subsequent queries + ANALYZE linestrings_to_merge; + + -- Add all Merged-LineStrings intersecting with Source-LineStrings affected by this update + INSERT INTO linestrings_to_merge + SELECT NULL::BIGINT AS osm_id, m.id, + ARRAY( + SELECT s.source_id FROM osm_transportation_merge_linestring_gen_z11_source_ids s WHERE s.id = m.id + )::BIGINT[] AS source_ids, m.geometry, m.highway, m.network, m.construction, + visible_brunnel(m.geometry, m.is_bridge, 11) AS is_bridge, + visible_brunnel(m.geometry, m.is_tunnel, 11) AS is_tunnel, + visible_brunnel(m.geometry, m.is_ford, 11) AS is_ford, + m.expressway, m.bicycle, m.foot, m.horse, m.mtb_scale, m.sac_scale, m.access, m.toll, + visible_layer(m.geometry, m.layer, 11) AS layer, m.z_order + FROM linestrings_to_merge + JOIN osm_transportation_merge_linestring_gen_z11 m ON (ST_Intersects(linestrings_to_merge.geometry, m.geometry)); + + -- Analyze the created table to speed up subsequent queries + ANALYZE linestrings_to_merge; + + -- Delete all Merged-LineStrings intersecting with Source-LineStrings affected by this update. + -- We can use the linestrings_to_merge table since Source-LineStrings affected by this update and present in the + -- table will have their ID-Column set to NULL by the previous query. + DELETE + FROM osm_transportation_merge_linestring_gen_z11 m + USING linestrings_to_merge + WHERE m.id = linestrings_to_merge.id; + DELETE + FROM osm_transportation_merge_linestring_gen_z11_source_ids m + USING linestrings_to_merge + WHERE linestrings_to_merge.id = m.id; + + -- Create table containing all LineStrings to and create clusters of intersecting LineStrings partitioned by their + -- groups + CREATE TEMPORARY TABLE clustered_linestrings_to_merge AS + SELECT *, + -- Get intersecting clusters by setting minimum distance to 0 and minimum intersecting points to 1 + -- https://postgis.net/docs/ST_ClusterDBSCAN.html + ST_ClusterDBSCAN(geometry, 0, 1) OVER ( + PARTITION BY highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, bicycle, foot, + horse, mtb_scale, sac_scale, access, toll, layer + ) AS cluster, + -- ST_ClusterDBSCAN returns an increasing integer as the cluster-ids within each partition starting at 0. + -- This leads to clusters having the same ID across multiple partitions therefore we generate a + -- Cluster-Group-ID by utilizing the DENSE_RANK function sorted over the partition columns. + DENSE_RANK() OVER ( + ORDER BY highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, bicycle, foot, horse, + mtb_scale, sac_scale, access, toll, layer + ) as cluster_group + FROM linestrings_to_merge; + + -- Drop temporary tables early to save resources + DROP TABLE linestrings_to_merge; + + -- Create index on cluster columns and analyze the created table to speed up subsequent queries + CREATE INDEX ON clustered_linestrings_to_merge (cluster_group, cluster); + ANALYZE clustered_linestrings_to_merge; + + -- Create temporary Merged-LineString to Source-LineStrings-ID columns to store relations before they have been + -- intersected + ALTER TABLE osm_transportation_merge_linestring_gen_z11 ADD COLUMN IF NOT EXISTS new_source_ids BIGINT[]; + ALTER TABLE osm_transportation_merge_linestring_gen_z11 ADD COLUMN IF NOT EXISTS old_source_ids BIGINT[]; + + WITH inserted_linestrings AS ( + -- Merge LineStrings of each cluster and insert them + INSERT INTO osm_transportation_merge_linestring_gen_z11(geometry, new_source_ids, old_source_ids, highway, + network, construction, is_bridge, is_tunnel, is_ford, + expressway, z_order, bicycle, foot, horse, mtb_scale, + sac_scale, access, toll, layer) + SELECT (ST_Dump(ST_LineMerge(ST_Union(geometry)))).geom AS geometry, + -- We use St_Union instead of St_Collect to ensure no overlapping points exist within the geometries to + -- merge. https://postgis.net/docs/ST_Union.html + -- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString if + -- more than two LineStrings form an intersection or no intersection could be found. + -- https://postgis.net/docs/ST_LineMerge.html + -- In order to not end up with a mixture of LineStrings and MultiLineStrings we dump eventual + -- MultiLineStrings via ST_Dump. https://postgis.net/docs/ST_Dump.html + coalesce( array_agg(osm_id) FILTER (WHERE osm_id IS NOT NULL), '{}' )::BIGINT[] AS new_source_ids, + array_cat_agg(source_ids)::BIGINT[] AS old_source_ids, + highway, + network, + construction, + is_bridge, + is_tunnel, + is_ford, + expressway, + min(z_order) as z_order, + bicycle, + foot, + horse, + mtb_scale, + sac_scale, + access, + toll, + layer + FROM clustered_linestrings_to_merge + GROUP BY cluster_group, cluster, highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, + bicycle, foot, horse, mtb_scale, sac_scale, access, toll, layer + RETURNING id, new_source_ids, old_source_ids, geometry + ) + -- Store OSM-IDs of Source-LineStrings by intersecting Merged-LineStrings with their sources. + -- This is required because ST_LineMerge only merges across singular intersections and groups its output into a + -- MultiLineString if more than two LineStrings form an intersection or no intersection could be found. + INSERT INTO osm_transportation_merge_linestring_gen_z11_source_ids (id, source_id) + SELECT m.id, source_id + FROM ( + SELECT id, source_id, geometry + FROM inserted_linestrings + CROSS JOIN LATERAL ( + SELECT DISTINCT all_source_ids.source_id + FROM unnest( + array_cat(inserted_linestrings.new_source_ids, inserted_linestrings.old_source_ids) + ) AS all_source_ids(source_id) + ) source_ids + ) m + JOIN osm_highway_linestring_gen_z11 s ON (m.source_id = s.osm_id) + WHERE ST_Intersects(s.geometry, m.geometry) + ON CONFLICT (id, source_id) DO NOTHING; + + -- Cleanup remaining table + DROP TABLE clustered_linestrings_to_merge; + + -- Drop temporary Merged-LineString to Source-LineStrings-ID columns + ALTER TABLE osm_transportation_merge_linestring_gen_z11 DROP COLUMN IF EXISTS new_source_ids; + ALTER TABLE osm_transportation_merge_linestring_gen_z11 DROP COLUMN IF EXISTS old_source_ids; + + -- noinspection SqlWithoutWhere + DELETE FROM transportation.changes_z11; + -- noinspection SqlWithoutWhere + DELETE FROM transportation.updates_z11; + + RAISE LOG 'Refresh transportation z11 done in %', age(clock_timestamp(), t); + + -- Update z10 and z9 tables + PERFORM insert_transportation_merge_linestring_gen_z10(FALSE); + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store_transportation_highway_linestring_gen_z11 + AFTER INSERT OR UPDATE OR DELETE + ON osm_highway_linestring_gen_z11 + FOR EACH ROW +EXECUTE PROCEDURE transportation.store_gen_z11(); + +CREATE TRIGGER trigger_store_osm_transportation_merge_linestring_gen_z11 + AFTER INSERT OR UPDATE OR DELETE + ON osm_transportation_merge_linestring_gen_z11 + FOR EACH ROW +EXECUTE PROCEDURE transportation.store_merge_z11(); + +CREATE TRIGGER trigger_flag_transportation_z11 + AFTER INSERT OR UPDATE OR DELETE + ON osm_highway_linestring_gen_z11 + FOR EACH STATEMENT +EXECUTE PROCEDURE transportation.flag_z11(); + +CREATE CONSTRAINT TRIGGER trigger_refresh_z11 + AFTER INSERT + ON transportation.updates_z11 + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE transportation.refresh_z11(); + + +-- Handle updates on +-- osm_transportation_merge_linestring_gen_z9 -> osm_transportation_merge_linestring_gen_z8 +-- osm_transportation_merge_linestring_gen_z8 -> osm_transportation_merge_linestring_gen_z7 +-- osm_transportation_merge_linestring_gen_z8 -> osm_transportation_merge_linestring_gen_z6 +-- osm_transportation_merge_linestring_gen_z8 -> osm_transportation_merge_linestring_gen_z5 +-- osm_transportation_merge_linestring_gen_z8 -> osm_transportation_merge_linestring_gen_z4 + +CREATE TABLE IF NOT EXISTS transportation.changes_z9 +( + is_old boolean, + id bigint, + PRIMARY KEY (is_old, id) +); + +-- Store IDs of changed elements from osm_highway_linestring_gen_z9 table. +CREATE OR REPLACE FUNCTION transportation.store_z9() RETURNS trigger AS +$$ +BEGIN + IF (tg_op = 'INSERT' OR tg_op = 'UPDATE') THEN + INSERT INTO transportation.changes_z9(is_old, id) + VALUES (FALSE, new.id) + ON CONFLICT (is_old, id) DO NOTHING; + END IF; + IF (tg_op = 'DELETE' OR tg_op = 'UPDATE') THEN + INSERT INTO transportation.changes_z9(is_old, id) + VALUES (TRUE, old.id) + ON CONFLICT (is_old, id) DO NOTHING; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +-- Store IDs of changed elements from osm_highway_linestring_gen_z8 table. +CREATE OR REPLACE FUNCTION transportation.store_z8() RETURNS trigger AS +$$ +BEGIN + IF (tg_op = 'INSERT' OR tg_op = 'UPDATE') THEN + INSERT INTO transportation.changes_z4_z5_z6_z7(is_old, id) + VALUES (FALSE, new.id) + ON CONFLICT (is_old, id) DO NOTHING; + END IF; + IF tg_op = 'DELETE' THEN + INSERT INTO transportation.changes_z4_z5_z6_z7(is_old, id) + VALUES (TRUE, old.id) + ON CONFLICT (is_old, id) DO NOTHING; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS transportation.updates_z9 +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION transportation.flag_z9() RETURNS trigger AS +$$ +BEGIN + INSERT INTO transportation.updates_z9(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION transportation.refresh_z8() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh transportation z8'; + + -- Analyze tracking and source tables before performing update + ANALYZE transportation.changes_z9; + ANALYZE osm_transportation_merge_linestring_gen_z9; + + -- Fetch updated and deleted Merged-LineString from relation-table filtering for each Merged-LineString which + -- contains an updated Source-LineString. + -- Additionally attach a list of Source-LineString-IDs to each Merged-LineString in order to unnest them later. + CREATE TEMPORARY TABLE affected_merged_linestrings AS + SELECT m.id, array_agg(source_id) AS source_ids + FROM osm_transportation_merge_linestring_gen_z8_source_ids m + WHERE EXISTS( + SELECT NULL + FROM transportation.changes_z9 c + WHERE c.is_old IS TRUE AND c.id = m.source_id + ) + GROUP BY id; + + -- Analyze the created table to speed up subsequent queries + ANALYZE affected_merged_linestrings; + + -- Delete all Merged-LineStrings which contained an updated or deleted Source-LineString + DELETE + FROM osm_transportation_merge_linestring_gen_z8 m + USING affected_merged_linestrings + WHERE affected_merged_linestrings.id = m.id; + DELETE + FROM osm_transportation_merge_linestring_gen_z8_source_ids m + USING affected_merged_linestrings + WHERE affected_merged_linestrings.id = m.id; + + -- Analyze the tables affected by the delete-query in order to speed up subsequent queries + ANALYZE osm_transportation_merge_linestring_gen_z8; + ANALYZE osm_transportation_merge_linestring_gen_z8_source_ids; + + -- Create a table containing all LineStrings which should be merged + CREATE TEMPORARY TABLE linestrings_to_merge AS + -- Add all Source-LineStrings affected by this update + SELECT id AS source_id, NULL::INT AS id, NULL::INT[] AS source_ids, geometry, highway, network, construction, + visible_brunnel(geometry, is_bridge, 9) AS is_bridge, + visible_brunnel(geometry, is_tunnel, 9) AS is_tunnel, + visible_brunnel(geometry, is_ford, 9) AS is_ford, expressway, z_order + -- Create a table containing the IDs of all Source-LineStrings affected by this update + FROM ( + -- Get Source-LineString-IDs of deleted or updated elements + SELECT unnest(affected_merged_linestrings.source_ids)::bigint AS source_id FROM affected_merged_linestrings + UNION + -- Get Source-LineString-IDs of inserted or updated elements + SELECT id AS source_id FROM transportation.changes_z9 WHERE transportation.changes_z9.is_old IS FALSE + ORDER BY source_id + ) affected_source_linestrings + JOIN osm_transportation_merge_linestring_gen_z9 ON ( + affected_source_linestrings.source_id = osm_transportation_merge_linestring_gen_z9.id + ) + WHERE ( + highway IN ('motorway', 'trunk', 'primary') OR + construction IN ('motorway', 'trunk', 'primary') + ) AND + ST_IsValid(geometry) AND + access IS NULL; + + -- Drop temporary tables early to save resources + DROP TABLE affected_merged_linestrings; + + -- Analyze the created table to speed up subsequent queries + ANALYZE linestrings_to_merge; + + -- Add all Merged-LineStrings intersecting with Source-LineStrings affected by this update + INSERT INTO linestrings_to_merge + SELECT NULL::INT AS source_id, m.id, + ARRAY( + SELECT s.source_id FROM osm_transportation_merge_linestring_gen_z8_source_ids s WHERE s.id = m.id + )::INT[] AS source_ids, m.geometry, m.highway, m.network, m.construction, + visible_brunnel(m.geometry, m.is_bridge, 9) AS is_bridge, + visible_brunnel(m.geometry, m.is_tunnel, 9) AS is_tunnel, + visible_brunnel(m.geometry, m.is_ford, 9) AS is_ford, m.expressway, m.z_order + FROM linestrings_to_merge + JOIN osm_transportation_merge_linestring_gen_z8 m ON (ST_Intersects(linestrings_to_merge.geometry, m.geometry)); + + -- Analyze the created table to speed up subsequent queries + ANALYZE linestrings_to_merge; + + -- Delete all Merged-LineStrings intersecting with Source-LineStrings affected by this update. + -- We can use the linestrings_to_merge table since Source-LineStrings affected by this update and present in the + -- table will have their ID-Column set to NULL by the previous query. + DELETE + FROM osm_transportation_merge_linestring_gen_z8 m + USING linestrings_to_merge + WHERE m.id = linestrings_to_merge.id; + DELETE + FROM osm_transportation_merge_linestring_gen_z8_source_ids m + USING linestrings_to_merge + WHERE m.id = linestrings_to_merge.id; + + -- Create table containing all LineStrings to and create clusters of intersecting LineStrings partitioned by their + -- groups + CREATE TEMPORARY TABLE clustered_linestrings_to_merge AS + SELECT *, + -- Get intersecting clusters by setting minimum distance to 0 and minimum intersecting points to 1 + -- https://postgis.net/docs/ST_ClusterDBSCAN.html + ST_ClusterDBSCAN(geometry, 0, 1) OVER ( + PARTITION BY highway, network, construction, is_bridge, is_tunnel, is_ford, expressway + ) AS cluster, + -- ST_ClusterDBSCAN returns an increasing integer as the cluster-ids within each partition starting at 0. + -- This leads to clusters having the same ID across multiple partitions therefore we generate a + -- Cluster-Group-ID by utilizing the DENSE_RANK function sorted over the partition columns. + DENSE_RANK() OVER ( + ORDER BY highway, network, construction, is_bridge, is_tunnel, is_ford, expressway + ) as cluster_group + FROM linestrings_to_merge; + + -- Drop temporary tables early to save resources + DROP TABLE linestrings_to_merge; + + -- Create index on cluster columns and analyze the created table to speed up subsequent queries + CREATE INDEX ON clustered_linestrings_to_merge (cluster_group, cluster); + ANALYZE clustered_linestrings_to_merge; + + -- Create temporary Merged-LineString to Source-LineStrings-ID columns to store relations before they have been + -- intersected + ALTER TABLE osm_transportation_merge_linestring_gen_z8 ADD COLUMN IF NOT EXISTS new_source_ids INT[]; + ALTER TABLE osm_transportation_merge_linestring_gen_z8 ADD COLUMN IF NOT EXISTS old_source_ids INT[]; + + WITH inserted_linestrings AS ( + -- Merge LineStrings of each cluster and insert them + INSERT INTO osm_transportation_merge_linestring_gen_z8(geometry, new_source_ids, old_source_ids, highway, + network, construction, is_bridge, is_tunnel, is_ford, + expressway, z_order) + SELECT (ST_Dump(ST_Simplify(ST_LineMerge(ST_Union(geometry)), ZRes(10)))).geom AS geometry, + -- We use St_Union instead of St_Collect to ensure no overlapping points exist within the geometries to + -- merge. https://postgis.net/docs/ST_Union.html + -- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString if + -- more than two LineStrings form an intersection or no intersection could be found. + -- https://postgis.net/docs/ST_LineMerge.html + -- In order to not end up with a mixture of LineStrings and MultiLineStrings we dump eventual + -- MultiLineStrings via ST_Dump. https://postgis.net/docs/ST_Dump.html + coalesce( array_agg(source_id) FILTER (WHERE source_id IS NOT NULL), '{}' )::INT[] AS new_source_ids, + array_cat_agg(source_ids)::INT[] as old_source_ids, + highway, + network, + construction, + is_bridge, + is_tunnel, + is_ford, + expressway, + min(z_order) as z_order + FROM clustered_linestrings_to_merge + GROUP BY cluster_group, cluster, highway, network, construction, is_bridge, is_tunnel, is_ford, expressway + RETURNING id, new_source_ids, old_source_ids, geometry + ) + -- Store OSM-IDs of Source-LineStrings by intersecting Merged-LineStrings with their sources. This required because + -- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString if + -- more than two LineStrings form an intersection or no intersection could be found. + INSERT INTO osm_transportation_merge_linestring_gen_z8_source_ids (id, source_id) + SELECT m.id, m.source_id + FROM ( + SELECT id, source_id, geometry + FROM inserted_linestrings + CROSS JOIN LATERAL ( + SELECT DISTINCT all_source_ids.source_id + FROM unnest( + array_cat(inserted_linestrings.new_source_ids, inserted_linestrings.old_source_ids) + ) AS all_source_ids(source_id) + ) source_ids + ) m + JOIN osm_transportation_merge_linestring_gen_z9 s ON (m.source_id = s.id) + WHERE ST_Intersects(s.geometry, m.geometry) + ON CONFLICT (id, source_id) DO NOTHING; + + -- Cleanup + DROP TABLE clustered_linestrings_to_merge; + + -- Drop temporary Merged-LineString to Source-LineStrings-ID columns + ALTER TABLE osm_transportation_merge_linestring_gen_z8 DROP COLUMN IF EXISTS new_source_ids; + ALTER TABLE osm_transportation_merge_linestring_gen_z8 DROP COLUMN IF EXISTS old_source_ids; + + -- noinspection SqlWithoutWhere + DELETE FROM transportation.changes_z9; + -- noinspection SqlWithoutWhere + DELETE FROM transportation.updates_z9; + + RAISE LOG 'Refresh transportation z8 done in %', age(clock_timestamp(), t); + + -- Update z7, z6, z5 and z4 tables + PERFORM insert_transportation_merge_linestring_gen_z7(FALSE); + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store_transportation_highway_linestring_gen_z9 + AFTER INSERT OR UPDATE OR DELETE + ON osm_transportation_merge_linestring_gen_z9 + FOR EACH ROW +EXECUTE PROCEDURE transportation.store_z9(); + +CREATE TRIGGER trigger_store_osm_transportation_merge_linestring_gen_z8 + AFTER INSERT OR UPDATE OR DELETE + ON osm_transportation_merge_linestring_gen_z8 + FOR EACH ROW +EXECUTE PROCEDURE transportation.store_z8(); + +CREATE TRIGGER trigger_flag_transportation_z9 + AFTER INSERT OR UPDATE OR DELETE + ON osm_transportation_merge_linestring_gen_z9 + FOR EACH STATEMENT +EXECUTE PROCEDURE transportation.flag_z9(); + +CREATE CONSTRAINT TRIGGER trigger_refresh_z8 + AFTER INSERT + ON transportation.updates_z9 + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE transportation.refresh_z8(); diff --git a/layers/transportation_name/README.md b/layers/transportation_name/README.md new file mode 100644 index 0000000..15ab81c --- /dev/null +++ b/layers/transportation_name/README.md @@ -0,0 +1,10 @@ +## transportation_name + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#transportation_name** + +### Mapping Diagram +![Mapping diagram for transportation_name](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for transportation_name](etl_diagram.png?raw=true) diff --git a/layers/transportation_name/etl_diagram.png b/layers/transportation_name/etl_diagram.png new file mode 100644 index 0000000..c4ee4e0 Binary files /dev/null and b/layers/transportation_name/etl_diagram.png differ diff --git a/layers/transportation_name/highway_classification.sql b/layers/transportation_name/highway_classification.sql new file mode 100644 index 0000000..b7d5e7a --- /dev/null +++ b/layers/transportation_name/highway_classification.sql @@ -0,0 +1,51 @@ +CREATE OR REPLACE FUNCTION highway_to_val(hwy_class varchar) +RETURNS int +IMMUTABLE +LANGUAGE sql +AS $$ + SELECT CASE hwy_class + WHEN 'motorway' THEN 6 + WHEN 'trunk' THEN 5 + WHEN 'primary' THEN 4 + WHEN 'secondary' THEN 3 + WHEN 'tertiary' THEN 2 + WHEN 'unclassified' THEN 1 + ELSE 0 + END; +$$; + +CREATE OR REPLACE FUNCTION val_to_highway(hwy_val int) +RETURNS varchar +IMMUTABLE +LANGUAGE sql +AS $$ + SELECT CASE hwy_val + WHEN 6 THEN 'motorway' + WHEN 5 THEN 'trunk' + WHEN 4 THEN 'primary' + WHEN 3 THEN 'secondary' + WHEN 2 THEN 'tertiary' + WHEN 1 THEN 'unclassified' + ELSE null + END; +$$; + +CREATE OR REPLACE FUNCTION highest_hwy_sfunc(agg_state varchar, hwy_class varchar) +RETURNS varchar +IMMUTABLE +LANGUAGE sql +AS $$ + SELECT val_to_highway( + GREATEST( + highway_to_val(agg_state), + highway_to_val(hwy_class) + ) + ); +$$; + +DROP AGGREGATE IF EXISTS highest_highway (varchar); +CREATE AGGREGATE highest_highway (varchar) +( + sfunc = highest_hwy_sfunc, + stype = varchar +); diff --git a/layers/transportation_name/mapping_diagram.png b/layers/transportation_name/mapping_diagram.png new file mode 100644 index 0000000..c47f739 Binary files /dev/null and b/layers/transportation_name/mapping_diagram.png differ diff --git a/layers/transportation_name/style.json b/layers/transportation_name/style.json new file mode 100644 index 0000000..2ca6dad --- /dev/null +++ b/layers/transportation_name/style.json @@ -0,0 +1,406 @@ +{ + "layers": [ + { + "id": "ferry_label", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "transportation_name", + "minzoom": 14, + "layout": { + "text-font": [ + "Noto Sans Regular" + ], + "text-size": 10, + "text-field": "{name}", + "text-anchor": "center", + "text-offset": [ + 0, + 0 + ], + "symbol-placement": "line" + }, + "paint": { + "text-color": "#6666ff", + "text-halo-blur": 1, + "text-halo-color": "rgba(255, 255, 255, 0.34)", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "subclass", + "ferry" + ] + ], + "order": 184 + }, + { + "id": "road_label", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "transportation_name", + "minzoom": 14, + "layout": { + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "base": 1, + "stops": [ + [ + 14, + 9 + ], + [ + 18, + 13 + ] + ] + }, + "text-field": "{name}", + "text-anchor": "center", + "text-offset": [ + 0, + 0 + ], + "symbol-placement": "line" + }, + "paint": { + "text-color": "#000000", + "text-halo-color": "rgba(255, 255, 255, 0.97)", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "!=", + "subclass", + "ferry" + ] + ], + "order": 185 + }, + { + "id": "highway-shield-tertiary", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "transportation_name", + "minzoom": 9, + "layout": { + "icon-size": 1, + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 15, + 11 + ], + [ + 17, + 12 + ] + ] + }, + "icon-image": "road_tertiary", + "text-field": "{ref}", + "visibility": "visible", + "icon-anchor": "center", + "icon-padding": 2, + "icon-text-fit": "both", + "symbol-spacing": 560, + "symbol-placement": { + "base": 1, + "stops": [ + [ + 10, + "point" + ], + [ + 11, + "line" + ] + ] + }, + "symbol-avoid-edges": true, + "icon-text-fit-padding": [ + 3, + 4, + 3, + 4 + ], + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#3b3b3b" + }, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "class", + "tertiary" + ], + [ + "has", + "ref" + ] + ], + "order": 186 + }, + { + "id": "highway-shield-secondary", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "transportation_name", + "minzoom": 9, + "layout": { + "icon-size": 1, + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 15, + 11 + ], + [ + 17, + 12 + ] + ] + }, + "icon-image": "road_secondary", + "text-field": "{ref}", + "visibility": "visible", + "icon-anchor": "center", + "icon-padding": 2, + "icon-text-fit": "both", + "symbol-spacing": 560, + "symbol-placement": { + "base": 1, + "stops": [ + [ + 10, + "point" + ], + [ + 11, + "line" + ] + ] + }, + "symbol-avoid-edges": true, + "icon-text-fit-padding": [ + 3, + 4, + 3, + 4 + ], + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#323b00" + }, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "class", + "secondary" + ], + [ + "has", + "ref" + ] + ], + "order": 187 + }, + { + "id": "highway-shield-primary", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "transportation_name", + "minzoom": 9, + "layout": { + "icon-size": 1, + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 15, + 11 + ], + [ + 17, + 12 + ] + ] + }, + "icon-image": "road_primary", + "text-field": "{ref}", + "visibility": "visible", + "icon-anchor": "center", + "icon-padding": 2, + "icon-text-fit": "both", + "symbol-spacing": 560, + "symbol-placement": { + "base": 1, + "stops": [ + [ + 10, + "point" + ], + [ + 11, + "line" + ] + ] + }, + "symbol-avoid-edges": true, + "icon-text-fit-padding": [ + 3, + 4, + 3, + 4 + ], + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#4c2e00" + }, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "class", + "primary" + ], + [ + "has", + "ref" + ] + ], + "order": 188 + }, + { + "id": "highway-shield-motorway", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "transportation_name", + "minzoom": 9, + "layout": { + "icon-size": 1, + "text-font": [ + "Noto Sans Regular" + ], + "text-size": { + "stops": [ + [ + 9, + 10 + ], + [ + 15, + 11 + ], + [ + 17, + 12 + ] + ] + }, + "icon-image": "road_motorway", + "text-field": "{ref}", + "visibility": "visible", + "icon-anchor": "center", + "icon-padding": 2, + "icon-text-fit": "both", + "text-optional": false, + "symbol-spacing": 760, + "text-max-width": 10, + "symbol-placement": { + "base": 1, + "stops": [ + [ + 10, + "point" + ], + [ + 11, + "line" + ] + ] + }, + "text-keep-upright": true, + "symbol-avoid-edges": true, + "icon-text-fit-padding": [ + 3, + 4, + 3, + 4 + ], + "icon-rotation-alignment": "viewport", + "text-rotation-alignment": "viewport" + }, + "paint": { + "text-color": "#620728" + }, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "class", + "motorway" + ], + [ + "has", + "ref" + ] + ], + "order": 189 + } + ] +} \ No newline at end of file diff --git a/layers/transportation_name/transportation_name.sql b/layers/transportation_name/transportation_name.sql new file mode 100644 index 0000000..2458df0 --- /dev/null +++ b/layers/transportation_name/transportation_name.sql @@ -0,0 +1,291 @@ +-- etldoc: layer_transportation_name[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_transportation_name | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; + +CREATE OR REPLACE FUNCTION layer_transportation_name(bbox geometry, zoom_level integer) + RETURNS TABLE + ( + geometry geometry, + name text, + name_en text, + name_de text, + tags hstore, + ref text, + ref_length int, + network text, + route_1_network text, + route_1_ref text, + route_1_name text, + route_1_colour text, + route_2_network text, + route_2_ref text, + route_2_name text, + route_2_colour text, + route_3_network text, + route_3_ref text, + route_3_name text, + route_3_colour text, + route_4_network text, + route_4_ref text, + route_4_name text, + route_4_colour text, + route_5_network text, + route_5_ref text, + route_5_name text, + route_5_colour text, + route_6_network text, + route_6_ref text, + route_6_name text, + route_6_colour text, + class text, + subclass text, + brunnel text, + layer int, + level int, + indoor int + ) +AS +$$ +SELECT geometry, + tags->'name' AS name, + COALESCE(tags->'name:en', tags->'name') AS name_en, + COALESCE(tags->'name:de', tags->'name', tags->'name:en') AS name_de, + tags, + ref, + NULLIF(LENGTH(ref), 0) AS ref_length, + CASE + WHEN network IS NOT NULL + THEN network::text + WHEN length(coalesce(ref, '')) > 0 + THEN 'road' + END AS network, + route_1->'network' AS route_1_network, + route_1->'ref' AS route_1_ref, + route_1->'name' AS route_1_name, + route_1->'colour' AS route_1_colour, + + route_2->'network' AS route_2_network, + route_2->'ref' AS route_2_ref, + route_2->'name' AS route_2_name, + route_2->'colour' AS route_2_colour, + + route_3->'network' AS route_3_network, + route_3->'ref' AS route_3_ref, + route_3->'name' AS route_3_name, + route_3->'colour' AS route_3_colour, + + route_4->'network' AS route_4_network, + route_4->'ref' AS route_4_ref, + route_4->'name' AS route_4_name, + route_4->'colour' AS route_4_colour, + + route_5->'network' AS route_5_network, + route_5->'ref' AS route_5_ref, + route_5->'name' AS route_5_name, + route_5->'colour' AS route_5_colour, + + route_6->'network' AS route_6_network, + route_6->'ref' AS route_6_ref, + route_6->'name' AS route_6_name, + route_6->'colour' AS route_6_colour, + highway_class(highway, '', subclass) AS class, + CASE + WHEN highway IS NOT NULL AND highway_class(highway, '', subclass) = 'path' + THEN highway + ELSE subclass + END AS subclass, + NULLIF(brunnel, '') AS brunnel, + NULLIF(layer, 0) AS layer, + "level", + CASE WHEN indoor = TRUE THEN 1 END AS indoor +FROM ( + + -- etldoc: osm_transportation_name_linestring_gen4 -> layer_transportation_name:z6 + SELECT geometry, + tags, + ref, + highway, + subclass, + brunnel, + network, + route_1, + route_2, + route_3, + route_4, + route_5, + route_6, + z_order, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor + FROM osm_transportation_name_linestring_gen4 + WHERE zoom_level = 6 + UNION ALL + + -- etldoc: osm_transportation_name_linestring_gen3 -> layer_transportation_name:z7 + SELECT geometry, + tags, + ref, + highway, + subclass, + brunnel, + network, + route_1, + route_2, + route_3, + route_4, + route_5, + route_6, + z_order, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor + FROM osm_transportation_name_linestring_gen3 + WHERE ST_Length(geometry) > 20000 AND zoom_level = 7 + UNION ALL + + -- etldoc: osm_transportation_name_linestring_gen2 -> layer_transportation_name:z8 + SELECT geometry, + tags, + ref, + highway, + subclass, + brunnel, + network, + route_1, + route_2, + route_3, + route_4, + route_5, + route_6, + z_order, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor + FROM osm_transportation_name_linestring_gen2 + WHERE ST_Length(geometry) > 14000 AND zoom_level = 8 + UNION ALL + + -- etldoc: osm_transportation_name_linestring_gen1 -> layer_transportation_name:z9 + -- etldoc: osm_transportation_name_linestring_gen1 -> layer_transportation_name:z10 + -- etldoc: osm_transportation_name_linestring_gen1 -> layer_transportation_name:z11 + SELECT geometry, + tags, + ref, + highway, + subclass, + brunnel, + network, + route_1, + route_2, + route_3, + route_4, + route_5, + route_6, + z_order, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor + FROM osm_transportation_name_linestring_gen1 + WHERE ST_Length(geometry) > 8000 / POWER(2, zoom_level - 9) AND zoom_level BETWEEN 9 AND 11 + UNION ALL + + -- etldoc: osm_transportation_name_linestring -> layer_transportation_name:z12 + SELECT geometry, + "tags", + ref, + highway, + subclass, + brunnel, + network, + route_1, route_2, route_3, route_4, route_5, route_6, + z_order, + layer, + "level", + indoor + FROM osm_transportation_name_linestring + WHERE zoom_level = 12 + AND LineLabel(zoom_level, COALESCE(ref, tags->'name'), geometry) + AND NOT highway_is_link(highway) + AND + CASE WHEN highway_class(highway, NULL::text, NULL::text) NOT IN ('path', 'minor') THEN TRUE + WHEN highway IN ('aerialway', 'unclassified', 'residential', 'shipway') THEN TRUE + WHEN route_rank = 1 THEN TRUE END + + UNION ALL + + -- etldoc: osm_transportation_name_linestring -> layer_transportation_name:z13 + SELECT geometry, + "tags", + ref, + highway, + subclass, + brunnel, + network, + route_1, route_2, route_3, route_4, route_5, route_6, + z_order, + layer, + "level", + indoor + FROM osm_transportation_name_linestring + WHERE zoom_level = 13 + AND LineLabel(zoom_level, COALESCE(ref, tags->'name'), geometry) + AND + CASE WHEN highway <> 'path' THEN TRUE + WHEN highway = 'path' AND ( + tags->'name' <> '' + OR network IS NOT NULL + OR sac_scale <> '' + OR route_rank <= 2 + ) THEN TRUE + END + + UNION ALL + + -- etldoc: osm_transportation_name_linestring -> layer_transportation_name:z14_ + SELECT geometry, + "tags", + ref, + highway, + subclass, + brunnel, + network, + route_1, route_2, route_3, route_4, route_5, route_6, + z_order, + layer, + "level", + indoor + FROM osm_transportation_name_linestring + WHERE zoom_level >= 14 + UNION ALL + + -- etldoc: osm_highway_point -> layer_transportation_name:z10 + SELECT + p.geometry, + p.tags, + p.ref, + ( + SELECT highest_highway(l.tags->'highway') + FROM osm_highway_linestring l + WHERE ST_Intersects(p.geometry,l.geometry) + ) AS class, + 'junction'::text AS subclass, + NULL AS brunnel, + NULL AS network, + NULL::hstore AS route_1, + NULL::hstore AS route_2, + NULL::hstore AS route_3, + NULL::hstore AS route_4, + NULL::hstore AS route_5, + NULL::hstore AS route_6, + z_order, + layer, + NULL::int AS level, + NULL::boolean AS indoor + FROM osm_highway_point p + WHERE highway = 'motorway_junction' AND zoom_level >= 10 + ) AS zoom_levels +WHERE geometry && bbox +ORDER BY z_order ASC; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/transportation_name/transportation_name.yaml b/layers/transportation_name/transportation_name.yaml new file mode 100644 index 0000000..7c8654a --- /dev/null +++ b/layers/transportation_name/transportation_name.yaml @@ -0,0 +1,138 @@ +layer: + id: "transportation_name" + # transportation_name relies on the function highway_class() defined in transportation layer + requires: + layers: + - transportation + description: | + This is the layer for labelling the highways. Only highways that are named `name=*` and are long enough + to place text upon appear. The OSM roads are stitched together if they contain the same name + to have better label placement than having many small linestrings. + For motorways you should use the `ref` field to label them while for other roads you should use `name`. + buffer_size: 8 + srs: +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over + fields: + name: The OSM [`name`](http://wiki.openstreetmap.org/wiki/Highways#Names_and_references) value of the highway. + name_en: English name `name:en` if available, otherwise `name`. This is deprecated and will be removed in a future release in favor of `name:en`. + name_de: German name `name:de` if available, otherwise `name` or `name:en`. This is deprecated and will be removed in a future release in favor of `name:de`. + ref: The OSM [`ref`](http://wiki.openstreetmap.org/wiki/Key:ref) tag of the motorway or its network. + ref_length: Length of the `ref` field. Useful for having a shield icon as background for labeling motorways. + network: + description: | + The network type derived mainly from [`network`](http://wiki.openstreetmap.org/wiki/Key:network) tag of the road. + See more info about [`us-*`](http://wiki.openstreetmap.org/wiki/Road_signs_in_the_United_States), + [`ca-transcanada`](https://en.wikipedia.org/wiki/Trans-Canada_Highway), + or [`gb-*`](http://wiki.openstreetmap.org/wiki/United_Kingdom_Tagging_Guidelines#UK_roads). + values: + - us-interstate + - us-highway + - us-state + - ca-transcanada + - ca-provincial-arterial + - ca-provincial + - gb-motorway + - gb-trunk + - gb-primary + - ie-motorway + - ie-national + - ie-regional + - road (default) + class: + description: | + Distinguish between more and less important roads and roads under construction. + values: + - motorway + - trunk + - primary + - secondary + - tertiary + - minor + - service + - track + - path + - raceway + - motorway_construction + - trunk_construction + - primary_construction + - secondary_construction + - tertiary_construction + - minor_construction + - service_construction + - track_construction + - path_construction + - raceway_construction + - rail + - transit + - motorway_junction + subclass: + description: | + Distinguish more specific classes of path: + Subclass is value of the + [`highway`](http://wiki.openstreetmap.org/wiki/Key:highway) (for paths), + and "junction" for [`motorway junctions`](http://wiki.openstreetmap.org/wiki/Tag:highway=motorway_junction). + values: + - pedestrian + - path + - footway + - cycleway + - steps + - bridleway + - corridor + - platform + - junction + brunnel: + description: | + Mark whether way is a bridge, a tunnel or a ford. + values: + - bridge + - tunnel + - ford + level: + description: | + Experimental feature! Filled only for steps and footways. Original + value of [`level`](http://wiki.openstreetmap.org/wiki/Key:level) tag. + layer: + description: | + Experimental feature! Filled only for steps and footways. Original + value of [`layer`](http://wiki.openstreetmap.org/wiki/Key:layer) tag. + indoor: + description: | + Experimental feature! Filled only for steps and footways. Original + value of [`indoor`](http://wiki.openstreetmap.org/wiki/Key:indoor) tag. + values: + - 1 + route_1_network: 1st route concurrency network. + route_1_ref: 1st route concurrency ref. + route_1_name: 1st route concurrency name. + route_1_colour: 1st route concurrency colour. + route_2_network: 2nd route concurrency network. + route_2_ref: 2nd route concurrency ref. + route_2_name: 2nd route concurrency name. + route_2_colour: 2nd route concurrency colour. + route_3_network: 3rd route concurrency network. + route_3_ref: 3rd route concurrency ref. + route_3_name: 3rd route concurrency name. + route_3_colour: 3rd route concurrency colour. + route_4_network: 4th route concurrency network. + route_4_ref: 4th route concurrency ref. + route_4_name: 4th route concurrency name. + route_4_colour: 4th route concurrency colour. + route_5_network: 5th route concurrency network. + route_5_ref: 5th route concurrency ref. + route_5_name: 5th route concurrency name. + route_5_colour: 5th route concurrency colour. + route_6_network: 6th route concurrency network. + route_6_ref: 6th route concurrency ref. + route_6_name: 6th route concurrency name. + route_6_colour: 6th route concurrency colour. + datasource: + geometry_field: geometry + srid: 900913 + query: (SELECT geometry, name, name_en, name_de, {name_languages}, ref, ref_length, network::text, class::text, subclass, brunnel, layer, level, indoor, route_1_network, route_1_ref, route_1_name, route_1_colour, route_2_network, route_2_ref, route_2_name, route_2_colour, route_3_network, route_3_ref, route_3_name, route_3_colour, route_4_network, route_4_ref, route_4_name, route_4_colour, route_5_network, route_5_ref, route_5_name, route_5_colour, route_6_network, route_6_ref, route_6_name, route_6_colour FROM layer_transportation_name(!bbox!, z(!scale_denominator!))) AS t +schema: + - ./highway_classification.sql + - ./update_transportation_name.sql + - ./transportation_name.sql +datasources: + - type: imposm3 + mapping_file: ../transportation/mapping.yaml diff --git a/layers/transportation_name/update_transportation_name.sql b/layers/transportation_name/update_transportation_name.sql new file mode 100644 index 0000000..213051e --- /dev/null +++ b/layers/transportation_name/update_transportation_name.sql @@ -0,0 +1,1600 @@ +DROP TRIGGER IF EXISTS trigger_store_transportation_route_member ON osm_route_member; +DROP TRIGGER IF EXISTS trigger_store_transportation_superroute_member ON osm_superroute_member; +DROP TRIGGER IF EXISTS trigger_store_transportation_highway_linestring ON osm_highway_linestring; +DROP TRIGGER IF EXISTS trigger_flag_transportation_name ON transportation_name.network_changes; +DROP TRIGGER IF EXISTS trigger_refresh_network ON transportation_name.updates_network; +DROP TRIGGER IF EXISTS trigger_store_transportation_name_network ON osm_transportation_name_network; +DROP TRIGGER IF EXISTS trigger_store_transportation_name_shipway ON osm_shipway_linestring; +DROP TRIGGER IF EXISTS trigger_store_transportation_name_aerialway ON osm_aerialway_linestring; +DROP TRIGGER IF EXISTS trigger_store_transportation_name_linestring ON osm_transportation_name_linestring; +DROP TRIGGER IF EXISTS trigger_flag_name ON transportation_name.name_changes; +DROP TRIGGER IF EXISTS trigger_flag_shipway ON transportation_name.shipway_changes; +DROP TRIGGER IF EXISTS trigger_flag_aerialway ON transportation_name.aerialway_changes; +DROP TRIGGER IF EXISTS trigger_refresh_name ON transportation_name.updates_name; +DROP TRIGGER IF EXISTS trigger_refresh_shipway ON transportation_name.updates_shipway; +DROP TRIGGER IF EXISTS trigger_refresh_aerialway ON transportation_name.updates_aerialway; + +-- Instead of using relations to find out the road names we +-- stitch together the touching ways with the same name +-- to allow for nice label rendering +-- Because this works well for roads that do not have relations as well + +-- Indexes for filling and updating osm_transportation_name_linestring table +CREATE UNIQUE INDEX IF NOT EXISTS osm_shipway_linestring_update_partial_idx ON osm_shipway_linestring (osm_id) + WHERE name <> ''; +CREATE UNIQUE INDEX IF NOT EXISTS osm_aerialway_linestring_update_partial_idx ON osm_aerialway_linestring (osm_id) + WHERE name <> ''; +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_name_network_update_partial_idx + ON osm_transportation_name_network (osm_id) + WHERE coalesce(tags->'name', '') <> '' OR + coalesce(ref, '') <> ''; +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_name_network_osm_id_idx ON osm_transportation_name_network (osm_id); + +-- Analyze tables with indexes created on them +ANALYZE osm_aerialway_linestring, osm_shipway_linestring, osm_transportation_name_network; + +-- etldoc: osm_transportation_name_network -> osm_transportation_name_linestring +-- etldoc: osm_shipway_linestring -> osm_transportation_name_linestring +-- etldoc: osm_aerialway_linestring -> osm_transportation_name_linestring +CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring( + id SERIAL, + source integer, + geometry geometry('LineString'), + source_ids bigint[], + tags hstore, + ref text, + highway varchar, + subclass text, + brunnel text, + sac_scale varchar, + "level" integer, + layer integer, + indoor boolean, + network route_network_type, + route_1 hstore, + route_2 hstore, + route_3 hstore, + route_4 hstore, + route_5 hstore, + route_6 hstore, + z_order integer, + route_rank integer +); + +ALTER TABLE osm_transportation_name_linestring ADD COLUMN IF NOT EXISTS source_ids bigint[]; + +-- Create OneToMany-Relation-Table storing relations of a Merged-LineString in table +-- osm_transportation_name_linestring to Source-LineStrings from tables osm_transportation_name_network, +-- osm_shipway_linestring and osm_aerialway_linestring +CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_source_ids( + source int, + id int, + source_id bigint, + PRIMARY KEY (source, id, source_id) +); + +-- Ensure tables are emtpy if they haven't been created +TRUNCATE osm_transportation_name_linestring; +TRUNCATE osm_transportation_name_linestring_source_ids; + +INSERT INTO osm_transportation_name_linestring(source, geometry, source_ids, tags, ref, highway, subclass, brunnel, + sac_scale, "level", layer, indoor, network, route_1, route_2, + route_3, route_4, route_5, route_6,z_order, route_rank) +SELECT source, + geometry, + source_ids, + tags || get_basic_names(tags, geometry) AS tags, + ref, + highway, + subclass, + brunnel, + sac_scale, + "level", + layer, + indoor, + network_type AS network, + route_1, route_2, route_3, route_4, route_5, route_6, + z_order, + route_rank +FROM ( + -- Merge LineStrings from osm_transportation_name_network by grouping them and creating intersecting + -- clusters of each group via ST_ClusterDBSCAN + SELECT (ST_Dump(ST_LineMerge(ST_Union(geometry)))).geom AS geometry, + -- We use St_Union instead of St_Collect to ensure no overlapping points exist within the + -- geometries to merge. https://postgis.net/docs/ST_Union.html + -- ST_LineMerge only merges across singular intersections and groups its output into a + -- MultiLineString if more than two LineStrings form an intersection or no intersection could be + -- found. https://postgis.net/docs/ST_LineMerge.html + -- In order to not end up with a mixture of LineStrings and MultiLineStrings we dump eventual + -- MultiLineStrings via ST_Dump. https://postgis.net/docs/ST_Dump.html + array_agg(osm_id) AS source_ids, + 0 AS source, + tags, + ref, + highway, + subclass, + brunnel, + sac_scale, + level, + layer, + indoor, + network_type, + route_1, route_2, route_3, route_4, route_5, route_6, + min(z_order) AS z_order, + min(route_rank) AS route_rank + FROM ( + SELECT *, + -- Get intersecting clusters by setting minimum distance to 0 and minimum intersecting points + -- to 1. https://postgis.net/docs/ST_ClusterDBSCAN.html + ST_ClusterDBSCAN(geometry, 0, 1) OVER ( + PARTITION BY tags, ref, highway, subclass, brunnel, level, layer, sac_scale, indoor, + network_type, route_1, route_2, route_3, route_4, route_5, route_6 + ) AS cluster, + -- ST_ClusterDBSCAN returns an increasing integer as the cluster-ids within each partition + -- starting at 0. This leads to clusters having the same ID across multiple partitions + -- therefore we generate a Cluster-Group-ID by utilizing the DENSE_RANK function sorted over the + -- partition columns. + DENSE_RANK() OVER ( + ORDER BY tags, ref, highway, subclass, brunnel, level, layer, sac_scale, indoor, + network_type, route_1, route_2, route_3, route_4, route_5, route_6 + ) as cluster_group + FROM osm_transportation_name_network + WHERE coalesce(tags->'name', '') <> '' OR + coalesce(ref, '') <> '' + ) q + GROUP BY cluster_group, cluster, tags, ref, highway, subclass, brunnel, level, layer, sac_scale, indoor, + network_type, route_1, route_2, route_3, route_4, route_5, route_6 + UNION ALL + + -- Merge LineStrings from osm_shipway_linestring by grouping them and creating intersecting + -- clusters of each group via ST_ClusterDBSCAN + SELECT (ST_Dump(ST_LineMerge(ST_Union(geometry)))).geom AS geometry, + -- We use St_Union instead of St_Collect to ensure no overlapping points exist within the + -- geometries to merge. https://postgis.net/docs/ST_Union.html + -- ST_LineMerge only merges across singular intersections and groups its output into a + -- MultiLineString if more than two LineStrings form an intersection or no intersection could be + -- found. https://postgis.net/docs/ST_LineMerge.html + -- In order to not end up with a mixture of LineStrings and MultiLineStrings we dump eventual + -- MultiLineStrings via ST_Dump. https://postgis.net/docs/ST_Dump.html + array_agg(osm_id) AS source_ids, + 1 AS source, + transportation_name_tags( + NULL::geometry, tags, name, name_en, name_de + ) AS tags, + NULL AS ref, + 'shipway' AS highway, + shipway AS subclass, + NULL AS brunnel, + NULL AS sac_scale, + NULL::int AS level, + layer, + NULL AS indoor, + NULL AS network_type, + NULL::hstore AS route_1, + NULL::hstore AS route_2, + NULL::hstore AS route_3, + NULL::hstore AS route_4, + NULL::hstore AS route_5, + NULL::hstore AS route_6, + min(z_order) AS z_order, + NULL::int AS route_rank + FROM ( + SELECT *, + -- Get intersecting clusters by setting minimum distance to 0 and minimum intersecting points + -- to 1. https://postgis.net/docs/ST_ClusterDBSCAN.html + ST_ClusterDBSCAN(geometry, 0, 1) OVER ( + PARTITION BY transportation_name_tags( + NULL::geometry, tags, name, name_en, name_de + ), shipway, layer + ) AS cluster, + -- ST_ClusterDBSCAN returns an increasing integer as the cluster-ids within each partition + -- starting at 0. This leads to clusters having the same ID across multiple partitions + -- therefore we generate a Cluster-Group-ID by utilizing the DENSE_RANK function sorted over the + -- partition columns. + DENSE_RANK() OVER ( + ORDER BY transportation_name_tags( + NULL::geometry, tags, name, name_en, name_de + ), shipway, layer + ) as cluster_group + FROM osm_shipway_linestring + WHERE name <> '' + ) q + GROUP BY cluster_group, cluster, transportation_name_tags( + NULL::geometry, tags, name, name_en, name_de + ), shipway, layer + UNION ALL + + -- Merge LineStrings from osm_aerialway_linestring by grouping them and creating intersecting + -- clusters of each group via ST_ClusterDBSCAN + SELECT (ST_Dump(ST_LineMerge(ST_Union(geometry)))).geom AS geometry, + -- We use St_Union instead of St_Collect to ensure no overlapping points exist within the + -- geometries to merge. https://postgis.net/docs/ST_Union.html + -- ST_LineMerge only merges across singular intersections and groups its output into a + -- MultiLineString if more than two LineStrings form an intersection or no intersection could be + -- found. https://postgis.net/docs/ST_LineMerge.html + -- In order to not end up with a mixture of LineStrings and MultiLineStrings we dump eventual + -- MultiLineStrings via ST_Dump. https://postgis.net/docs/ST_Dump.html + array_agg(osm_id) AS source_ids, + 2 AS source, + transportation_name_tags( + NULL::geometry, tags, name, name_en, name_de + ) AS tags, + NULL AS ref, + 'aerialway' AS highway, + aerialway AS subclass, + NULL AS brunnel, + NULL AS sac_scale, + NULL::int AS level, + layer, + NULL AS indoor, + NULL AS network_type, + NULL::hstore AS route_1, + NULL::hstore AS route_2, + NULL::hstore AS route_3, + NULL::hstore AS route_4, + NULL::hstore AS route_5, + NULL::hstore AS route_6, + min(z_order) AS z_order, + NULL::int AS route_rank + FROM ( + SELECT *, + -- Get intersecting clusters by setting minimum distance to 0 and minimum intersecting points + -- to 1. https://postgis.net/docs/ST_ClusterDBSCAN.html + ST_ClusterDBSCAN(geometry, 0, 1) OVER ( + PARTITION BY transportation_name_tags( + NULL::geometry, tags, name, name_en, name_de + ), aerialway, layer + ) AS cluster, + -- ST_ClusterDBSCAN returns an increasing integer as the cluster-ids within each partition + -- starting at 0. This leads to clusters having the same ID across multiple partitions + -- therefore we generate a Cluster-Group-ID by utilizing the DENSE_RANK function sorted over the + -- partition columns. + DENSE_RANK() OVER ( + ORDER BY transportation_name_tags( + NULL::geometry, tags, name, name_en, name_de + ), aerialway, layer + ) as cluster_group + FROM osm_aerialway_linestring + WHERE name <> '' + ) q + GROUP BY cluster_group, cluster, transportation_name_tags( + NULL::geometry, tags, name, name_en, name_de + ), aerialway, layer + ) AS highway_union; + +-- Geometry Index +CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_geometry_idx + ON osm_transportation_name_linestring USING gist (geometry); + +-- Create table for simplified LineStrings +CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen1 ( + id integer, + geometry geometry, + tags hstore, + ref text, + highway varchar, + subclass text, + brunnel text, + network route_network_type, + route_1 hstore, + route_2 hstore, + route_3 hstore, + route_4 hstore, + route_5 hstore, + route_6 hstore, + z_order integer +); + +-- Create osm_transportation_name_linestring_gen2 as a copy of osm_transportation_name_linestring_gen1 +CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen2 +(LIKE osm_transportation_name_linestring_gen1); + +-- Create osm_transportation_name_linestring_gen3 as a copy of osm_transportation_name_linestring_gen2 +CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen3 +(LIKE osm_transportation_name_linestring_gen2); + +-- Create osm_transportation_name_linestring_gen4 as a copy of osm_transportation_name_linestring_gen3 +CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen4 +(LIKE osm_transportation_name_linestring_gen3); + +-- Create Primary-Keys for osm_transportation_name_linestring and +-- osm_transportation_name_linestring_gen1/gen2/gen3/gen4 tables +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_name_linestring' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_name_linestring ADD PRIMARY KEY (id); + END IF; + + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_name_linestring_gen1' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_name_linestring_gen1 ADD PRIMARY KEY (id); + END IF; + + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_name_linestring_gen2' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_name_linestring_gen2 ADD PRIMARY KEY (id); + END IF; + + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_name_linestring_gen3' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_name_linestring_gen3 ADD PRIMARY KEY (id); + END IF; + + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_transportation_name_linestring_gen4' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_transportation_name_linestring_gen4 ADD PRIMARY KEY (id); + END IF; +END; +$$ LANGUAGE plpgsql; + +-- Indexes which can be utilized during full-update for queries originating from +-- update_transportation_name_linestring_gen() function +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_name_linestring_update_partial_idx + ON osm_transportation_name_linestring (id) + WHERE (highway IN ('motorway', 'trunk') OR highway = 'construction' AND subclass IN ('motorway', 'trunk')) + AND ST_Length(geometry) > 8000; + +-- Temporary index for filling source tables +CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_source_idx ON osm_transportation_name_linestring (source); + +-- Analyze populated table with indexes +ANALYZE osm_transportation_name_linestring; + +-- Store OSM-IDs of Source-LineStrings by intersecting Merged-LineStrings with their sources. This required because +-- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString if +-- more than two LineStrings form an intersection or no intersection could be found. +-- Execute after indexes have been created on osm_transportation_merge_linestring_gen_z11 to improve performance +INSERT INTO osm_transportation_name_linestring_source_ids(source, id, source_id) +SELECT m.source, m.id, source_id +FROM ( + SELECT id, source, unnest(source_ids) AS source_id, geometry + FROM osm_transportation_name_linestring + WHERE osm_transportation_name_linestring.source = 0 +) m +JOIN osm_transportation_name_network s ON (m.source_id = s.osm_id) +WHERE ST_Intersects(s.geometry, m.geometry) +ON CONFLICT (source, id, source_id) DO NOTHING; +INSERT INTO osm_transportation_name_linestring_source_ids(source, id, source_id) +SELECT m.source, m.id, source_id +FROM ( + SELECT id, source, unnest(source_ids) AS source_id, geometry + FROM osm_transportation_name_linestring + WHERE osm_transportation_name_linestring.source = 1 +) m +JOIN osm_shipway_linestring s ON (m.source_id = s.osm_id) +WHERE ST_Intersects(s.geometry, m.geometry) +ON CONFLICT (source, id, source_id) DO NOTHING; +INSERT INTO osm_transportation_name_linestring_source_ids(source, id, source_id) +SELECT m.source, m.id, source_id +FROM ( + SELECT id, source, unnest(source_ids) AS source_id, geometry + FROM osm_transportation_name_linestring + WHERE osm_transportation_name_linestring.source = 2 +) m +JOIN osm_aerialway_linestring s ON (m.source_id = s.osm_id) +WHERE ST_Intersects(s.geometry, m.geometry) +ON CONFLICT (source, id, source_id) DO NOTHING; + +-- Drop temporary Merged-LineString to Source-LineStrings-ID column +ALTER TABLE osm_transportation_name_linestring DROP COLUMN IF EXISTS source_ids; + +-- Drop temporary index +DROP INDEX IF EXISTS osm_transportation_name_linestring_source_idx; + +CREATE SCHEMA IF NOT EXISTS transportation_name; + +CREATE TABLE IF NOT EXISTS transportation_name.name_changes_gen +( + is_old boolean, + id int, + PRIMARY KEY (is_old, id) +); + +CREATE OR REPLACE FUNCTION update_transportation_name_linestring_gen (full_update bool) RETURNS VOID AS $$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh transportation_name merged'; + + -- Analyze tracking and source tables before performing update + ANALYZE transportation_name.name_changes_gen; + ANALYZE osm_transportation_name_linestring; + + -- Remove entries which have been deleted from source table + DELETE FROM osm_transportation_name_linestring_gen1 + USING transportation_name.name_changes_gen + WHERE full_update IS TRUE OR ( + transportation_name.name_changes_gen.is_old IS TRUE AND + transportation_name.name_changes_gen.id = osm_transportation_name_linestring_gen1.id + ); + + -- etldoc: osm_transportation_name_linestring -> osm_transportation_name_linestring_gen1 + INSERT INTO osm_transportation_name_linestring_gen1 (id, geometry, tags, ref, highway, subclass, brunnel, network, + route_1, route_2, route_3, route_4, route_5, route_6) + SELECT MIN(id) as id, + ST_Simplify(ST_LineMerge(ST_Collect(geometry)), 50) AS geometry, + tags, ref, highway, subclass, brunnel, network, + route_1, route_2, route_3, route_4, route_5, route_6 + FROM ( + SELECT id, + geometry, + tags, ref, highway, subclass, + visible_text(geometry, brunnel, 9) AS brunnel, + network, route_1, route_2, route_3, route_4, route_5, route_6 + FROM osm_transportation_name_linestring + ) osm_transportation_name_linestring_gen1_pre_merge + WHERE ( + full_update IS TRUE OR EXISTS ( + SELECT NULL + FROM transportation_name.name_changes_gen + WHERE transportation_name.name_changes_gen.is_old IS FALSE AND + transportation_name.name_changes_gen.id = osm_transportation_name_linestring_gen1_pre_merge.id + ) + ) AND ( + (highway IN ('motorway', 'trunk') OR highway = 'construction' AND subclass IN ('motorway', 'trunk')) + ) + GROUP BY tags, ref, highway, subclass, brunnel, network, route_1, route_2, route_3, route_4, route_5, route_6 + ON CONFLICT (id) DO UPDATE SET geometry = excluded.geometry, tags = excluded.tags, ref = excluded.ref, + highway = excluded.highway, subclass = excluded.subclass, + brunnel = excluded.brunnel, network = excluded.network, route_1 = excluded.route_1, + route_2 = excluded.route_2, route_3 = excluded.route_3, route_4 = excluded.route_4, + route_5 = excluded.route_5, route_6 = excluded.route_6; + + -- Analyze source table + ANALYZE osm_transportation_name_linestring_gen1; + + -- Remove entries which have been deleted from source table + DELETE FROM osm_transportation_name_linestring_gen2 + USING transportation_name.name_changes_gen + WHERE full_update IS TRUE OR ( + transportation_name.name_changes_gen.is_old IS TRUE AND + transportation_name.name_changes_gen.id = osm_transportation_name_linestring_gen2.id + ); + + -- etldoc: osm_transportation_name_linestring_gen1 -> osm_transportation_name_linestring_gen2 + INSERT INTO osm_transportation_name_linestring_gen2 (id, geometry, tags, ref, highway, subclass, brunnel, network, + route_1, route_2, route_3, route_4, route_5, route_6) + SELECT MIN(id) as id, + ST_Simplify(ST_LineMerge(ST_Collect(geometry)), 120) AS geometry, + tags, ref, highway, subclass, brunnel, network, + route_1, route_2, route_3, route_4, route_5, route_6 + FROM ( + SELECT id, + (ST_Dump(geometry)).geom AS geometry, + tags, ref, highway, subclass, + visible_text(geometry, brunnel, 8) AS brunnel, + network, route_1, route_2, route_3, route_4, route_5, route_6 + FROM osm_transportation_name_linestring_gen1 + ) osm_transportation_name_linestring_gen2_pre_merge + WHERE ( + full_update IS TRUE OR EXISTS ( + SELECT NULL + FROM transportation_name.name_changes_gen + WHERE transportation_name.name_changes_gen.is_old IS FALSE AND + transportation_name.name_changes_gen.id = osm_transportation_name_linestring_gen2_pre_merge.id + ) + ) AND ( + (highway IN ('motorway', 'trunk') OR highway = 'construction' AND subclass IN ('motorway', 'trunk')) + ) + GROUP BY tags, ref, highway, subclass, brunnel, network, route_1, route_2, route_3, route_4, route_5, route_6 + ON CONFLICT (id) DO UPDATE SET geometry = excluded.geometry, tags = excluded.tags, ref = excluded.ref, + highway = excluded.highway, subclass = excluded.subclass, + brunnel = excluded.brunnel, network = excluded.network, route_1 = excluded.route_1, + route_2 = excluded.route_2, route_3 = excluded.route_3, route_4 = excluded.route_4, + route_5 = excluded.route_5, route_6 = excluded.route_6; + + -- Analyze source table + ANALYZE osm_transportation_name_linestring_gen2; + + -- Remove entries which have been deleted from source table + DELETE FROM osm_transportation_name_linestring_gen3 + USING transportation_name.name_changes_gen + WHERE full_update IS TRUE OR ( + transportation_name.name_changes_gen.is_old IS TRUE AND + transportation_name.name_changes_gen.id = osm_transportation_name_linestring_gen3.id + ); + + -- etldoc: osm_transportation_name_linestring_gen2 -> osm_transportation_name_linestring_gen3 + INSERT INTO osm_transportation_name_linestring_gen3 (id, geometry, tags, ref, highway, subclass, brunnel, network, + route_1, route_2, route_3, route_4, route_5, route_6) + SELECT MIN(id) as id, + ST_Simplify(ST_LineMerge(ST_Collect(geometry)), 200) AS geometry, + tags, ref, highway, subclass, brunnel, network, + route_1, route_2, route_3, route_4, route_5, route_6 + FROM ( + SELECT id, + (ST_Dump(geometry)).geom AS geometry, + tags, ref, highway, subclass, + visible_text(geometry, brunnel, 7) AS brunnel, + network, route_1, route_2, route_3, route_4, route_5, route_6 + FROM osm_transportation_name_linestring_gen2 + ) osm_transportation_name_linestring_gen3_pre_merge + WHERE ( + full_update IS TRUE OR EXISTS ( + SELECT NULL + FROM transportation_name.name_changes_gen + WHERE transportation_name.name_changes_gen.is_old IS FALSE AND + transportation_name.name_changes_gen.id = osm_transportation_name_linestring_gen3_pre_merge.id + ) + ) AND ( + (highway = 'motorway' OR highway = 'construction' AND subclass = 'motorway') + ) + GROUP BY tags, ref, highway, subclass, brunnel, network, route_1, route_2, route_3, route_4, route_5, route_6 + ON CONFLICT (id) DO UPDATE SET geometry = excluded.geometry, tags = excluded.tags, ref = excluded.ref, + highway = excluded.highway, subclass = excluded.subclass, + brunnel = excluded.brunnel, network = excluded.network, route_1 = excluded.route_1, + route_2 = excluded.route_2, route_3 = excluded.route_3, route_4 = excluded.route_4, + route_5 = excluded.route_5, route_6 = excluded.route_6; + + -- Analyze source table + ANALYZE osm_transportation_name_linestring_gen3; + + -- Remove entries which have been deleted from source table + DELETE FROM osm_transportation_name_linestring_gen4 + USING transportation_name.name_changes_gen + WHERE full_update IS TRUE OR ( + transportation_name.name_changes_gen.is_old IS TRUE AND + transportation_name.name_changes_gen.id = osm_transportation_name_linestring_gen4.id + ); + + -- etldoc: osm_transportation_name_linestring_gen3 -> osm_transportation_name_linestring_gen4 + INSERT INTO osm_transportation_name_linestring_gen4 (id, geometry, tags, ref, highway, subclass, brunnel, network, + route_1, route_2, route_3, route_4, route_5, route_6) + SELECT MIN(id) as id, + ST_Simplify(ST_LineMerge(ST_Collect(geometry)), 500) AS geometry, + tags, ref, highway, subclass, brunnel, network, + route_1, route_2, route_3, route_4, route_5, route_6 + FROM ( + SELECT id, + (ST_Dump(geometry)).geom AS geometry, + tags, ref, highway, subclass, + visible_text(geometry, brunnel, 6) AS brunnel, + network, route_1, route_2, route_3, route_4, route_5, route_6 + FROM osm_transportation_name_linestring_gen3 + ) osm_transportation_name_linestring_gen4_pre_merge + WHERE ( + full_update IS TRUE OR EXISTS ( + SELECT NULL + FROM transportation_name.name_changes_gen + WHERE transportation_name.name_changes_gen.is_old IS FALSE AND + transportation_name.name_changes_gen.id = osm_transportation_name_linestring_gen4_pre_merge.id + ) + ) AND ( + ST_Length(geometry) > 20000 AND + (highway = 'motorway' OR highway = 'construction' AND subclass = 'motorway') + ) + GROUP BY tags, ref, highway, subclass, brunnel, network, route_1, route_2, route_3, route_4, route_5, route_6 + ON CONFLICT (id) DO UPDATE SET geometry = excluded.geometry, tags = excluded.tags, ref = excluded.ref, + highway = excluded.highway, subclass = excluded.subclass, + brunnel = excluded.brunnel, network = excluded.network, route_1 = excluded.route_1, + route_2 = excluded.route_2, route_3 = excluded.route_3, route_4 = excluded.route_4, + route_5 = excluded.route_5, route_6 = excluded.route_6; + + -- noinspection SqlWithoutWhere + DELETE FROM transportation_name.name_changes_gen; + + RAISE LOG 'Refresh transportation_name merged done in %', age(clock_timestamp(), t); +END; +$$ LANGUAGE plpgsql; + +-- Ensure tables are emtpy if they haven't been created +TRUNCATE osm_transportation_name_linestring_gen1; +TRUNCATE osm_transportation_name_linestring_gen2; +TRUNCATE osm_transportation_name_linestring_gen3; +TRUNCATE osm_transportation_name_linestring_gen4; + +SELECT update_transportation_name_linestring_gen(TRUE); + +-- Indexes for queries originating from update_transportation_name_linestring_gen() function +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen1_update_partial_idx + ON osm_transportation_name_linestring_gen1 (id) + WHERE (highway IN ('motorway', 'trunk') OR highway = 'construction' AND subclass IN ('motorway', 'trunk')) + AND ST_Length(geometry) > 14000; +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen2_update_partial_idx + ON osm_transportation_name_linestring_gen2 (id) + WHERE (highway = 'motorway' OR highway = 'construction' AND subclass = 'motorway') + AND ST_Length(geometry) > 20000; +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen3_update_partial_idx + ON osm_transportation_name_linestring_gen3 (id) + WHERE (highway = 'motorway' OR highway = 'construction' AND subclass = 'motorway') + AND ST_Length(geometry) > 20000; + +-- Geometry Indexes +CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen1_geometry_idx + ON osm_transportation_name_linestring_gen1 USING gist (geometry); +CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen2_geometry_idx + ON osm_transportation_name_linestring_gen2 USING gist (geometry); +CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen3_geometry_idx + ON osm_transportation_name_linestring_gen3 USING gist (geometry); +CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen4_geometry_idx + ON osm_transportation_name_linestring_gen4 USING gist (geometry); + +-- Handle updates + +-- Trigger to update "osm_transportation_name_network" from "osm_route_member" and "osm_highway_linestring" + +CREATE TABLE IF NOT EXISTS transportation_name.network_changes +( + is_old bool, + osm_id bigint, + PRIMARY KEY (is_old, osm_id) +); + +-- Store IDs of changed elements from osm_route_member table. +CREATE OR REPLACE FUNCTION transportation_name.route_member_store() RETURNS trigger AS +$$ +BEGIN + IF tg_op = 'DELETE' OR (tg_op = 'UPDATE' AND (old.member IS DISTINCT FROM new.member)) + THEN + INSERT INTO transportation_name.network_changes(is_old, osm_id) + VALUES (TRUE, old.member) + ON CONFLICT(is_old, osm_id) DO NOTHING; + END IF; + IF (tg_op IN ('UPDATE', 'INSERT')) + THEN + INSERT INTO transportation_name.network_changes(is_old, osm_id) + VALUES (FALSE, new.member) + ON CONFLICT(is_old, osm_id) DO NOTHING; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +-- Store IDs of changed elements from osm_highway_linestring table. +CREATE OR REPLACE FUNCTION transportation_name.highway_linestring_store() RETURNS trigger AS +$$ +BEGIN + IF tg_op = 'DELETE' OR (tg_op = 'UPDATE' AND (old.osm_id IS DISTINCT FROM new.osm_id)) + THEN + INSERT INTO transportation_name.network_changes(is_old, osm_id) + VALUES (TRUE, old.osm_id) + ON CONFLICT(is_old, osm_id) DO NOTHING; + END IF; + IF (tg_op IN ('UPDATE', 'INSERT')) + THEN + INSERT INTO transportation_name.network_changes(is_old, osm_id) + VALUES (FALSE, new.osm_id) + ON CONFLICT(is_old, osm_id) DO NOTHING; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS transportation_name.updates_network +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION transportation_name.flag_network() RETURNS trigger AS +$$ +BEGIN + INSERT INTO transportation_name.updates_network(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION transportation_name.refresh_network() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh transportation_name_network'; + + -- Update Way-Relations and analyze table afterwards + PERFORM update_osm_route_member(FALSE); + ANALYZE transportation_route_member_coalesced; + + -- REFRESH osm_transportation_name_network + DELETE + FROM osm_transportation_name_network + USING transportation_name.network_changes c + WHERE c.is_old IS TRUE AND osm_transportation_name_network.osm_id = c.osm_id; + + UPDATE osm_highway_linestring + SET network = NULL + FROM transportation_name.network_changes c + WHERE c.is_old IS TRUE AND osm_highway_linestring.osm_id = c.osm_id; + + UPDATE osm_highway_linestring_gen_z11 + SET network = NULL + FROM transportation_name.network_changes c + WHERE c.is_old IS TRUE AND osm_highway_linestring_gen_z11.osm_id = c.osm_id; + + UPDATE osm_highway_linestring + SET network = rm.network_type + FROM transportation_name.network_changes c + JOIN transportation_route_member_coalesced rm ON (c.osm_id = rm.member AND rm.concurrency_index=1) + WHERE c.is_old IS FALSE AND osm_highway_linestring.osm_id=c.osm_id; + + UPDATE osm_highway_linestring_gen_z11 + SET network = rm.network_type + FROM transportation_name.network_changes c + JOIN transportation_route_member_coalesced rm ON (c.osm_id = rm.member AND rm.concurrency_index=1) + WHERE c.is_old IS FALSE AND osm_highway_linestring_gen_z11.osm_id=c.osm_id; + + INSERT INTO osm_transportation_name_network + SELECT + geometry, + osm_id, + tags || get_basic_names(tags, geometry) AS tags, + ref, + highway, + subclass, + brunnel, + level, + sac_scale, + layer, + indoor, + network_type, + route_1, route_2, route_3, route_4, route_5, route_6, + z_order, + route_rank + FROM ( + SELECT hl.geometry, + hl.osm_id, + transportation_name_tags(hl.geometry, hl.tags, hl.name, hl.name_en, hl.name_de) AS tags, + rm1.network_type, + CASE + WHEN rm1.network_type IS NOT NULL AND rm1.ref::text <> '' + THEN rm1.ref::text + ELSE NULLIF(hl.ref, '') + END AS ref, + hl.highway, + NULLIF(hl.construction, '') AS subclass, + brunnel(hl.is_bridge, hl.is_tunnel, hl.is_ford) AS brunnel, + sac_scale, + CASE WHEN highway IN ('footway', 'steps') THEN layer END AS layer, + CASE WHEN highway IN ('footway', 'steps') THEN level END AS level, + CASE WHEN highway IN ('footway', 'steps') THEN indoor END AS indoor, + create_route_hstore(rm1.network, rm1.ref, rm1.name, rm1.colour, rm1.ref_colour) AS route_1, + create_route_hstore(rm2.network, rm2.ref, rm2.name, rm2.colour, rm2.ref_colour) AS route_2, + create_route_hstore(rm3.network, rm3.ref, rm3.name, rm3.colour, rm3.ref_colour) AS route_3, + create_route_hstore(rm4.network, rm4.ref, rm4.name, rm4.colour, rm4.ref_colour) AS route_4, + create_route_hstore(rm5.network, rm5.ref, rm5.name, rm5.colour, rm5.ref_colour) AS route_5, + create_route_hstore(rm6.network, rm6.ref, rm6.name, rm6.colour, rm6.ref_colour) AS route_6, + hl.z_order, + LEAST(rm1.rank, rm2.rank, rm3.rank, rm4.rank, rm5.rank, rm6.rank) AS route_rank + FROM osm_highway_linestring hl + JOIN transportation_name.network_changes AS c ON + c.is_old IS FALSE AND hl.osm_id = c.osm_id + LEFT OUTER JOIN transportation_route_member_coalesced rm1 ON rm1.member = hl.osm_id AND rm1.concurrency_index=1 + LEFT OUTER JOIN transportation_route_member_coalesced rm2 ON rm2.member = hl.osm_id AND rm2.concurrency_index=2 + LEFT OUTER JOIN transportation_route_member_coalesced rm3 ON rm3.member = hl.osm_id AND rm3.concurrency_index=3 + LEFT OUTER JOIN transportation_route_member_coalesced rm4 ON rm4.member = hl.osm_id AND rm4.concurrency_index=4 + LEFT OUTER JOIN transportation_route_member_coalesced rm5 ON rm5.member = hl.osm_id AND rm5.concurrency_index=5 + LEFT OUTER JOIN transportation_route_member_coalesced rm6 ON rm6.member = hl.osm_id AND rm6.concurrency_index=6 + WHERE (hl.name <> '' OR hl.ref <> '' OR rm1.ref <> '' OR rm1.network <> '') + AND hl.highway <> '' + ) AS t + ON CONFLICT DO NOTHING; + + -- noinspection SqlWithoutWhere + DELETE FROM transportation_name.network_changes; + -- noinspection SqlWithoutWhere + DELETE FROM transportation_name.updates_network; + + RAISE LOG 'Refresh transportation_name network done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER trigger_store_transportation_route_member + AFTER INSERT OR UPDATE OR DELETE + ON osm_route_member + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.route_member_store(); + +CREATE TRIGGER trigger_store_transportation_highway_linestring + AFTER INSERT OR UPDATE OR DELETE + ON osm_highway_linestring + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.highway_linestring_store(); + +CREATE TRIGGER trigger_flag_transportation_name + AFTER INSERT + ON transportation_name.network_changes + FOR EACH STATEMENT +EXECUTE PROCEDURE transportation_name.flag_network(); + +CREATE CONSTRAINT TRIGGER trigger_refresh_network + AFTER INSERT + ON transportation_name.updates_network + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.refresh_network(); + +-- Handle updates on +-- osm_transportation_name_network -> osm_transportation_name_linestring +-- osm_shipway_linestring -> osm_transportation_name_linestring +-- osm_aerialway_linestring -> osm_transportation_name_linestring +-- osm_transportation_name_linestring -> osm_transportation_name_linestring_gen1 +-- osm_transportation_name_linestring -> osm_transportation_name_linestring_gen2 +-- osm_transportation_name_linestring -> osm_transportation_name_linestring_gen3 +-- osm_transportation_name_linestring -> osm_transportation_name_linestring_gen4 + +CREATE OR REPLACE AGGREGATE array_cat_agg(anycompatiblearray) ( + SFUNC=array_cat, + STYPE=anycompatiblearray, + INITCOND = '{}' +); + +CREATE TABLE IF NOT EXISTS transportation_name.name_changes +( + is_old boolean, + osm_id bigint, + PRIMARY KEY (is_old, osm_id) +); +CREATE TABLE IF NOT EXISTS transportation_name.shipway_changes +( + is_old boolean, + osm_id bigint, + PRIMARY KEY (is_old, osm_id) +); +CREATE TABLE IF NOT EXISTS transportation_name.aerialway_changes +( + is_old boolean, + osm_id bigint, + PRIMARY KEY (is_old, osm_id) +); + +-- Store IDs of changed elements from osm_transportation_name_network table. +CREATE OR REPLACE FUNCTION transportation_name.name_network_store() RETURNS trigger AS +$$ +BEGIN + IF (tg_op IN ('DELETE', 'UPDATE')) + THEN + INSERT INTO transportation_name.name_changes(is_old, osm_id) + VALUES (TRUE, old.osm_id) + ON CONFLICT (is_old, osm_id) DO NOTHING; + END IF; + IF (tg_op IN ('UPDATE', 'INSERT')) + THEN + INSERT INTO transportation_name.name_changes(is_old, osm_id) + VALUES (FALSE, new.osm_id) + ON CONFLICT (is_old, osm_id) DO NOTHING; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +-- Store IDs of changed elements from osm_shipway_linestring table. +CREATE OR REPLACE FUNCTION transportation_name.name_shipway_store() RETURNS trigger AS +$$ +BEGIN + IF (tg_op IN ('DELETE', 'UPDATE')) + THEN + INSERT INTO transportation_name.shipway_changes(is_old, osm_id) + VALUES (TRUE, old.osm_id) + ON CONFLICT (is_old, osm_id) DO NOTHING; + END IF; + IF (tg_op IN ('UPDATE', 'INSERT')) + THEN + INSERT INTO transportation_name.shipway_changes(is_old, osm_id) + VALUES (FALSE, new.osm_id) + ON CONFLICT (is_old, osm_id) DO NOTHING; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +-- Store IDs of changed elements from osm_aerialway_linestring table. +CREATE OR REPLACE FUNCTION transportation_name.name_aerialway_store() RETURNS trigger AS +$$ +BEGIN + IF (tg_op IN ('DELETE', 'UPDATE')) + THEN + INSERT INTO transportation_name.aerialway_changes(is_old, osm_id) + VALUES (TRUE, old.osm_id) + ON CONFLICT (is_old, osm_id) DO NOTHING; + END IF; + IF (tg_op IN ('UPDATE', 'INSERT')) + THEN + INSERT INTO transportation_name.aerialway_changes(is_old, osm_id) + VALUES (FALSE, new.osm_id) + ON CONFLICT (is_old, osm_id) DO NOTHING; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +-- Store IDs of changed elements from osm_transportation_name_linestring table. +CREATE OR REPLACE FUNCTION transportation_name.name_linestring_store() RETURNS trigger AS +$$ +BEGIN + IF (tg_op = 'DELETE') + THEN + INSERT INTO transportation_name.name_changes_gen(is_old, id) + VALUES (TRUE, old.id) + ON CONFLICT (is_old, id) DO NOTHING; + END IF; + IF (tg_op = 'UPDATE' OR tg_op = 'INSERT') + THEN + INSERT INTO transportation_name.name_changes_gen(is_old, id) + VALUES (FALSE, new.id) + ON CONFLICT (is_old, id) DO NOTHING; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS transportation_name.updates_name +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE TABLE IF NOT EXISTS transportation_name.updates_shipway +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE TABLE IF NOT EXISTS transportation_name.updates_aerialway +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION transportation_name.flag_name() RETURNS trigger AS +$$ +BEGIN + INSERT INTO transportation_name.updates_name(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION transportation_name.flag_shipway() RETURNS trigger AS +$$ +BEGIN + INSERT INTO transportation_name.updates_shipway(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION transportation_name.flag_aerialway() RETURNS trigger AS +$$ +BEGIN + INSERT INTO transportation_name.updates_aerialway(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION transportation_name.refresh_name() RETURNS trigger AS +$BODY$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh transportation_name'; + + -- REFRESH osm_transportation_name_linestring from osm_transportation_name_network + + -- Analyze tracking and source tables before performing update + ANALYZE transportation_name.name_changes; + ANALYZE osm_transportation_name_network; + + -- Fetch updated and deleted Merged-LineString from relation-table filtering for each Merged-LineString which + -- contains an updated Source-LineString. + -- Additionally attach a list of Source-LineString-IDs to each Merged-LineString in order to unnest them later. + CREATE TEMPORARY TABLE affected_merged_linestrings AS + SELECT m.id, array_agg(source_id) AS source_ids + FROM osm_transportation_name_linestring_source_ids m + WHERE m.source = 0 AND EXISTS( + SELECT NULL + FROM transportation_name.name_changes c + WHERE c.is_old IS TRUE AND c.osm_id = m.source_id + ) + GROUP BY id; + + -- Analyze the created table to speed up subsequent queries + ANALYZE affected_merged_linestrings; + + -- Delete all Merged-LineStrings which contained an updated or deleted Source-LineString + DELETE + FROM osm_transportation_name_linestring m + USING affected_merged_linestrings + WHERE affected_merged_linestrings.id = m.id; + DELETE + FROM osm_transportation_name_linestring_source_ids m + USING affected_merged_linestrings + WHERE affected_merged_linestrings.id = m.id; + + -- Analyze the tables affected by the delete-query in order to speed up subsequent queries + ANALYZE osm_transportation_name_linestring; + ANALYZE osm_transportation_name_linestring_source_ids; + + -- Create a table containing all LineStrings which should be merged + CREATE TEMPORARY TABLE linestrings_to_merge AS + -- Add all Source-LineStrings affected by this update + SELECT osm_id, NULL::INTEGER AS id, NULL::BIGINT[] AS source_ids, geometry, tags, ref, highway, subclass, brunnel, + sac_scale, level, layer, indoor, network_type, route_1, route_2, route_3, route_4, route_5, route_6, + z_order, route_rank + FROM ( + -- Get Source-LineString-IDs of deleted or updated elements + SELECT unnest(affected_merged_linestrings.source_ids)::bigint AS source_id + FROM affected_merged_linestrings + UNION + -- Get Source-LineString-IDs of inserted or updated elements + SELECT osm_id AS source_id FROM transportation_name.name_changes WHERE is_old IS FALSE + ORDER BY source_id + ) affected_source_linestrings + JOIN osm_transportation_name_network ON ( + affected_source_linestrings.source_id = osm_transportation_name_network.osm_id + ) + WHERE coalesce(tags->'name', '') <> '' OR coalesce(ref, '') <> ''; + + -- Drop temporary tables early to save resources + DROP TABLE affected_merged_linestrings; + + -- Analyze the created table to speed up subsequent queries + ANALYZE linestrings_to_merge; + + -- Add all Merged-LineStrings intersecting with Source-LineStrings affected by this update + INSERT INTO linestrings_to_merge + SELECT NULL::BIGINT AS osm_id, m.id, + ARRAY( + SELECT s.source_id + FROM osm_transportation_name_linestring_source_ids s + WHERE s.source = 0 AND m.id = s.id + )::BIGINT[] AS source_ids, + m.geometry, m.tags, m.ref, m.highway, m.subclass, m.brunnel, m.sac_scale, + m.level, m.layer, m.indoor, m.network AS network_type, m.route_1, m.route_2, m.route_3, + m.route_4, m.route_5, m.route_6, m.z_order, m.route_rank + FROM linestrings_to_merge + JOIN osm_transportation_name_linestring m ON (ST_Intersects(linestrings_to_merge.geometry, m.geometry)) + WHERE m.source = 0; + + -- Analyze the created table to speed up subsequent queries + ANALYZE linestrings_to_merge; + + -- Delete all Merged-LineStrings intersecting with Source-LineStrings affected by this update. + -- We can use the linestrings_to_merge table since Source-LineStrings affected by this update and present in the + -- table will have their ID-Column set to NULL by the previous query. + DELETE + FROM osm_transportation_name_linestring m + USING linestrings_to_merge + WHERE m.id = linestrings_to_merge.id; + DELETE + FROM osm_transportation_name_linestring_source_ids m + USING linestrings_to_merge + WHERE m.id = linestrings_to_merge.id; + + -- Create table containing all LineStrings to and create clusters of intersecting LineStrings partitioned by their + -- groups + CREATE TEMPORARY TABLE clustered_linestrings_to_merge AS + SELECT *, + -- Get intersecting clusters by setting minimum distance to 0 and minimum intersecting points to 1. + -- https://postgis.net/docs/ST_ClusterDBSCAN.html + ST_ClusterDBSCAN(geometry, 0, 1) OVER ( + PARTITION BY tags, ref, highway, subclass, brunnel, level, layer, sac_scale, indoor, network_type, + route_1, route_2, route_3, route_4, route_5, route_6 + ) AS cluster, + -- ST_ClusterDBSCAN returns an increasing integer as the cluster-ids within each partition starting at 0. + -- This leads to clusters having the same ID across multiple partitions therefore we generate a + -- Cluster-Group-ID by utilizing the DENSE_RANK function sorted over the partition columns. + DENSE_RANK() OVER ( + ORDER BY tags, ref, highway, subclass, brunnel, level, layer, sac_scale, indoor, network_type, route_1, + route_2, route_3, route_4, route_5, route_6 + ) as cluster_group + FROM linestrings_to_merge; + + -- Drop temporary tables early to save resources + DROP TABLE linestrings_to_merge; + + -- Create index on cluster columns and analyze the created table to speed up subsequent queries + CREATE INDEX ON clustered_linestrings_to_merge (cluster_group, cluster); + ANALYZE clustered_linestrings_to_merge; + + -- Create temporary Merged-LineString to Source-LineStrings-ID columns to store relations before they have been + -- intersected + ALTER TABLE osm_transportation_name_linestring ADD COLUMN IF NOT EXISTS new_source_ids BIGINT[]; + ALTER TABLE osm_transportation_name_linestring ADD COLUMN IF NOT EXISTS old_source_ids BIGINT[]; + + + WITH inserted_linestrings AS ( + -- Merge LineStrings of each cluster and insert them + INSERT INTO osm_transportation_name_linestring(source, geometry, new_source_ids, old_source_ids, tags, ref, + highway, subclass, brunnel, sac_scale, "level", layer, indoor, + network, route_1, route_2, route_3, route_4, route_5, route_6, + z_order, route_rank) + SELECT 0 AS source, (ST_Dump(ST_LineMerge(ST_Union(geometry)))).geom AS geometry, + -- We use St_Union instead of St_Collect to ensure no overlapping points exist within the geometries + -- to merge. https://postgis.net/docs/ST_Union.html + -- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString + -- if more than two LineStrings form an intersection or no intersection could be found. + -- https://postgis.net/docs/ST_LineMerge.html + -- In order to not end up with a mixture of LineStrings and MultiLineStrings we dump eventual + -- MultiLineStrings via ST_Dump. https://postgis.net/docs/ST_Dump.html + coalesce( array_agg(osm_id) FILTER (WHERE osm_id IS NOT NULL), '{}' )::BIGINT[] AS new_source_ids, + array_cat_agg(source_ids)::BIGINT[] as old_source_ids, + tags, ref, highway, subclass, brunnel, sac_scale, level, layer, + indoor, network_type, route_1, route_2, route_3, route_4, route_5, route_6, + min(z_order) AS z_order, min(route_rank) AS route_rank + FROM clustered_linestrings_to_merge + GROUP BY cluster_group, cluster, tags, ref, highway, subclass, brunnel, level, layer, sac_scale, indoor, + network_type, route_1, route_2, route_3, route_4, route_5, route_6 + RETURNING source, id, new_source_ids, old_source_ids, geometry + ) + -- Store OSM-IDs of Source-LineStrings by intersecting Merged-LineStrings with their sources. + -- This is required because ST_LineMerge only merges across singular intersections and groups its output into a + -- MultiLineString if more than two LineStrings form an intersection or no intersection could be found. + INSERT INTO osm_transportation_name_linestring_source_ids (source, id, source_id) + SELECT m.source, m.id, source_id + FROM ( + SELECT source, id, source_id, geometry + FROM inserted_linestrings + CROSS JOIN LATERAL ( + SELECT DISTINCT all_source_ids.source_id + FROM unnest( + array_cat(inserted_linestrings.new_source_ids, inserted_linestrings.old_source_ids) + ) AS all_source_ids(source_id) + ) source_ids + ) m + JOIN osm_transportation_name_network s ON (m.source_id = s.osm_id) + WHERE ST_Intersects(s.geometry, m.geometry) + ON CONFLICT (source, id, source_id) DO NOTHING; + + -- Cleanup remaining table + DROP TABLE clustered_linestrings_to_merge; + + -- Drop temporary Merged-LineString to Source-LineStrings-ID columns + ALTER TABLE osm_transportation_name_linestring DROP COLUMN IF EXISTS new_source_ids; + ALTER TABLE osm_transportation_name_linestring DROP COLUMN IF EXISTS old_source_ids; + + -- noinspection SqlWithoutWhere + DELETE FROM transportation_name.name_changes; + -- noinspection SqlWithoutWhere + DELETE FROM transportation_name.updates_name; + + RAISE LOG 'Refresh transportation_name done in %', age(clock_timestamp(), t); + + -- Update gen1, gen2, gen3 and gen4 tables + PERFORM update_transportation_name_linestring_gen(FALSE); + + RETURN NULL; +END; +$BODY$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION transportation_name.refresh_shipway_linestring() RETURNS trigger AS +$BODY$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh transportation_name shiwpway'; + + -- REFRESH osm_transportation_name_linestring from osm_shipway_linestring + + -- Analyze tracking and source tables before performing update + ANALYZE transportation_name.shipway_changes; + ANALYZE osm_shipway_linestring; + + -- Fetch updated and deleted Merged-LineString from relation-table filtering for each Merged-LineString which + -- contains an updated Source-LineString. + -- Additionally attach a list of Source-LineString-IDs to each Merged-LineString in order to unnest them later. + CREATE TEMPORARY TABLE affected_merged_linestrings AS + SELECT m.id, array_agg(source_id) AS source_ids + FROM osm_transportation_name_linestring_source_ids m + WHERE m.source = 1 AND EXISTS( + SELECT NULL + FROM transportation_name.shipway_changes c + WHERE c.is_old IS TRUE AND c.osm_id = m.source_id + ) + GROUP BY id; + + -- Analyze the created table to speed up subsequent queries + ANALYZE affected_merged_linestrings; + + -- Delete all Merged-LineStrings which contained an updated or deleted Source-LineString + DELETE + FROM osm_transportation_name_linestring m + USING affected_merged_linestrings + WHERE affected_merged_linestrings.id = m.id; + DELETE + FROM osm_transportation_name_linestring_source_ids m + USING affected_merged_linestrings + WHERE affected_merged_linestrings.id = m.id; + + -- Analyze the tables affected by the delete-query in order to speed up subsequent queries + ANALYZE osm_transportation_name_linestring; + ANALYZE osm_transportation_name_linestring_source_ids; + + -- Create a table containing all LineStrings which should be merged + CREATE TEMPORARY TABLE linestrings_to_merge AS + -- Add all Source-LineStrings affected by this update + SELECT osm_id, NULL::INTEGER AS id, NULL::BIGINT[] AS source_ids, geometry, + transportation_name_tags( + NULL::geometry, tags, name, name_en, name_de + ) AS tags, shipway AS subclass, layer, z_order + FROM ( + -- Get Source-LineString-IDs of deleted or updated elements + SELECT unnest(affected_merged_linestrings.source_ids)::bigint AS source_id + FROM affected_merged_linestrings + UNION + -- Get Source-LineString-IDs of inserted or updated elements + SELECT osm_id AS source_id FROM transportation_name.shipway_changes WHERE is_old IS FALSE + ORDER BY source_id + ) affected_source_linestrings + JOIN osm_shipway_linestring ON ( + affected_source_linestrings.source_id = osm_shipway_linestring.osm_id + ) + WHERE name <> ''; + + -- Drop temporary tables early to save resources + DROP TABLE affected_merged_linestrings; + + -- Analyze the created table to speed up subsequent queries + ANALYZE linestrings_to_merge; + + -- Add all Merged-LineStrings intersecting with Source-LineStrings affected by this update + INSERT INTO linestrings_to_merge + SELECT NULL::BIGINT AS osm_id, m.id, + ARRAY( + SELECT s.source_id + FROM osm_transportation_name_linestring_source_ids s + WHERE s.source = 1 AND m.id = s.id + )::BIGINT[] AS source_ids, + m.geometry, m.tags, m.subclass, m.layer, m.z_order + FROM linestrings_to_merge + JOIN osm_transportation_name_linestring m ON (ST_Intersects(linestrings_to_merge.geometry, m.geometry)) + WHERE m.source = 1; + + -- Analyze the created table to speed up subsequent queries + ANALYZE linestrings_to_merge; + + -- Delete all Merged-LineStrings intersecting with Source-LineStrings affected by this update. + -- We can use the linestrings_to_merge table since Source-LineStrings affected by this update and present in the + -- table will have their ID-Column set to NULL by the previous query. + DELETE + FROM osm_transportation_name_linestring m + USING linestrings_to_merge + WHERE m.id = linestrings_to_merge.id; + DELETE + FROM osm_transportation_name_linestring_source_ids m + USING linestrings_to_merge + WHERE m.id = linestrings_to_merge.id; + + -- Create table containing all LineStrings to and create clusters of intersecting LineStrings partitioned by their + -- groups + CREATE TEMPORARY TABLE clustered_linestrings_to_merge AS + SELECT *, + -- Get intersecting clusters by setting minimum distance to 0 and minimum intersecting points to 1. + -- https://postgis.net/docs/ST_ClusterDBSCAN.html + ST_ClusterDBSCAN(geometry, 0, 1) OVER (PARTITION BY tags, subclass, layer) AS cluster, + -- ST_ClusterDBSCAN returns an increasing integer as the cluster-ids within each partition starting at 0. + -- This leads to clusters having the same ID across multiple partitions therefore we generate a + -- Cluster-Group-ID by utilizing the DENSE_RANK function sorted over the partition columns. + DENSE_RANK() OVER (ORDER BY tags, subclass, layer) as cluster_group + FROM linestrings_to_merge; + + -- Drop temporary tables early to save resources + DROP TABLE linestrings_to_merge; + + -- Create index on cluster columns and analyze the created table to speed up subsequent queries + CREATE INDEX ON clustered_linestrings_to_merge (cluster_group, cluster); + ANALYZE clustered_linestrings_to_merge; + + -- Create temporary Merged-LineString to Source-LineStrings-ID columns to store relations before they have been + -- intersected + ALTER TABLE osm_transportation_name_linestring ADD COLUMN IF NOT EXISTS new_source_ids BIGINT[]; + ALTER TABLE osm_transportation_name_linestring ADD COLUMN IF NOT EXISTS old_source_ids BIGINT[]; + + WITH inserted_linestrings AS ( + -- Merge LineStrings of each cluster and insert them + INSERT INTO osm_transportation_name_linestring(source, geometry, new_source_ids, old_source_ids, tags, highway, + subclass, z_order) + SELECT 1 AS source, (ST_Dump(ST_LineMerge(ST_Union(geometry)))).geom AS geometry, + -- We use St_Union instead of St_Collect to ensure no overlapping points exist within the geometries + -- to merge. https://postgis.net/docs/ST_Union.html + -- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString + -- if more than two LineStrings form an intersection or no intersection could be found. + -- https://postgis.net/docs/ST_LineMerge.html + -- In order to not end up with a mixture of LineStrings and MultiLineStrings we dump eventual + -- MultiLineStrings via ST_Dump. https://postgis.net/docs/ST_Dump.html + coalesce( array_agg(osm_id) FILTER (WHERE osm_id IS NOT NULL), '{}' )::BIGINT[] AS new_source_ids, + array_cat_agg(source_ids)::BIGINT[] as old_source_ids, + tags, 'shipway' AS highway, subclass, min(z_order) AS z_order + FROM clustered_linestrings_to_merge + GROUP BY cluster_group, cluster, tags, subclass, layer + RETURNING source, id, new_source_ids, old_source_ids, geometry + ) + -- Store OSM-IDs of Source-LineStrings by intersecting Merged-LineStrings with their sources. + -- This is required because ST_LineMerge only merges across singular intersections and groups its output into a + -- MultiLineString if more than two LineStrings form an intersection or no intersection could be found. + INSERT INTO osm_transportation_name_linestring_source_ids (source, id, source_id) + SELECT m.source, m.id, source_id + FROM ( + SELECT source, id, source_id, geometry + FROM inserted_linestrings + CROSS JOIN LATERAL ( + SELECT DISTINCT all_source_ids.source_id + FROM unnest( + array_cat(inserted_linestrings.new_source_ids, inserted_linestrings.old_source_ids) + ) AS all_source_ids(source_id) + ) source_ids + ) m + JOIN osm_shipway_linestring s ON (m.source_id = s.osm_id) + WHERE ST_Intersects(s.geometry, m.geometry) + ON CONFLICT (source, id, source_id) DO NOTHING; + + -- Cleanup remaining table + DROP TABLE clustered_linestrings_to_merge; + + -- Drop temporary Merged-LineString to Source-LineStrings-ID columns + ALTER TABLE osm_transportation_name_linestring DROP COLUMN IF EXISTS new_source_ids; + ALTER TABLE osm_transportation_name_linestring DROP COLUMN IF EXISTS old_source_ids; + + -- noinspection SqlWithoutWhere + DELETE FROM transportation_name.shipway_changes; + -- noinspection SqlWithoutWhere + DELETE FROM transportation_name.updates_shipway; + + RAISE LOG 'Refresh transportation_name shipway done in %', age(clock_timestamp(), t); + + -- Update gen1, gen2, gen3 and gen4 tables + PERFORM update_transportation_name_linestring_gen(FALSE); + + RETURN NULL; +END; +$BODY$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION transportation_name.refresh_aerialway_linestring() RETURNS trigger AS +$BODY$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh transportation_name aerialway'; + + -- REFRESH osm_transportation_name_linestring from osm_aerialway_linestring + + -- Analyze tracking and source tables before performing update + ANALYZE transportation_name.aerialway_changes; + ANALYZE osm_aerialway_linestring; + + -- Fetch updated and deleted Merged-LineString from relation-table filtering for each Merged-LineString which + -- contains an updated Source-LineString. + -- Additionally attach a list of Source-LineString-IDs to each Merged-LineString in order to unnest them later. + CREATE TEMPORARY TABLE affected_merged_linestrings AS + SELECT m.id, array_agg(source_id) AS source_ids + FROM osm_transportation_name_linestring_source_ids m + WHERE m.source = 2 AND EXISTS( + SELECT NULL + FROM transportation_name.aerialway_changes c + WHERE c.is_old IS TRUE AND c.osm_id = m.source_id + ) + GROUP BY id; + + -- Analyze the created table to speed up subsequent queries + ANALYZE affected_merged_linestrings; + + -- Delete all Merged-LineStrings which contained an updated or deleted Source-LineString + DELETE + FROM osm_transportation_name_linestring m + USING affected_merged_linestrings + WHERE affected_merged_linestrings.id = m.id; + DELETE + FROM osm_transportation_name_linestring_source_ids m + USING affected_merged_linestrings + WHERE affected_merged_linestrings.id = m.id; + + -- Analyze the tables affected by the delete-query in order to speed up subsequent queries + ANALYZE osm_transportation_name_linestring; + ANALYZE osm_transportation_name_linestring_source_ids; + + -- Create a table containing all LineStrings which should be merged + CREATE TEMPORARY TABLE linestrings_to_merge AS + -- Add all Source-LineStrings affected by this update + SELECT osm_id, NULL::INTEGER AS id, NULL::BIGINT[] AS source_ids, geometry, + transportation_name_tags( + NULL::geometry, tags, name, name_en, name_de + ) AS tags, aerialway AS subclass, layer, z_order + FROM ( + -- Get Source-LineString-IDs of deleted or updated elements + SELECT unnest(affected_merged_linestrings.source_ids)::bigint AS source_id + FROM affected_merged_linestrings + UNION + -- Get Source-LineString-IDs of inserted or updated elements + SELECT osm_id AS source_id FROM transportation_name.aerialway_changes WHERE is_old IS FALSE + ORDER BY source_id + ) affected_source_linestrings + JOIN osm_aerialway_linestring ON ( + affected_source_linestrings.source_id = osm_aerialway_linestring.osm_id + ) + WHERE name <> ''; + + -- Drop temporary tables early to save resources + DROP TABLE affected_merged_linestrings; + + -- Analyze the created table to speed up subsequent queries + ANALYZE linestrings_to_merge; + + -- Add all Merged-LineStrings intersecting with Source-LineStrings affected by this update + INSERT INTO linestrings_to_merge + SELECT NULL::BIGINT AS osm_id, m.id, + ARRAY( + SELECT s.source_id + FROM osm_transportation_name_linestring_source_ids s + WHERE s.source = 2 AND m.id = s.id + )::BIGINT[] AS source_ids, + m.geometry, m.tags, m.subclass, m.layer, m.z_order + FROM linestrings_to_merge + JOIN osm_transportation_name_linestring m ON (ST_Intersects(linestrings_to_merge.geometry, m.geometry)) + WHERE m.source = 2; + + -- Analyze the created table to speed up subsequent queries + ANALYZE linestrings_to_merge; + + -- Delete all Merged-LineStrings intersecting with Source-LineStrings affected by this update. + -- We can use the linestrings_to_merge table since Source-LineStrings affected by this update and present in the + -- table will have their ID-Column set to NULL by the previous query. + DELETE + FROM osm_transportation_name_linestring m + USING linestrings_to_merge + WHERE m.id = linestrings_to_merge.id; + DELETE + FROM osm_transportation_name_linestring_source_ids m + USING linestrings_to_merge + WHERE m.id = linestrings_to_merge.id; + + -- Create table containing all LineStrings to and create clusters of intersecting LineStrings partitioned by their + -- groups + CREATE TEMPORARY TABLE clustered_linestrings_to_merge AS + SELECT *, + -- Get intersecting clusters by setting minimum distance to 0 and minimum intersecting points to 1. + -- https://postgis.net/docs/ST_ClusterDBSCAN.html + ST_ClusterDBSCAN(geometry, 0, 1) OVER (PARTITION BY tags, subclass, layer) AS cluster, + -- ST_ClusterDBSCAN returns an increasing integer as the cluster-ids within each partition starting at 0. + -- This leads to clusters having the same ID across multiple partitions therefore we generate a + -- Cluster-Group-ID by utilizing the DENSE_RANK function sorted over the partition columns. + DENSE_RANK() OVER (ORDER BY tags, subclass, layer) as cluster_group + FROM linestrings_to_merge; + + -- Drop temporary tables early to save resources + DROP TABLE linestrings_to_merge; + + -- Create index on cluster columns and analyze the created table to speed up subsequent queries + CREATE INDEX ON clustered_linestrings_to_merge (cluster_group, cluster); + ANALYZE clustered_linestrings_to_merge; + + -- Create temporary Merged-LineString to Source-LineStrings-ID columns to store relations before they have been + -- intersected + ALTER TABLE osm_transportation_name_linestring ADD COLUMN IF NOT EXISTS new_source_ids BIGINT[]; + ALTER TABLE osm_transportation_name_linestring ADD COLUMN IF NOT EXISTS old_source_ids BIGINT[]; + + WITH inserted_linestrings AS ( + -- Merge LineStrings of each cluster and insert them + INSERT INTO osm_transportation_name_linestring(source, geometry, new_source_ids, old_source_ids, tags, highway, + subclass, z_order) + SELECT 2 AS source, (ST_Dump(ST_LineMerge(ST_Union(geometry)))).geom AS geometry, + -- We use St_Union instead of St_Collect to ensure no overlapping points exist within the geometries + -- to merge. https://postgis.net/docs/ST_Union.html + -- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString + -- if more than two LineStrings form an intersection or no intersection could be found. + -- https://postgis.net/docs/ST_LineMerge.html + -- In order to not end up with a mixture of LineStrings and MultiLineStrings we dump eventual + -- MultiLineStrings via ST_Dump. https://postgis.net/docs/ST_Dump.html + coalesce( array_agg(osm_id) FILTER (WHERE osm_id IS NOT NULL), '{}' )::BIGINT[] AS new_source_ids, + array_cat_agg(source_ids)::BIGINT[] as old_source_ids, + tags, 'aerialway' AS highway, subclass, min(z_order) AS z_order + FROM clustered_linestrings_to_merge + GROUP BY cluster_group, cluster, tags, subclass, layer + RETURNING source, id, new_source_ids, old_source_ids, geometry + ) + -- Store OSM-IDs of Source-LineStrings by intersecting Merged-LineStrings with their sources. + -- This is required because ST_LineMerge only merges across singular intersections and groups its output into a + -- MultiLineString if more than two LineStrings form an intersection or no intersection could be found. + INSERT INTO osm_transportation_name_linestring_source_ids (source, id, source_id) + SELECT m.source, m.id, source_id + FROM ( + SELECT source, id, source_id, geometry + FROM inserted_linestrings + CROSS JOIN LATERAL ( + SELECT DISTINCT all_source_ids.source_id + FROM unnest( + array_cat(inserted_linestrings.new_source_ids, inserted_linestrings.old_source_ids) + ) AS all_source_ids(source_id) + ) source_ids + ) m + JOIN osm_aerialway_linestring s ON (m.source_id = s.osm_id) + WHERE ST_Intersects(s.geometry, m.geometry) + ON CONFLICT (source, id, source_id) DO NOTHING; + + -- Cleanup remaining table + DROP TABLE clustered_linestrings_to_merge; + + -- Drop temporary Merged-LineString to Source-LineStrings-ID columns + ALTER TABLE osm_transportation_name_linestring DROP COLUMN IF EXISTS new_source_ids; + ALTER TABLE osm_transportation_name_linestring DROP COLUMN IF EXISTS old_source_ids; + + -- noinspection SqlWithoutWhere + DELETE FROM transportation_name.aerialway_changes; + -- noinspection SqlWithoutWhere + DELETE FROM transportation_name.updates_aerialway; + + RAISE LOG 'Refresh transportation_name aerialway done in %', age(clock_timestamp(), t); + + -- Update gen1, gen2, gen3 and gen4 tables + PERFORM update_transportation_name_linestring_gen(FALSE); + + RETURN NULL; +END; +$BODY$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store_transportation_name_network + AFTER INSERT OR UPDATE OR DELETE + ON osm_transportation_name_network + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.name_network_store(); + +CREATE TRIGGER trigger_store_transportation_name_shipway + AFTER INSERT OR UPDATE OR DELETE + ON osm_shipway_linestring + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.name_shipway_store(); + +CREATE TRIGGER trigger_store_transportation_name_aerialway + AFTER INSERT OR UPDATE OR DELETE + ON osm_aerialway_linestring + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.name_aerialway_store(); + +CREATE TRIGGER trigger_store_transportation_name_linestring + AFTER INSERT OR UPDATE OR DELETE + ON osm_transportation_name_linestring + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.name_linestring_store(); + +CREATE TRIGGER trigger_flag_name + AFTER INSERT + ON transportation_name.name_changes + FOR EACH STATEMENT +EXECUTE PROCEDURE transportation_name.flag_name(); + +CREATE TRIGGER trigger_flag_shipway + AFTER INSERT + ON transportation_name.shipway_changes + FOR EACH STATEMENT +EXECUTE PROCEDURE transportation_name.flag_shipway(); + +CREATE TRIGGER trigger_flag_aerialway + AFTER INSERT + ON transportation_name.aerialway_changes + FOR EACH STATEMENT +EXECUTE PROCEDURE transportation_name.flag_aerialway(); + +CREATE CONSTRAINT TRIGGER trigger_refresh_name + AFTER INSERT + ON transportation_name.updates_name + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.refresh_name(); + +CREATE CONSTRAINT TRIGGER trigger_refresh_shipway + AFTER INSERT + ON transportation_name.updates_shipway + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.refresh_shipway_linestring(); + +CREATE CONSTRAINT TRIGGER trigger_refresh_aerialway + AFTER INSERT + ON transportation_name.updates_aerialway + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.refresh_aerialway_linestring(); diff --git a/layers/water/README.md b/layers/water/README.md new file mode 100644 index 0000000..a1c0375 --- /dev/null +++ b/layers/water/README.md @@ -0,0 +1,10 @@ +## water + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#water** + +### Mapping Diagram +![Mapping diagram for water](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for water](etl_diagram.png?raw=true) diff --git a/layers/water/etl_diagram.png b/layers/water/etl_diagram.png new file mode 100644 index 0000000..6efbbb8 Binary files /dev/null and b/layers/water/etl_diagram.png differ diff --git a/layers/water/mapping.yaml b/layers/water/mapping.yaml new file mode 100644 index 0000000..8491d67 --- /dev/null +++ b/layers/water/mapping.yaml @@ -0,0 +1,117 @@ +generalized_tables: + # etldoc: osm_water_polygon_gen_z7 -> osm_water_polygon_gen_z6 + water_polygon_gen_z6: + source: water_polygon_gen_z7 + sql_filter: area>power(ZRES5,2) + tolerance: ZRES7 + + # etldoc: osm_water_polygon_gen_z8 -> osm_water_polygon_gen_z7 + water_polygon_gen_z7: + source: water_polygon_gen_z8 + sql_filter: area>power(ZRES6,2) + tolerance: ZRES8 + + # etldoc: osm_water_polygon_gen_z9 -> osm_water_polygon_gen_z8 + water_polygon_gen_z8: + source: water_polygon_gen_z9 + sql_filter: area>power(ZRES7,2) + tolerance: ZRES9 + + # etldoc: osm_water_polygon_gen_z10 -> osm_water_polygon_gen_z9 + water_polygon_gen_z9: + source: water_polygon_gen_z10 + sql_filter: area>power(ZRES8,2) + tolerance: ZRES10 + + # etldoc: osm_water_polygon_gen_z11 -> osm_water_polygon_gen_z10 + water_polygon_gen_z10: + source: water_polygon_gen_z11 + sql_filter: area>power(ZRES9,2) + tolerance: ZRES11 + + # etldoc: osm_water_polygon -> osm_water_polygon_gen_z11 + water_polygon_gen_z11: + source: water_polygon + sql_filter: area>power(ZRES10,2) + tolerance: ZRES12 + +tunnel_field: &tunnel + key: tunnel + name: is_tunnel + type: bool +bridge_field: &bridge + key: bridge + name: is_bridge + type: bool + +tables: + + # etldoc: imposm3 -> osm_water_polygon + water_polygon: + columns: + - name: osm_id + type: id + - name: geometry + type: validated_geometry + - name: area + type: area + - key: name + name: name + type: string + - name: name_en + key: name:en + type: string + - name: name_de + key: name:de + type: string + - name: tags + type: hstore_tags + - name: place + key: place + type: string + - name: natural + key: natural + type: string + - name: landuse + key: landuse + type: string + - name: waterway + key: waterway + type: string + - name: leisure + key: leisure + type: string + - name: water + key: water + type: string + - name: is_intermittent + key: intermittent + type: bool + - *tunnel + - *bridge + filters: + reject: + covered: ["yes"] + mapping: + landuse: + - reservoir + - basin + - salt_pond + leisure: + - swimming_pool + natural: + - water + - bay + - spring + waterway: + - dock + water: + - river + - stream + - canal + - ditch + - drain + - pond + - basin + - wastewater + type: polygon diff --git a/layers/water/mapping_diagram.png b/layers/water/mapping_diagram.png new file mode 100644 index 0000000..7a6f2ef Binary files /dev/null and b/layers/water/mapping_diagram.png differ diff --git a/layers/water/style.json b/layers/water/style.json new file mode 100644 index 0000000..9ce2fca --- /dev/null +++ b/layers/water/style.json @@ -0,0 +1,54 @@ +{ + "layers": [ + { + "id": "water_intermittent", + "type": "fill", + "source": "openmaptiles", + "source-layer": "water", + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": "rgba(172, 218, 251, 1)", + "fill-opacity": 0.85 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "intermittent", + 1 + ] + ], + "order": 17 + }, + { + "id": "water", + "type": "fill", + "source": "openmaptiles", + "source-layer": "water", + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": "#aad3df" + }, + "metadata": {}, + "filter": [ + "all", + [ + "!=", + "intermittent", + 1 + ], + [ + "!=", + "brunnel", + "tunnel" + ] + ], + "order": 18 + } + ] +} \ No newline at end of file diff --git a/layers/water/update_water.sql b/layers/water/update_water.sql new file mode 100644 index 0000000..b85ad24 --- /dev/null +++ b/layers/water/update_water.sql @@ -0,0 +1,142 @@ +-- Recreate ocean layer by union regular squares into larger polygons +-- etldoc: osm_ocean_polygon -> osm_ocean_polygon_union +CREATE TABLE IF NOT EXISTS osm_ocean_polygon_union AS + ( + SELECT (ST_Dump(ST_Union(ST_MakeValid(geometry)))).geom::geometry(Polygon, 3857) AS geometry + FROM osm_ocean_polygon + --for union select just full square (not big triangles) + WHERE ST_Area(geometry) > 100000000 AND + ST_NPoints(geometry) = 5 + UNION ALL + SELECT geometry + FROM osm_ocean_polygon + -- as 321 records have less then 5 coordinates (triangle) + -- bigger then 5 coordinates have squares with holes from island and coastline + WHERE ST_NPoints(geometry) <> 5 + ); + +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_union_geom_idx + ON osm_ocean_polygon_union + USING GIST (geometry); + +--Drop data from original table but keep table as `CREATE TABLE IF NOT EXISTS` still test if query is valid +TRUNCATE TABLE osm_ocean_polygon; + +-- This statement can be deleted after the water importer image stops creating this object as a table +DO +$$ + BEGIN + DROP TABLE IF EXISTS osm_ocean_polygon_gen_z11 CASCADE; + EXCEPTION + WHEN wrong_object_type THEN + END; +$$ LANGUAGE plpgsql; + +-- etldoc: osm_ocean_polygon_union -> osm_ocean_polygon_gen_z11 +DROP MATERIALIZED VIEW IF EXISTS osm_ocean_polygon_gen_z11 CASCADE; +CREATE MATERIALIZED VIEW osm_ocean_polygon_gen_z11 AS +( +SELECT ST_Simplify(geometry, ZRes(13)) AS geometry +FROM osm_ocean_polygon_union + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_gen_z11_idx ON osm_ocean_polygon_gen_z11 USING gist (geometry); + + +-- This statement can be deleted after the water importer image stops creating this object as a table +DO +$$ + BEGIN + DROP TABLE IF EXISTS osm_ocean_polygon_gen_z10 CASCADE; + EXCEPTION + WHEN wrong_object_type THEN + END; +$$ LANGUAGE plpgsql; + +-- etldoc: osm_ocean_polygon_gen_z11 -> osm_ocean_polygon_gen_z10 +DROP MATERIALIZED VIEW IF EXISTS osm_ocean_polygon_gen_z10 CASCADE; +CREATE MATERIALIZED VIEW osm_ocean_polygon_gen_z10 AS +( +SELECT ST_Simplify(geometry, ZRes(12)) AS geometry +FROM osm_ocean_polygon_gen_z11 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_gen_z10_idx ON osm_ocean_polygon_gen_z10 USING gist (geometry); + + +-- This statement can be deleted after the water importer image stops creating this object as a table +DO +$$ + BEGIN + DROP TABLE IF EXISTS osm_ocean_polygon_gen_z9 CASCADE; + EXCEPTION + WHEN wrong_object_type THEN + END; +$$ LANGUAGE plpgsql; + +-- etldoc: osm_ocean_polygon_gen_z10 -> osm_ocean_polygon_gen_z9 +DROP MATERIALIZED VIEW IF EXISTS osm_ocean_polygon_gen_z9 CASCADE; +CREATE MATERIALIZED VIEW osm_ocean_polygon_gen_z9 AS +( +SELECT ST_Simplify(geometry, ZRes(11)) AS geometry +FROM osm_ocean_polygon_gen_z10 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_gen_z9_idx ON osm_ocean_polygon_gen_z9 USING gist (geometry); + + +-- This statement can be deleted after the water importer image stops creating this object as a table +DO +$$ + BEGIN + DROP TABLE IF EXISTS osm_ocean_polygon_gen_z8 CASCADE; + EXCEPTION + WHEN wrong_object_type THEN + END; +$$ LANGUAGE plpgsql; + +-- etldoc: osm_ocean_polygon_gen_z9 -> osm_ocean_polygon_gen_z8 +DROP MATERIALIZED VIEW IF EXISTS osm_ocean_polygon_gen_z8 CASCADE; +CREATE MATERIALIZED VIEW osm_ocean_polygon_gen_z8 AS +( +SELECT ST_Simplify(geometry, ZRes(10)) AS geometry +FROM osm_ocean_polygon_gen_z9 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_gen_z8_idx ON osm_ocean_polygon_gen_z8 USING gist (geometry); + + +-- This statement can be deleted after the water importer image stops creating this object as a table +DO +$$ + BEGIN + DROP TABLE IF EXISTS osm_ocean_polygon_gen_z7 CASCADE; + EXCEPTION + WHEN wrong_object_type THEN + END; +$$ LANGUAGE plpgsql; + +-- etldoc: osm_ocean_polygon_gen_z8 -> osm_ocean_polygon_gen_z7 +DROP MATERIALIZED VIEW IF EXISTS osm_ocean_polygon_gen_z7 CASCADE; +CREATE MATERIALIZED VIEW osm_ocean_polygon_gen_z7 AS +( +SELECT ST_Simplify(geometry, ZRes(9)) AS geometry +FROM osm_ocean_polygon_gen_z8 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_gen_z7_idx ON osm_ocean_polygon_gen_z7 USING gist (geometry); + + +-- This statement can be deleted after the water importer image stops creating this object as a table +DO +$$ + BEGIN + DROP TABLE IF EXISTS osm_ocean_polygon_gen_z6 CASCADE; + EXCEPTION + WHEN wrong_object_type THEN + END; +$$ LANGUAGE plpgsql; + +-- etldoc: osm_ocean_polygon_gen_z7 -> osm_ocean_polygon_gen_z6 +DROP MATERIALIZED VIEW IF EXISTS osm_ocean_polygon_gen_z6 CASCADE; +CREATE MATERIALIZED VIEW osm_ocean_polygon_gen_z6 AS +( +SELECT ST_Simplify(geometry, ZRes(8)) AS geometry +FROM osm_ocean_polygon_gen_z7 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_gen_z6_idx ON osm_ocean_polygon_gen_z6 USING gist (geometry); diff --git a/layers/water/water.sql b/layers/water/water.sql new file mode 100644 index 0000000..9e94817 --- /dev/null +++ b/layers/water/water.sql @@ -0,0 +1,632 @@ +CREATE OR REPLACE FUNCTION water_class(waterway text, water text, leisure text) RETURNS text AS +$$ +SELECT CASE + WHEN water IN ('river', 'canal', 'stream', 'ditch', 'drain') THEN 'river' + %%FIELD_MAPPING: class %% + ELSE 'lake' + END; +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; + + +CREATE OR REPLACE FUNCTION waterway_brunnel(is_bridge bool, is_tunnel bool) RETURNS text AS +$$ +SELECT CASE + WHEN is_bridge THEN 'bridge' + WHEN is_tunnel THEN 'tunnel' + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; + + +-- Get matching osm id for natural earth id. +DROP MATERIALIZED VIEW IF EXISTS match_osm_ne_id CASCADE; +CREATE MATERIALIZED VIEW match_osm_ne_id AS +( +WITH name_match AS + ( + -- Distinct on keeps just the first occurence -> order by 'area_ratio DESC'. + SELECT DISTINCT ON (ne.ne_id) + ne.ne_id, + osm.osm_id, + (ST_Area(ST_Intersection(ne.geometry, osm.geometry))/ST_Area(ne.geometry)) AS area_ratio + FROM ne_10m_lakes ne, osm_water_polygon_gen_z6 osm + WHERE ne.name = osm.name + AND ST_Intersects(ne.geometry, osm.geometry) + ORDER BY ne_id, + area_ratio DESC + ), + -- Add lakes which are not match by name, but intersects. + -- Duplicity solves 'DISTICT ON' with 'area_ratio'. + geom_match AS + (SELECT DISTINCT ON (ne.ne_id) + ne.ne_id, + osm.osm_id, + (ST_Area(ST_Intersection(ne.geometry, osm.geometry))/ST_Area(ne.geometry)) AS area_ratio + FROM ne_10m_lakes ne, osm_water_polygon_gen_z6 osm + WHERE ST_Intersects(ne.geometry, osm.geometry) + AND ne.ne_id NOT IN + ( SELECT ne_id + FROM name_match + ) + ORDER BY ne_id, + area_ratio DESC + ) + +SELECT ne_id, + osm_id +FROM name_match + +UNION + +SELECT ne_id, + osm_id +FROM geom_match +); + +-- ne_10m_ocean +-- etldoc: ne_10m_ocean -> ne_10m_ocean_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_ocean_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_ocean_gen_z5 AS +( +SELECT NULL::integer AS id, + (ST_Dump(ST_Simplify(geometry, ZRes(7)))).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM ne_10m_ocean + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_ocean_gen_z5_idx ON ne_10m_ocean_gen_z5 USING gist (geometry); + +-- ne_10m_lakes +-- etldoc: ne_10m_lakes -> ne_10m_lakes_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_lakes_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_lakes_gen_z5 AS +( +SELECT COALESCE(osm.osm_id, ne_id) AS id, + -- Union fixing e.g. Lake Huron and Georgian Bay duplicity + (ST_Dump(ST_MakeValid(ST_Simplify(ST_Union(geometry), ZRes(7))))).geom AS geometry, + 'lake'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM ne_10m_lakes +LEFT JOIN match_osm_ne_id osm USING (ne_id) +GROUP BY COALESCE(osm.osm_id, ne_id), is_intermittent, is_bridge, is_tunnel + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_lakes_gen_z5_idx ON ne_10m_lakes_gen_z5 USING gist (geometry); + +-- etldoc: ne_10m_lakes_gen_z5 -> ne_10m_lakes_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_lakes_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_lakes_gen_z4 AS +( +SELECT id, + (ST_Dump(ST_MakeValid(ST_Simplify(geometry, ZRes(6))))).geom AS geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_10m_lakes_gen_z5 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_lakes_gen_z4_idx ON ne_10m_lakes_gen_z4 USING gist (geometry); + +-- ne_50m_ocean +-- etldoc: ne_50m_ocean -> ne_50m_ocean_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_ocean_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_ocean_gen_z4 AS +( +SELECT NULL::integer AS id, + (ST_Dump(ST_Simplify(geometry, ZRes(6)))).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM ne_50m_ocean + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_ocean_gen_z4_idx ON ne_50m_ocean_gen_z4 USING gist (geometry); + +-- etldoc: ne_50m_ocean_gen_z4 -> ne_50m_ocean_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_ocean_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_ocean_gen_z3 AS +( +SELECT id, + ST_Simplify(geometry, ZRes(5)) AS geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_ocean_gen_z4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_ocean_gen_z3_idx ON ne_50m_ocean_gen_z3 USING gist (geometry); + +-- etldoc: ne_50m_ocean_gen_z3 -> ne_50m_ocean_gen_z2 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_ocean_gen_z2 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_ocean_gen_z2 AS +( +SELECT id, + ST_Simplify(geometry, ZRes(4)) AS geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_ocean_gen_z3 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_ocean_gen_z2_idx ON ne_50m_ocean_gen_z2 USING gist (geometry); + +-- ne_50m_lakes +-- etldoc: ne_50m_lakes -> ne_50m_lakes_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_lakes_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_lakes_gen_z3 AS +( +SELECT COALESCE(osm.osm_id, ne_id) AS id, + (ST_Dump(ST_MakeValid(ST_Simplify(geometry, ZRes(5))))).geom AS geometry, + 'lake'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM ne_50m_lakes +LEFT JOIN match_osm_ne_id osm USING (ne_id) + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_lakes_gen_z3_idx ON ne_50m_lakes_gen_z3 USING gist (geometry); + +-- etldoc: ne_50m_lakes_gen_z3 -> ne_50m_lakes_gen_z2 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_lakes_gen_z2 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_lakes_gen_z2 AS +( +SELECT id, + (ST_Dump(ST_MakeValid(ST_Simplify(geometry, ZRes(4))))).geom AS geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_lakes_gen_z3 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_lakes_gen_z2_idx ON ne_50m_lakes_gen_z2 USING gist (geometry); + +--ne_110m_ocean +-- etldoc: ne_110m_ocean -> ne_110m_ocean_gen_z1 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_ocean_gen_z1 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_ocean_gen_z1 AS +( +SELECT NULL::integer AS id, + ST_Simplify(geometry, ZRes(3)) AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM ne_110m_ocean + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_ocean_gen_z1_idx ON ne_110m_ocean_gen_z1 USING gist (geometry); + +-- etldoc: ne_110m_ocean_gen_z1 -> ne_110m_ocean_gen_z0 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_ocean_gen_z0 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_ocean_gen_z0 AS +( +SELECT id, + ST_Simplify(geometry, ZRes(2)) AS geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_110m_ocean_gen_z1 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_ocean_gen_z0_idx ON ne_110m_ocean_gen_z0 USING gist (geometry); + + +-- ne_110m_lakes +-- etldoc: ne_110m_lakes -> ne_110m_lakes_gen_z1 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_lakes_gen_z1 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_lakes_gen_z1 AS +( +SELECT COALESCE(osm.osm_id, ne_id) AS id, + (ST_Dump(ST_Simplify(geometry, ZRes(3)))).geom AS geometry, + 'lake'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM ne_110m_lakes +LEFT JOIN match_osm_ne_id osm USING (ne_id) + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_lakes_gen_z1_idx ON ne_110m_lakes_gen_z1 USING gist (geometry); + +-- etldoc: ne_110m_lakes_gen_z1 -> ne_110m_lakes_gen_z0 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_lakes_gen_z0 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_lakes_gen_z0 AS +( +SELECT id, + (ST_Dump(ST_Simplify(geometry, ZRes(2)))).geom AS geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_110m_lakes_gen_z1 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_lakes_gen_z0_idx ON ne_110m_lakes_gen_z0 USING gist (geometry); + +DROP MATERIALIZED VIEW IF EXISTS water_z6; +DROP MATERIALIZED VIEW IF EXISTS water_z7; +DROP MATERIALIZED VIEW IF EXISTS water_z8; +DROP MATERIALIZED VIEW IF EXISTS water_z9; +DROP MATERIALIZED VIEW IF EXISTS water_z10; +DROP MATERIALIZED VIEW IF EXISTS water_z11; +DROP MATERIALIZED VIEW IF EXISTS water_z12; + +CREATE OR REPLACE VIEW water_z0 AS +( +-- etldoc: ne_110m_ocean_gen_z0 -> water_z0 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_110m_ocean_gen_z0 +UNION ALL +-- etldoc: ne_110m_lakes_gen_z0 -> water_z0 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_110m_lakes_gen_z0 + ); + +CREATE OR REPLACE VIEW water_z1 AS +( +-- etldoc: ne_110m_ocean_gen_z1 -> water_z1 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_110m_ocean_gen_z1 +UNION ALL +-- etldoc: ne_110m_lakes_gen_z1 -> water_z1 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_110m_lakes_gen_z1 + ); + +CREATE OR REPLACE VIEW water_z2 AS +( +-- etldoc: ne_50m_ocean_gen_z2 -> water_z2 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_ocean_gen_z2 +UNION ALL +-- etldoc: ne_50m_lakes_gen_z2 -> water_z2 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_lakes_gen_z2 + ); + +CREATE OR REPLACE VIEW water_z3 AS +( +-- etldoc: ne_50m_ocean_gen_z3 -> water_z3 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_ocean_gen_z3 +UNION ALL +-- etldoc: ne_50m_lakes_gen_z3 -> water_z3 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_lakes_gen_z3 + ); + +CREATE OR REPLACE VIEW water_z4 AS +( +-- etldoc: ne_50m_ocean_gen_z4 -> water_z4 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_ocean_gen_z4 +UNION ALL +-- etldoc: ne_10m_lakes_gen_z4 -> water_z4 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_10m_lakes_gen_z4 + ); + + +CREATE OR REPLACE VIEW water_z5 AS +( +-- etldoc: ne_10m_ocean_gen_z5 -> water_z5 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_10m_ocean_gen_z5 +UNION ALL +-- etldoc: ne_10m_lakes_gen_z5 -> water_z5 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_10m_lakes_gen_z5 + ); + +CREATE MATERIALIZED VIEW water_z6 AS +( +-- etldoc: osm_ocean_polygon_gen_z6 -> water_z6 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_gen_z6 +UNION ALL +-- etldoc: osm_water_polygon_gen_z6 -> water_z6 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_water_polygon_gen_z6 +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z6 USING gist(geometry); + +CREATE MATERIALIZED VIEW water_z7 AS +( +-- etldoc: osm_ocean_polygon_gen_z7 -> water_z7 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_gen_z7 +UNION ALL +-- etldoc: osm_water_polygon_gen_z7 -> water_z7 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_water_polygon_gen_z7 +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z7 USING gist(geometry); + +CREATE MATERIALIZED VIEW water_z8 AS +( +-- etldoc: osm_ocean_polygon_gen_z8 -> water_z8 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_gen_z8 +UNION ALL +-- etldoc: osm_water_polygon_gen_z8 -> water_z8 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_water_polygon_gen_z8 +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z8 USING gist(geometry); + +CREATE MATERIALIZED VIEW water_z9 AS +( +-- etldoc: osm_ocean_polygon_gen_z9 -> water_z9 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_gen_z9 +UNION ALL +-- etldoc: osm_water_polygon_gen_z9 -> water_z9 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_water_polygon_gen_z9 +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z9 USING gist(geometry); + +CREATE MATERIALIZED VIEW water_z10 AS +( +-- etldoc: osm_ocean_polygon_gen_z10 -> water_z10 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_gen_z10 +UNION ALL +-- etldoc: osm_water_polygon_gen_z10 -> water_z10 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_water_polygon_gen_z10 +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z10 USING gist(geometry); + +CREATE MATERIALIZED VIEW water_z11 AS +( +-- etldoc: osm_ocean_polygon_gen_z11 -> water_z11 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_gen_z11 +UNION ALL +-- etldoc: osm_water_polygon_gen_z11 -> water_z11 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_water_polygon_gen_z11 +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z11 USING gist(geometry); + +CREATE MATERIALIZED VIEW water_z12 AS +( +-- etldoc: osm_ocean_polygon_union -> water_z12 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_union +UNION ALL +-- etldoc: osm_water_polygon -> water_z12 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + is_bridge, + is_tunnel +FROM osm_water_polygon +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z12 USING gist(geometry); + +-- etldoc: layer_water [shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_water | z0|z1|z2|z3 | z4|z5|z6|z7| z8 | z9 | z10 | z11 | z12+" ] ; + +CREATE OR REPLACE FUNCTION layer_water(bbox geometry, zoom_level int) + RETURNS TABLE + ( + id bigint, + geometry geometry, + class text, + brunnel text, + intermittent int + ) +AS +$$ +SELECT id, + geometry, + class::text, + waterway_brunnel(is_bridge, is_tunnel) AS brunnel, + is_intermittent::int AS intermittent +FROM ( + -- etldoc: water_z0 -> layer_water:z0 + SELECT * + FROM water_z0 + WHERE zoom_level = 0 + UNION ALL + -- etldoc: water_z1 -> layer_water:z1 + SELECT * + FROM water_z1 + WHERE zoom_level = 1 + UNION ALL + -- etldoc: water_z2 -> layer_water:z2 + SELECT * + FROM water_z2 + WHERE zoom_level = 2 + UNION ALL + -- etldoc: water_z3 -> layer_water:z3 + SELECT * + FROM water_z3 + WHERE zoom_level = 3 + UNION ALL + -- etldoc: water_z4 -> layer_water:z4 + SELECT * + FROM water_z4 + WHERE zoom_level = 4 + UNION ALL + -- etldoc: water_z5 -> layer_water:z5 + SELECT * + FROM water_z5 + WHERE zoom_level = 5 + UNION ALL + -- etldoc: water_z6 -> layer_water:z6 + SELECT * + FROM water_z6 + WHERE zoom_level = 6 + UNION ALL + -- etldoc: water_z7 -> layer_water:z7 + SELECT * + FROM water_z7 + WHERE zoom_level = 7 + UNION ALL + -- etldoc: water_z8 -> layer_water:z8 + SELECT * + FROM water_z8 + WHERE zoom_level = 8 + UNION ALL + -- etldoc: water_z9 -> layer_water:z9 + SELECT * + FROM water_z9 + WHERE zoom_level = 9 + UNION ALL + -- etldoc: water_z10 -> layer_water:z10 + SELECT * + FROM water_z10 + WHERE zoom_level = 10 + UNION ALL + -- etldoc: water_z11 -> layer_water:z11 + SELECT * + FROM water_z11 + WHERE zoom_level = 11 + UNION ALL + -- etldoc: water_z12 -> layer_water:z12 + SELECT * + FROM water_z12 + WHERE zoom_level >= 12 + ) AS zoom_levels +WHERE geometry && bbox; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/water/water.yaml b/layers/water/water.yaml new file mode 100644 index 0000000..2ef0eda --- /dev/null +++ b/layers/water/water.yaml @@ -0,0 +1,66 @@ +layer: + id: "water" + requires: + tables: + - ne_10m_lakes + - ne_10m_ocean + - ne_110m_lakes + - ne_110m_ocean + - ne_50m_lakes + - ne_50m_ocean + - osm_ocean_polygon + description: | + Water polygons representing oceans and lakes. Covered watered areas are excluded (`covered=yes`). + On low zoom levels all water originates from Natural Earth. To get a more correct display of the south pole you should also + style the covering ice shelves over the water. + On higher zoom levels water polygons from [OpenStreetMapData](http://osmdata.openstreetmap.de/) are used. + The polygons are split into many smaller polygons to improve rendering performance. + This however can lead to less rendering options in clients since these boundaries show up. So you might not be + able to use border styling for ocean water features. + fields: + id: + description: | + From zoom 6 are taken OSM IDs. Up to zoom 5 there are used Natural Earth lakes, where are propagated the OSM IDs insted of Natural Earth IDs. + For smaller area then planet, NE lakes keep their Natural Earth IDs. + class: + description: | + All water polygons from [OpenStreetMapData](http://osmdata.openstreetmap.de/) have the class `ocean`. + The water-covered areas of flowing water bodies with the [`water=river`](http://wiki.openstreetmap.org/wiki/Tag:water=river), + [`water=canal`](http://wiki.openstreetmap.org/wiki/Tag:water=canal), + [`water=stream`](http://wiki.openstreetmap.org/wiki/Tag:water=stream), + [`water=ditch`](http://wiki.openstreetmap.org/wiki/Tag:water=ditch), or + [`water=drain`](http://wiki.openstreetmap.org/wiki/Tag:water=drain) tags are classified as river. Wet and dry docks + tagged [`waterway=dock`](http://wiki.openstreetmap.org/wiki/Tag:waterway=dock) are classified as a `dock`. + Various minor waterbodies are classified as a `pond`. + Swimming pools tagged [`leisure=swimming_pool`](https://wiki.openstreetmap.org/wiki/Tag:leisure=swimming_pool) are classified as a `swimming_pool` + All other water bodies are classified as `lake`. + values: + dock: + waterway: 'dock' + river: + water: ['river', 'stream', 'canal', 'ditch', 'drain'] + pond: + water: ['pond', 'basin', 'wastewater', 'salt_pond'] + lake: + ocean: + swimming_pool: + leisure: 'swimming_pool' + intermittent: + description: | + Mark with `1` if it is an [intermittent](http://wiki.openstreetmap.org/wiki/Key:intermittent) water polygon. + values: [0, 1] + brunnel: + description: | + Identifies the type of crossing as either a bridge or a tunnel. + values: + - bridge + - tunnel + buffer_size: 4 + datasource: + query: (SELECT id, geometry, class, intermittent, brunnel FROM layer_water(!bbox!, z(!scale_denominator!))) AS t +schema: + - ./update_water.sql + - ./water.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/water_name/README.md b/layers/water_name/README.md new file mode 100644 index 0000000..5ec0055 --- /dev/null +++ b/layers/water_name/README.md @@ -0,0 +1,10 @@ +## water_name + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#water_name** + +### Mapping Diagram +![Mapping diagram for water_name](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for water_name](etl_diagram.png?raw=true) diff --git a/layers/water_name/etl_diagram.png b/layers/water_name/etl_diagram.png new file mode 100644 index 0000000..89e1440 Binary files /dev/null and b/layers/water_name/etl_diagram.png differ diff --git a/layers/water_name/mapping.yaml b/layers/water_name/mapping.yaml new file mode 100644 index 0000000..8c5365d --- /dev/null +++ b/layers/water_name/mapping.yaml @@ -0,0 +1,42 @@ +tables: + # etldoc: imposm3 -> osm_marine_point + marine_point: + type: point + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - name: name + key: name + type: string + - name: name_en + key: name:en + type: string + - name: name_de + key: name:de + type: string + - name: tags + type: hstore_tags + - name: place + key: place + type: string + - name: natural + key: natural + type: string + - name: rank + key: rank + type: integer + - name: is_intermittent + key: intermittent + type: bool + filters: + require: + name: ["__any__"] + mapping: + place: + - ocean + - sea + natural: + - bay + - strait diff --git a/layers/water_name/mapping_diagram.png b/layers/water_name/mapping_diagram.png new file mode 100644 index 0000000..7a880ad Binary files /dev/null and b/layers/water_name/mapping_diagram.png differ diff --git a/layers/water_name/style.json b/layers/water_name/style.json new file mode 100644 index 0000000..283beab --- /dev/null +++ b/layers/water_name/style.json @@ -0,0 +1,74 @@ +{ + "layers": [ + { + "id": "water_name_line", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "water_name", + "minzoom": 0, + "layout": { + "text-font": [ + "Noto Sans Regular", + "Noto Sans Bold" + ], + "text-size": 12, + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-max-width": 5, + "symbol-placement": "line" + }, + "paint": { + "text-color": "#5d60be", + "text-halo-color": "rgba(255,255,255,0.7)", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ] + ], + "order": 152 + }, + { + "id": "water_name_point", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "water_name", + "minzoom": 16, + "maxzoom": 24, + "layout": { + "text-font": [ + "Noto Sans Regular" + ], + "text-size": 11, + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "text-max-width": 5 + }, + "paint": { + "text-color": "rgba(76, 125, 173, 1)", + "text-halo-color": "rgba(255,255,255,0)", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "Point" + ], + [ + "!=", + "class", + "ocean" + ] + ], + "order": 153 + } + ] +} \ No newline at end of file diff --git a/layers/water_name/update_marine_point.sql b/layers/water_name/update_marine_point.sql new file mode 100644 index 0000000..bd9293f --- /dev/null +++ b/layers/water_name/update_marine_point.sql @@ -0,0 +1,109 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_marine_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_marine_point; +DROP TRIGGER IF EXISTS trigger_refresh ON water_name_marine.updates; + +CREATE SCHEMA IF NOT EXISTS water_name_marine; + +CREATE TABLE IF NOT EXISTS water_name_marine.osm_ids +( + osm_id bigint PRIMARY KEY +); + +CREATE OR REPLACE FUNCTION update_osm_marine_point(full_update boolean) RETURNS void AS +$$ + -- etldoc: ne_10m_geography_marine_polys -> osm_marine_point + -- etldoc: osm_marine_point -> osm_marine_point + + WITH important_marine_point AS ( + SELECT osm.osm_id, ne.scalerank + FROM osm_marine_point AS osm + LEFT JOIN ne_10m_geography_marine_polys AS ne ON + ( + lower(trim(regexp_replace(ne.name, '\\s+', ' ', 'g'))) IN (lower(osm.name), lower(osm.tags->'name:en'), lower(osm.tags->'name:es')) + OR substring(lower(trim(regexp_replace(ne.name, '\\s+', ' ', 'g'))) FROM 1 FOR length(lower(osm.name))) = lower(osm.name) + ) + AND ST_DWithin(ne.geometry, osm.geometry, 50000) + ) + UPDATE osm_marine_point AS osm + SET "rank" = scalerank + FROM important_marine_point AS ne + WHERE (full_update OR osm.osm_id IN (SELECT osm_id FROM water_name_marine.osm_ids)) + AND osm.osm_id = ne.osm_id + AND "rank" IS DISTINCT FROM scalerank; + + UPDATE osm_marine_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM water_name_marine.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); + +$$ LANGUAGE SQL; + +SELECT update_osm_marine_point(true); + +CREATE INDEX IF NOT EXISTS osm_marine_point_rank_idx ON osm_marine_point ("rank"); + +-- Handle updates + +CREATE OR REPLACE FUNCTION water_name_marine.store() RETURNS trigger AS +$$ +BEGIN + INSERT INTO water_name_marine.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS water_name_marine.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION water_name_marine.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO water_name_marine.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION water_name_marine.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh water_name_marine rank'; + + -- Analyze tracking and source tables before performing update + ANALYZE water_name_marine.osm_ids; + ANALYZE osm_marine_point; + + PERFORM update_osm_marine_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM water_name_marine.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM water_name_marine.updates; + + RAISE LOG 'Refresh water_name_marine done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE + ON osm_marine_point + FOR EACH ROW +EXECUTE PROCEDURE water_name_marine.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE + ON osm_marine_point + FOR EACH STATEMENT +EXECUTE PROCEDURE water_name_marine.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON water_name_marine.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE water_name_marine.refresh(); diff --git a/layers/water_name/update_water_name.sql b/layers/water_name/update_water_name.sql new file mode 100644 index 0000000..b37ed76 --- /dev/null +++ b/layers/water_name/update_water_name.sql @@ -0,0 +1,213 @@ +DROP TRIGGER IF EXISTS trigger_store ON osm_water_polygon; +DROP TRIGGER IF EXISTS trigger_flag ON osm_water_polygon; +DROP TRIGGER IF EXISTS trigger_refresh ON water_name.updates; + +CREATE INDEX IF NOT EXISTS lake_centerline_osm_id_idx ON lake_centerline (osm_id); +CREATE INDEX IF NOT EXISTS osm_water_polygon_update_idx ON osm_water_polygon (name, ST_IsValid(geometry)) + WHERE name <> '' AND ST_IsValid(geometry);; + +CREATE OR REPLACE VIEW osm_water_lakeline_view AS +SELECT wp.osm_id, + ll.wkb_geometry AS geometry, + name, + name_en, + name_de, + update_tags(tags, ll.wkb_geometry) AS tags, + ST_Area(wp.geometry) AS area, + is_intermittent +FROM osm_water_polygon AS wp + INNER JOIN lake_centerline ll ON wp.osm_id = ll.osm_id +WHERE wp.name <> '' + AND ST_IsValid(wp.geometry); + +-- etldoc: osm_water_polygon -> osm_water_lakeline +-- etldoc: lake_centerline -> osm_water_lakeline +CREATE TABLE IF NOT EXISTS osm_water_lakeline AS +SELECT * +FROM osm_water_lakeline_view; +DO +$$ + BEGIN + ALTER TABLE osm_water_lakeline + ADD CONSTRAINT osm_water_lakeline_pk PRIMARY KEY (osm_id); + EXCEPTION + WHEN OTHERS THEN + RAISE NOTICE 'primary key osm_water_lakeline_pk already exists in osm_water_lakeline.'; + END; +$$; +CREATE INDEX IF NOT EXISTS osm_water_lakeline_geometry_idx ON osm_water_lakeline USING gist (geometry); + +-- etldoc: osm_water_polygon -> osm_water_point_view +-- etldoc: lake_centerline -> osm_water_point_view +CREATE OR REPLACE VIEW osm_water_point_view AS +SELECT wp.osm_id, + ST_PointOnSurface(wp.geometry) AS geometry, + wp.name, + wp.name_en, + wp.name_de, + CASE + WHEN "natural" = 'bay' THEN 'bay' + WHEN place = 'sea' THEN 'sea' + ELSE 'lake' + END AS class, + update_tags(wp.tags, ST_PointOnSurface(wp.geometry)) AS tags, + -- Area of the feature in square meters + ST_Area(wp.geometry) AS area, + wp.is_intermittent +FROM osm_water_polygon AS wp + LEFT JOIN lake_centerline ll ON wp.osm_id = ll.osm_id +WHERE ll.osm_id IS NULL + AND wp.name <> '' + AND ST_IsValid(wp.geometry); + +-- etldoc: osm_water_point_view -> osm_water_point_earth_view +CREATE OR REPLACE VIEW osm_water_point_earth_view AS +SELECT osm_id, + geometry, + name, + name_en, + name_de, + class, + tags, + -- Percentage of the earth's surface covered by this feature (approximately) + -- The constant below is 111,842^2 * 180 * 180, where 111,842 is the length of one degree of latitude at the equator in meters. + area / (405279708033600 * COS(ST_Y(ST_Transform(geometry,4326))*PI()/180)) as earth_area, + is_intermittent +FROM osm_water_point_view; + +-- etldoc: osm_water_point_earth_view -> osm_water_point +CREATE TABLE IF NOT EXISTS osm_water_point AS +SELECT * +FROM osm_water_point_earth_view; +DO +$$ + BEGIN + ALTER TABLE osm_water_point + ADD CONSTRAINT osm_water_point_pk PRIMARY KEY (osm_id); + EXCEPTION + WHEN OTHERS THEN + RAISE NOTICE 'primary key osm_water_point_pk already exists in osm_water_point.'; + END; +$$; +CREATE INDEX IF NOT EXISTS osm_water_point_geometry_idx ON osm_water_point USING gist (geometry); + +-- Handle updates + +CREATE SCHEMA IF NOT EXISTS water_name; + +CREATE TABLE IF NOT EXISTS water_name.osm_ids +( + osm_id bigint, + is_old bool, + PRIMARY KEY (osm_id, is_old) +); + +CREATE OR REPLACE FUNCTION update_osm_water_name() RETURNS void AS $$ +BEGIN + DELETE FROM osm_water_lakeline + WHERE EXISTS( + SELECT NULL + FROM water_name.osm_ids + WHERE water_name.osm_ids.osm_id = osm_water_lakeline.osm_id + AND water_name.osm_ids.is_old IS TRUE + ); + + INSERT INTO osm_water_lakeline + SELECT * FROM osm_water_lakeline_view + WHERE EXISTS( + SELECT NULL + FROM water_name.osm_ids + WHERE water_name.osm_ids.osm_id = osm_water_lakeline_view.osm_id + AND water_name.osm_ids.is_old IS FALSE + ) ON CONFLICT (osm_id) DO UPDATE SET geometry = excluded.geometry, name = excluded.name, name_en = excluded.name_en, + name_de = excluded.name_de, tags = excluded.tags, area = excluded.area, + is_intermittent = excluded.is_intermittent; + + DELETE FROM osm_water_point + WHERE EXISTS( + SELECT NULL + FROM water_name.osm_ids + WHERE water_name.osm_ids.osm_id = osm_water_point.osm_id + AND water_name.osm_ids.is_old IS TRUE + ); + + INSERT INTO osm_water_point + SELECT * FROM osm_water_point_earth_view + WHERE EXISTS( + SELECT NULL + FROM water_name.osm_ids + WHERE water_name.osm_ids.osm_id = osm_water_point_earth_view.osm_id + AND water_name.osm_ids.is_old IS FALSE + ) ON CONFLICT (osm_id) DO UPDATE SET geometry = excluded.geometry, name = excluded.name, name_en = excluded.name_en, + name_de = excluded.name_de, class = excluded.class, tags = excluded.tags, + earth_area = excluded.earth_area, is_intermittent = excluded.is_intermittent; + +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION water_name.store() RETURNS trigger AS $$ +BEGIN + IF (tg_op = 'DELETE') THEN + INSERT INTO water_name.osm_ids (osm_id, is_old) VALUES (OLD.osm_id, TRUE) ON CONFLICT (osm_id, is_old) DO NOTHING; + ELSE + INSERT INTO water_name.osm_ids (osm_id, is_old) VALUES (NEW.osm_id, FALSE) ON CONFLICT (osm_id, is_old) DO NOTHING; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS water_name.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION water_name.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO water_name.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION water_name.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh water_name'; + + -- Analyze tracking and source tables before performing update + ANALYZE water_name.osm_ids; + ANALYZE osm_water_lakeline; + ANALYZE osm_water_point; + + PERFORM update_osm_water_name(); + -- noinspection SqlWithoutWhere + DELETE FROM water_name.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM water_name.updates; + + RAISE LOG 'Refresh water_name done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_water_polygon + FOR EACH ROW +EXECUTE PROCEDURE water_name.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE + ON osm_water_polygon + FOR EACH STATEMENT +EXECUTE PROCEDURE water_name.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON water_name.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE water_name.refresh(); diff --git a/layers/water_name/water_name.sql b/layers/water_name/water_name.sql new file mode 100644 index 0000000..748b390 --- /dev/null +++ b/layers/water_name/water_name.sql @@ -0,0 +1,81 @@ +-- etldoc: layer_water_name[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_water_name | z0_8 | z9_13 | z14+" ] ; + +CREATE OR REPLACE FUNCTION layer_water_name(bbox geometry, zoom_level integer) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + name text, + name_en text, + name_de text, + tags hstore, + class text, + intermittent int + ) +AS +$$ +SELECT + -- etldoc: osm_water_lakeline -> layer_water_name:z9_13 + -- etldoc: osm_water_lakeline -> layer_water_name:z14_ + CASE + WHEN osm_id < 0 THEN -osm_id * 10 + 4 + ELSE osm_id * 10 + 1 + END AS osm_id_hash, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + 'lake'::text AS class, + is_intermittent::int AS intermittent +FROM osm_water_lakeline +WHERE geometry && bbox + AND ((zoom_level BETWEEN 3 AND 13 AND LineLabel(zoom_level, NULLIF(name, ''), geometry)) + OR (zoom_level >= 14)) +UNION ALL +SELECT + -- etldoc: osm_water_point -> layer_water_name:z9_13 + -- etldoc: osm_water_point -> layer_water_name:z14_ + CASE + WHEN osm_id < 0 THEN -osm_id * 10 + 4 + ELSE osm_id * 10 + 1 + END AS osm_id_hash, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + class, + is_intermittent::int AS intermittent +FROM osm_water_point +WHERE geometry && bbox + AND ( + -- Show a label if a water feature covers at least 1/4 of a tile or z14+ + (tags->'place' IN ('sea', 'ocean') AND POWER(4,zoom_level) * earth_area > 0.25) + OR (zoom_level BETWEEN 3 AND 13 AND POWER(4,zoom_level) * earth_area > 0.25) + OR (zoom_level >= 14) + ) +UNION ALL +SELECT + -- etldoc: osm_marine_point -> layer_water_name:z0_8 + -- etldoc: osm_marine_point -> layer_water_name:z9_13 + -- etldoc: osm_marine_point -> layer_water_name:z14_ + osm_id * 10 AS osm_id_hash, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + COALESCE(NULLIF("natural",''), "place") AS class, + is_intermittent::int AS intermittent +FROM osm_marine_point +WHERE geometry && bbox + AND CASE + WHEN place = 'ocean' THEN TRUE + WHEN zoom_level >= "rank" AND "rank" IS NOT NULL THEN TRUE + WHEN "natural" = 'bay' THEN zoom_level >= 13 + ELSE zoom_level >= 8 END; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/water_name/water_name.yaml b/layers/water_name/water_name.yaml new file mode 100644 index 0000000..4162dd3 --- /dev/null +++ b/layers/water_name/water_name.yaml @@ -0,0 +1,44 @@ +layer: + id: "water_name" + requires: + tables: + - ne_10m_geography_marine_polys + - lake_centerline + description: | + Lake center lines for labelling lake bodies. + This is based of the [osm-lakelines](https://github.com/openmaptiles/osm-lakelines) project + which derives nice centerlines from OSM water bodies. Only the most important lakes contain labels. + fields: + name: The OSM [`name`](http://wiki.openstreetmap.org/wiki/Key:name) value of the water body. Language-specific values are in `name:xx`. + name_en: English name `name:en` if available, otherwise `name`. This is deprecated and will be removed in a future release in favor of `name:en`. + name_de: German name `name:de` if available, otherwise `name` or `name:en`. This is deprecated and will be removed in a future release in favor of `name:de`. + class: + description: | + Distinguish between `lake`, `ocean`, `bay`, `strait`, and `sea`. + values: + - lake + - bay + - strait + - sea + - ocean + intermittent: + description: | + Mark with `1` if it is an [intermittent](http://wiki.openstreetmap.org/wiki/Key:intermittent) lake. + values: [0, 1] + buffer_size: 256 + srs: +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over + datasource: + geometry_field: geometry + key_field: osm_id + key_field_as_attribute: no + srid: 900913 + query: (SELECT osm_id, geometry, name, name_en, name_de, {name_languages}, class, intermittent FROM layer_water_name(!bbox!, z(!scale_denominator!))) AS t +schema: + - ./update_marine_point.sql + - ./update_water_name.sql + - ./water_name.sql +datasources: + - type: imposm3 + mapping_file: ../water/mapping.yaml + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/waterway/README.md b/layers/waterway/README.md new file mode 100644 index 0000000..eaa40c5 --- /dev/null +++ b/layers/waterway/README.md @@ -0,0 +1,10 @@ +## waterway + +### Docs +Read the layer documentation at **http://openmaptiles.org/schema#waterway** + +### Mapping Diagram +![Mapping diagram for waterway](mapping_diagram.png?raw=true) + +### ETL diagram +![ETL diagram for waterway](etl_diagram.png?raw=true) diff --git a/layers/waterway/etl_diagram.png b/layers/waterway/etl_diagram.png new file mode 100644 index 0000000..e88b513 Binary files /dev/null and b/layers/waterway/etl_diagram.png differ diff --git a/layers/waterway/mapping.yaml b/layers/waterway/mapping.yaml new file mode 100644 index 0000000..cecd114 --- /dev/null +++ b/layers/waterway/mapping.yaml @@ -0,0 +1,67 @@ +tunnel_field: &tunnel + key: tunnel + name: is_tunnel + type: bool +bridge_field: &bridge + key: bridge + name: is_bridge + type: bool + +tables: + # etldoc: imposm3 -> osm_waterway_linestring + waterway_linestring: + type: linestring + columns: + - name: osm_id + type: id + - name: geometry + type: geometry + - name: waterway + key: waterway + type: string + - key: name + name: name + type: string + - name: name_en + key: name:en + type: string + - name: name_de + key: name:de + type: string + - name: tags + type: hstore_tags + - *tunnel + - *bridge + - name: is_intermittent + key: intermittent + type: bool + mapping: + waterway: + - stream + - river + - canal + - drain + - ditch + + # etldoc: imposm3 -> osm_waterway_relation + waterway_relation: + type: relation_member + columns: + - name: osm_id + type: id + - name: member + type: member_id + - name: role + type: member_role + - name: geometry + type: geometry + - key: waterway + name: waterway + type: string + - key: name + name: name + type: string + - name: tags + type: hstore_tags + mapping: + waterway: [river] diff --git a/layers/waterway/mapping_diagram.png b/layers/waterway/mapping_diagram.png new file mode 100644 index 0000000..45b61fe Binary files /dev/null and b/layers/waterway/mapping_diagram.png differ diff --git a/layers/waterway/style.json b/layers/waterway/style.json new file mode 100644 index 0000000..67218da --- /dev/null +++ b/layers/waterway/style.json @@ -0,0 +1,373 @@ +{ + "layers": [ + { + "id": "waterway_tunnel", + "type": "line", + "source": "openmaptiles", + "source-layer": "waterway", + "minzoom": 14, + "layout": { + "line-cap": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#a0c8f0", + "line-width": { + "base": 1.3, + "stops": [ + [ + 13, + 0.5 + ], + [ + 20, + 6 + ] + ] + }, + "line-dasharray": [ + 2, + 4 + ] + }, + "filter": [ + "all", + [ + "==", + "brunnel", + "tunnel" + ] + ], + "order": 12 + }, + { + "id": "waterway_river", + "type": "line", + "source": "openmaptiles", + "source-layer": "waterway", + "layout": { + "line-cap": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#a0c8f0", + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 0.5 + ], + [ + 20, + 6 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "river" + ], + [ + "!=", + "brunnel", + "tunnel" + ], + [ + "!=", + "intermittent", + 1 + ] + ], + "order": 13 + }, + { + "id": "waterway_river_intermittent", + "type": "line", + "source": "openmaptiles", + "source-layer": "waterway", + "layout": { + "line-cap": "round" + }, + "paint": { + "line-color": "#a0c8f0", + "line-width": { + "base": 1.2, + "stops": [ + [ + 11, + 0.5 + ], + [ + 20, + 6 + ] + ] + }, + "line-dasharray": [ + 3, + 2 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "class", + "river" + ], + [ + "!=", + "brunnel", + "tunnel" + ], + [ + "==", + "intermittent", + 1 + ] + ], + "order": 14 + }, + { + "id": "waterway_other", + "type": "line", + "source": "openmaptiles", + "source-layer": "waterway", + "layout": { + "line-cap": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#a0c8f0", + "line-width": { + "base": 1.3, + "stops": [ + [ + 13, + 0.5 + ], + [ + 20, + 6 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "!=", + "class", + "river" + ], + [ + "!=", + "brunnel", + "tunnel" + ], + [ + "!=", + "intermittent", + 1 + ] + ], + "order": 15 + }, + { + "id": "waterway_other_intermittent", + "type": "line", + "source": "openmaptiles", + "source-layer": "waterway", + "layout": { + "line-cap": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#a0c8f0", + "line-width": { + "base": 1.3, + "stops": [ + [ + 13, + 0.5 + ], + [ + 20, + 6 + ] + ] + }, + "line-dasharray": [ + 4, + 3 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "!=", + "class", + "river" + ], + [ + "!=", + "brunnel", + "tunnel" + ], + [ + "==", + "intermittent", + 1 + ] + ], + "order": 16 + }, + { + "id": "waterway-bridge-case", + "type": "line", + "source": "openmaptiles", + "source-layer": "waterway", + "layout": { + "line-cap": "butt", + "line-join": "miter" + }, + "paint": { + "line-color": "#bbbbbb", + "line-width": { + "base": 1.6, + "stops": [ + [ + 12, + 0.5 + ], + [ + 20, + 5 + ] + ] + }, + "line-gap-width": { + "base": 1.3, + "stops": [ + [ + 13, + 0.5 + ], + [ + 20, + 6 + ] + ] + } + }, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "brunnel", + "bridge" + ] + ], + "order": 111 + }, + { + "id": "waterway-bridge", + "type": "line", + "source": "openmaptiles", + "source-layer": "waterway", + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "rgba(134, 204, 250, 1)", + "line-width": { + "base": 1.3, + "stops": [ + [ + 13, + 0.5 + ], + [ + 20, + 6 + ] + ] + } + }, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "brunnel", + "bridge" + ] + ], + "order": 112 + }, + { + "id": "water_way_name", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "waterway", + "minzoom": 0, + "layout": { + "text-font": [ + "Noto Sans Regular", + "Noto Sans Bold" + ], + "text-size": { + "stops": [ + [ + 11, + 10 + ], + [ + 13, + 12 + ] + ] + }, + "text-field": "{name:latin}\n{name:nonlatin}", + "visibility": "visible", + "symbol-spacing": 400, + "text-max-width": 5, + "symbol-placement": "line" + }, + "paint": { + "text-color": "#4d80b3", + "text-halo-color": "rgba(255, 255, 255, 0.8)", + "text-halo-width": 1 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ] + ], + "order": 151 + } + ] +} \ No newline at end of file diff --git a/layers/waterway/update_important_waterway.sql b/layers/waterway/update_important_waterway.sql new file mode 100644 index 0000000..aa0105e --- /dev/null +++ b/layers/waterway/update_important_waterway.sql @@ -0,0 +1,571 @@ +DROP TRIGGER IF EXISTS trigger_important_waterway_linestring_store ON osm_important_waterway_linestring; +DROP TRIGGER IF EXISTS trigger_store ON osm_waterway_linestring; +DROP TRIGGER IF EXISTS trigger_flag ON osm_waterway_linestring; +DROP TRIGGER IF EXISTS trigger_refresh ON waterway_important.updates; + +-- We merge the waterways by name like the highways +-- This helps to drop not important rivers (since they do not have a name) +-- and also makes it possible to filter out too short rivers + +-- Index for filling and updating osm_important_waterway_linestring table +CREATE UNIQUE INDEX IF NOT EXISTS osm_waterway_linestring_waterway_partial_idx + ON osm_waterway_linestring (osm_id) + WHERE name <> '' + AND waterway = 'river' + AND ST_IsValid(geometry); + +-- Analyze created index +ANALYZE osm_waterway_linestring; + +CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring ( + id SERIAL, + geometry geometry('LineString'), + source_ids bigint[], + name varchar, + name_en varchar, + name_de varchar, + tags hstore +); + +ALTER TABLE osm_important_waterway_linestring ADD COLUMN IF NOT EXISTS source_ids bigint[]; + +-- Create osm_important_waterway_linestring_gen_z11 as a copy of osm_important_waterway_linestring but drop the +-- "source_ids" column. This can be done because z10 and z9 tables are only simplified and not merged, therefore +-- relations to sources are direct via the id column. +CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring_gen_z11 +(LIKE osm_important_waterway_linestring); +ALTER TABLE osm_important_waterway_linestring_gen_z11 DROP COLUMN IF EXISTS source_ids; + +-- Create osm_important_waterway_linestring_gen_z10 as a copy of osm_important_waterway_linestring_gen_z11 +CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring_gen_z10 +(LIKE osm_important_waterway_linestring_gen_z11); + +-- Create osm_important_waterway_linestring_gen_z9 as a copy of osm_important_waterway_linestring_gen_z10 +CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring_gen_z9 +(LIKE osm_important_waterway_linestring_gen_z10); + +-- Create OneToMany-Relation-Table storing relations of a Merged-LineString in table +-- osm_important_waterway_linestring to Source-LineStrings from table osm_waterway_linestring +CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring_source_ids( + id int, + source_id bigint, + PRIMARY KEY (id, source_id) +); + +-- Ensure tables are emtpy if they haven't been created +TRUNCATE osm_important_waterway_linestring; +TRUNCATE osm_important_waterway_linestring_source_ids; + +-- etldoc: osm_waterway_linestring -> osm_important_waterway_linestring +-- Merge LineStrings from osm_waterway_linestring by grouping them and creating intersecting +-- clusters of each group via ST_ClusterDBSCAN +INSERT INTO osm_important_waterway_linestring (geometry, source_ids, name, name_en, name_de, tags) +SELECT (ST_Dump(ST_LineMerge(ST_Union(geometry)))).geom AS geometry, + -- We use St_Union instead of St_Collect to ensure no overlapping points exist within the geometries + -- to merge. https://postgis.net/docs/ST_Union.html + -- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString + -- if more than two LineStrings form an intersection or no intersection could be found. + -- https://postgis.net/docs/ST_LineMerge.html + -- In order to not end up with a mixture of LineStrings and MultiLineStrings we dump eventual + -- MultiLineStrings via ST_Dump. https://postgis.net/docs/ST_Dump.html + array_agg(osm_id) as source_ids, + name, + name_en, + name_de, + slice_language_tags(tags) AS tags +FROM ( + SELECT *, + -- Get intersecting clusters by setting minimum distance to 0 and minimum intersecting points to 1. + -- https://postgis.net/docs/ST_ClusterDBSCAN.html + ST_ClusterDBSCAN(geometry, 0, 1) OVER ( + PARTITION BY name, name_en, name_de, slice_language_tags(tags) + ) AS cluster, + -- ST_ClusterDBSCAN returns an increasing integer as the cluster-ids within each partition starting at 0. + -- This leads to clusters having the same ID across multiple partitions therefore we generate a + -- Cluster-Group-ID by utilizing the DENSE_RANK function sorted over the partition columns. + DENSE_RANK() OVER (ORDER BY name, name_en, name_de, slice_language_tags(tags)) as cluster_group + FROM osm_waterway_linestring + WHERE name <> '' AND waterway = 'river' AND ST_IsValid(geometry) +) q +GROUP BY cluster_group, cluster, name, name_en, name_de, slice_language_tags(tags); + +-- Geometry Index +CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_geometry_idx + ON osm_important_waterway_linestring USING gist (geometry); + +-- Create Primary-Keys for osm_important_waterway_linestring and osm_important_waterway_linestring_gen_z11/z10/z9 tables +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_important_waterway_linestring' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_important_waterway_linestring ADD PRIMARY KEY (id); + END IF; + + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_important_waterway_linestring_gen_z11' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_important_waterway_linestring_gen_z11 ADD PRIMARY KEY (id); + END IF; + + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_important_waterway_linestring_gen_z10' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_important_waterway_linestring_gen_z10 ADD PRIMARY KEY (id); + END IF; + + IF NOT EXISTS ( + SELECT constraint_name + FROM information_schema.table_constraints + WHERE table_name = 'osm_important_waterway_linestring_gen_z9' AND constraint_type = 'PRIMARY KEY' + ) THEN + ALTER TABLE osm_important_waterway_linestring_gen_z9 ADD PRIMARY KEY (id); + END IF; +END; +$$ LANGUAGE plpgsql; + +-- Index for storing OSM-IDs of Source-LineStrings +CREATE UNIQUE INDEX IF NOT EXISTS osm_waterway_linestring_osm_id_idx ON osm_waterway_linestring ("osm_id"); + +-- Indexes which can be utilized during full-update for queries originating from +-- insert_important_waterway_linestring_gen() function +CREATE UNIQUE INDEX IF NOT EXISTS osm_important_waterway_linestring_update_idx + ON osm_important_waterway_linestring (id) WHERE ST_Length(geometry) > 1000; + +-- Analyze populated table with indexes +ANALYZE osm_important_waterway_linestring; + +-- Store OSM-IDs of Source-LineStrings by intersecting Merged-LineStrings with their sources. This required because +-- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString if +-- more than two LineStrings form an intersection or no intersection could be found. +-- Execute after indexes have been created on osm_highway_linestring_gen_z11 to improve performance +INSERT INTO osm_important_waterway_linestring_source_ids (id, source_id) +SELECT m.id, m.source_id +FROM ( + SELECT id, unnest(source_ids) AS source_id, geometry + FROM osm_important_waterway_linestring +) m +JOIN osm_waterway_linestring s ON (m.source_id = s.osm_id) +WHERE ST_Intersects(s.geometry, m.geometry) +ON CONFLICT (id, source_id) DO NOTHING; + +-- Drop temporary Merged-LineString to Source-LineStrings-ID column +ALTER TABLE osm_important_waterway_linestring DROP COLUMN IF EXISTS source_ids; + +CREATE SCHEMA IF NOT EXISTS waterway_important; + +CREATE TABLE IF NOT EXISTS waterway_important.changes_z9_z10_z11 +( + is_old boolean, + id integer, + PRIMARY KEY (is_old, id) +); + +CREATE OR REPLACE FUNCTION insert_important_waterway_linestring_gen(full_update bool) RETURNS void AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh waterway z9 z10 z11'; + + -- Analyze tracking and source tables before performing update + ANALYZE waterway_important.changes_z9_z10_z11; + ANALYZE osm_important_waterway_linestring; + + -- Remove entries which have been deleted from source table + DELETE FROM osm_important_waterway_linestring_gen_z11 + USING waterway_important.changes_z9_z10_z11 + WHERE full_update IS TRUE OR ( + waterway_important.changes_z9_z10_z11.is_old IS TRUE AND + waterway_important.changes_z9_z10_z11.id = osm_important_waterway_linestring_gen_z11.id + ); + + -- etldoc: osm_important_waterway_linestring -> osm_important_waterway_linestring_gen_z11 + INSERT INTO osm_important_waterway_linestring_gen_z11 (geometry, id, name, name_en, name_de, tags) + SELECT ST_Simplify(geometry, ZRes(12)) AS geometry, + id, + name, + name_en, + name_de, + tags + FROM osm_important_waterway_linestring + WHERE ( + full_update OR + EXISTS( + SELECT NULL + FROM waterway_important.changes_z9_z10_z11 + WHERE waterway_important.changes_z9_z10_z11.is_old IS FALSE AND + waterway_important.changes_z9_z10_z11.id = osm_important_waterway_linestring.id + ) + ) AND ST_Length(geometry) > 1000 + ON CONFLICT (id) DO UPDATE SET geometry = excluded.geometry, name = excluded.name, name_en = excluded.name_en, + name_de = excluded.name_de, tags = excluded.tags; + + -- Analyze source table + ANALYZE osm_important_waterway_linestring_gen_z11; + + -- Remove entries which have been deleted from source table + DELETE FROM osm_important_waterway_linestring_gen_z10 + USING waterway_important.changes_z9_z10_z11 + WHERE full_update IS TRUE OR ( + waterway_important.changes_z9_z10_z11.is_old IS TRUE AND + waterway_important.changes_z9_z10_z11.id = osm_important_waterway_linestring_gen_z10.id + ); + + -- etldoc: osm_important_waterway_linestring_gen_z11 -> osm_important_waterway_linestring_gen_z10 + INSERT INTO osm_important_waterway_linestring_gen_z10 (geometry, id, name, name_en, name_de, tags) + SELECT ST_Simplify(geometry, ZRes(11)) AS geometry, + id, + name, + name_en, + name_de, + tags + FROM osm_important_waterway_linestring_gen_z11 + WHERE ( + full_update OR + EXISTS( + SELECT NULL + FROM waterway_important.changes_z9_z10_z11 + WHERE waterway_important.changes_z9_z10_z11.is_old IS FALSE AND + waterway_important.changes_z9_z10_z11.id = osm_important_waterway_linestring_gen_z11.id + ) + ) AND ST_Length(geometry) > 4000 + ON CONFLICT (id) DO UPDATE SET geometry = excluded.geometry, name = excluded.name, name_en = excluded.name_en, + name_de = excluded.name_de, tags = excluded.tags; + + -- Analyze source table + ANALYZE osm_important_waterway_linestring_gen_z10; + + -- Remove entries which have been deleted from source table + DELETE FROM osm_important_waterway_linestring_gen_z9 + USING waterway_important.changes_z9_z10_z11 + WHERE full_update IS TRUE OR ( + waterway_important.changes_z9_z10_z11.is_old IS TRUE AND + waterway_important.changes_z9_z10_z11.id = osm_important_waterway_linestring_gen_z9.id + ); + + -- etldoc: osm_important_waterway_linestring_gen_z10 -> osm_important_waterway_linestring_gen_z9 + INSERT INTO osm_important_waterway_linestring_gen_z9 (geometry, id, name, name_en, name_de, tags) + SELECT ST_Simplify(geometry, ZRes(10)) AS geometry, + id, + name, + name_en, + name_de, + tags + FROM osm_important_waterway_linestring_gen_z10 + WHERE ( + full_update OR + EXISTS( + SELECT NULL + FROM waterway_important.changes_z9_z10_z11 + WHERE waterway_important.changes_z9_z10_z11.is_old IS FALSE AND + waterway_important.changes_z9_z10_z11.id = osm_important_waterway_linestring_gen_z10.id + ) + ) AND ST_Length(geometry) > 8000 + ON CONFLICT (id) DO UPDATE SET geometry = excluded.geometry, name = excluded.name, name_en = excluded.name_en, + name_de = excluded.name_de, tags = excluded.tags; + + -- noinspection SqlWithoutWhere + DELETE FROM waterway_important.changes_z9_z10_z11; + + RAISE LOG 'Refresh waterway z9 z10 z11 done in %', age(clock_timestamp(), t); +END; +$$ LANGUAGE plpgsql; + +-- Ensure tables are emtpy if they haven't been created +TRUNCATE osm_important_waterway_linestring_gen_z11; +TRUNCATE osm_important_waterway_linestring_gen_z10; +TRUNCATE osm_important_waterway_linestring_gen_z9; + +SELECT insert_important_waterway_linestring_gen(TRUE); + +-- Indexes for queries originating from insert_important_waterway_linestring_gen() function +CREATE UNIQUE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen_z11_update_idx + ON osm_important_waterway_linestring_gen_z11 (id) WHERE ST_Length(geometry) > 4000; +CREATE UNIQUE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen_z10_update_idx + ON osm_important_waterway_linestring_gen_z10 (id) WHERE ST_Length(geometry) > 8000; + +-- Geometry Indexes +CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen_z11_geometry_idx + ON osm_important_waterway_linestring_gen_z11 USING gist (geometry); +CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen_z10_geometry_idx + ON osm_important_waterway_linestring_gen_z10 USING gist (geometry); +CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen_z9_geometry_idx + ON osm_important_waterway_linestring_gen_z9 USING gist (geometry); + + +-- Handle updates on +-- -- osm_waterway_linestring -> osm_important_waterway_linestring +-- -- osm_important_waterway_linestring -> osm_important_waterway_linestring_gen_z11 +-- -- osm_important_waterway_linestring -> osm_important_waterway_linestring_gen_z10 +-- -- osm_important_waterway_linestring -> osm_important_waterway_linestring_gen_z9 + +CREATE OR REPLACE AGGREGATE array_cat_agg(anycompatiblearray) ( + SFUNC=array_cat, + STYPE=anycompatiblearray, + INITCOND = '{}' +); + +CREATE TABLE IF NOT EXISTS waterway_important.changes +( + osm_id bigint, + is_old boolean, + PRIMARY KEY (is_old, osm_id) +); + +-- Store IDs of changed elements from osm_waterway_linestring table. +CREATE OR REPLACE FUNCTION waterway_important.store() RETURNS trigger AS +$$ +BEGIN + IF (tg_op IN ('DELETE', 'UPDATE')) AND OLD.name <> '' AND OLD.waterway = 'river' THEN + INSERT INTO waterway_important.changes(is_old, osm_id) + VALUES (TRUE, old.osm_id) ON CONFLICT DO NOTHING; + END IF; + IF (tg_op IN ('UPDATE', 'INSERT')) AND NEW.name <> '' AND NEW.waterway = 'river' THEN + INSERT INTO waterway_important.changes(is_old, osm_id) + VALUES (FALSE, new.osm_id) ON CONFLICT DO NOTHING; + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +-- Store IDs of changed elements from osm_important_waterway_linestring table. +CREATE OR REPLACE FUNCTION waterway_important.important_waterway_linestring_store() RETURNS trigger AS +$$ +BEGIN + IF (tg_op = 'UPDATE' OR tg_op = 'DELETE') THEN + INSERT INTO waterway_important.changes_z9_z10_z11 (is_old, id) VALUES (TRUE, old.id) ON CONFLICT DO NOTHING ; + END IF; + + IF (tg_op = 'UPDATE' OR tg_op = 'INSERT') THEN + INSERT INTO waterway_important.changes_z9_z10_z11 (is_old, id) VALUES (FALSE, new.id) ON CONFLICT DO NOTHING; + END IF; + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS waterway_important.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION waterway_important.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO waterway_important.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION waterway_important.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh waterway'; + + -- REFRESH osm_important_waterway_linestring + + -- Analyze tracking and source tables before performing update + ANALYZE waterway_important.changes; + ANALYZE osm_waterway_linestring; + + -- Fetch updated and deleted Merged-LineString from relation-table filtering for each Merged-LineString which + -- contains an updated Source-LineString. + -- Additionally attach a list of Source-LineString-IDs to each Merged-LineString in order to unnest them later. + CREATE TEMPORARY TABLE affected_merged_linestrings AS + SELECT m.id, array_agg(source_id) AS source_ids + FROM osm_important_waterway_linestring_source_ids m + WHERE EXISTS( + SELECT NULL + FROM waterway_important.changes c + WHERE c.is_old IS TRUE AND c.osm_id = m.source_id + ) + GROUP BY id; + + -- Analyze the created table to speed up subsequent queries + ANALYZE affected_merged_linestrings; + + -- Delete all Merged-LineStrings which contained an updated or deleted Source-LineString + DELETE + FROM osm_important_waterway_linestring m + USING affected_merged_linestrings + WHERE affected_merged_linestrings.id = m.id; + DELETE + FROM osm_important_waterway_linestring_source_ids m + USING affected_merged_linestrings + WHERE affected_merged_linestrings.id = m.id; + + -- Analyze the tables affected by the delete-query in order to speed up subsequent queries + ANALYZE osm_important_waterway_linestring; + ANALYZE osm_important_waterway_linestring_source_ids; + + -- Create a table containing all LineStrings which should be merged + CREATE TEMPORARY TABLE linestrings_to_merge AS + -- Add all Source-LineStrings affected by this update + SELECT osm_id, NULL::INTEGER AS id, NULL::BIGINT[] AS source_ids, geometry, name, name_en, name_de, + slice_language_tags(tags) as tags + -- Table containing the IDs of all Source-LineStrings affected by this update + FROM ( + -- Get Source-LineString-IDs of deleted or updated elements + SELECT unnest(affected_merged_linestrings.source_ids)::bigint AS source_id FROM affected_merged_linestrings + UNION + -- Get Source-LineString-IDs of inserted or updated elements + SELECT osm_id AS source_id FROM waterway_important.changes WHERE is_old IS FALSE + ORDER BY source_id + ) affected_source_linestrings + JOIN osm_waterway_linestring ON ( + affected_source_linestrings.source_id = osm_waterway_linestring.osm_id + ) + WHERE name <> '' AND waterway = 'river' AND ST_IsValid(geometry); + + -- Drop temporary tables early to save resources + DROP TABLE affected_merged_linestrings; + + -- Analyze the created table to speed up subsequent queries + ANALYZE linestrings_to_merge; + + -- Add all Merged-LineStrings intersecting with Source-LineStrings affected by this update + INSERT INTO linestrings_to_merge + SELECT NULL::BIGINT AS osm_id, m.id, + ARRAY( + SELECT s.source_id FROM osm_important_waterway_linestring_source_ids s WHERE s.id = m.id + )::BIGINT[] AS source_ids, + m.geometry, m.name, m.name_en, m.name_de, m.tags + FROM linestrings_to_merge + JOIN osm_important_waterway_linestring m ON (ST_Intersects(linestrings_to_merge.geometry, m.geometry)); + + -- Analyze the created table to speed up subsequent queries + ANALYZE linestrings_to_merge; + + -- Delete all Merged-LineStrings intersecting with Source-LineStrings affected by this update. + -- We can use the linestrings_to_merge table since Source-LineStrings affected by this update and present in the + -- table will have their ID-Column set to NULL by the previous query. + DELETE + FROM osm_important_waterway_linestring m + USING linestrings_to_merge + WHERE m.id = linestrings_to_merge.id; + DELETE + FROM osm_important_waterway_linestring_source_ids m + USING linestrings_to_merge + WHERE m.id = linestrings_to_merge.id; + + -- Create table containing all LineStrings to and create clusters of intersecting LineStrings partitioned by their + -- groups + CREATE TEMPORARY TABLE clustered_linestrings_to_merge AS + SELECT *, + -- Get intersecting clusters by setting minimum distance to 0 and minimum intersecting points to 1. + -- https://postgis.net/docs/ST_ClusterDBSCAN.html + ST_ClusterDBSCAN(geometry, 0, 1) OVER (PARTITION BY name, name_en, name_de, tags) AS cluster, + -- ST_ClusterDBSCAN returns an increasing integer as the cluster-ids within each partition starting at 0. + -- This leads to clusters having the same ID across multiple partitions therefore we generate a + -- Cluster-Group-ID by utilizing the DENSE_RANK function sorted over the partition columns. + DENSE_RANK() OVER (ORDER BY name, name_en, name_de, tags) as cluster_group + FROM linestrings_to_merge; + + -- Drop temporary tables early to save resources + DROP TABLE linestrings_to_merge; + + -- Create index on cluster columns and analyze the created table to speed up subsequent queries + CREATE INDEX ON clustered_linestrings_to_merge (cluster_group, cluster); + ANALYZE clustered_linestrings_to_merge; + + -- Create temporary Merged-LineString to Source-LineStrings-ID columns to store relations before they have been + -- intersected + ALTER TABLE osm_important_waterway_linestring ADD COLUMN IF NOT EXISTS new_source_ids BIGINT[]; + ALTER TABLE osm_important_waterway_linestring ADD COLUMN IF NOT EXISTS old_source_ids BIGINT[]; + + WITH inserted_linestrings AS ( + -- Merge LineStrings of each cluster and insert them + INSERT INTO osm_important_waterway_linestring (geometry, new_source_ids, old_source_ids, name, name_en, name_de, + tags) + SELECT (ST_Dump(ST_LineMerge(ST_Union(geometry)))).geom AS geometry, + -- We use St_Union instead of St_Collect to ensure no overlapping points exist within the geometries + -- to merge. https://postgis.net/docs/ST_Union.html + -- ST_LineMerge only merges across singular intersections and groups its output into a MultiLineString + -- if more than two LineStrings form an intersection or no intersection could be found. + -- https://postgis.net/docs/ST_LineMerge.html + -- In order to not end up with a mixture of LineStrings and MultiLineStrings we dump eventual + -- MultiLineStrings via ST_Dump. https://postgis.net/docs/ST_Dump.html + coalesce( array_agg(osm_id) FILTER (WHERE osm_id IS NOT NULL), '{}' )::BIGINT[] AS new_source_ids, + array_cat_agg(source_ids)::BIGINT[] as old_source_ids, + name, + name_en, + name_de, + tags + FROM clustered_linestrings_to_merge + GROUP BY cluster_group, cluster, name, name_en, name_de, tags + RETURNING id, new_source_ids, old_source_ids, geometry + ) + -- Store OSM-IDs of Source-LineStrings by intersecting Merged-LineStrings with their sources. + -- This is required because ST_LineMerge only merges across singular intersections and groups its output into a + -- MultiLineString if more than two LineStrings form an intersection or no intersection could be found. + INSERT INTO osm_important_waterway_linestring_source_ids (id, source_id) + SELECT m.id, source_id + FROM ( + SELECT id, source_id, geometry + FROM inserted_linestrings + CROSS JOIN LATERAL ( + SELECT DISTINCT all_source_ids.source_id + FROM unnest( + array_cat(inserted_linestrings.new_source_ids, inserted_linestrings.old_source_ids) + ) AS all_source_ids(source_id) + ) source_ids + ) m + JOIN osm_waterway_linestring s ON (m.source_id = s.osm_id) + WHERE ST_Intersects(s.geometry, m.geometry) + ON CONFLICT (id, source_id) DO NOTHING; + + -- Cleanup remaining table + DROP TABLE clustered_linestrings_to_merge; + + -- Drop temporary Merged-LineString to Source-LineStrings-ID columns + ALTER TABLE osm_important_waterway_linestring DROP COLUMN IF EXISTS new_source_ids; + ALTER TABLE osm_important_waterway_linestring DROP COLUMN IF EXISTS old_source_ids; + + -- noinspection SqlWithoutWhere + DELETE FROM waterway_important.changes; + -- noinspection SqlWithoutWhere + DELETE FROM waterway_important.updates; + + RAISE LOG 'Refresh waterway done in %', age(clock_timestamp(), t); + + -- Update z11, z10 and z9 tables + PERFORM insert_important_waterway_linestring_gen(FALSE); + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_important_waterway_linestring_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_important_waterway_linestring + FOR EACH ROW +EXECUTE PROCEDURE waterway_important.important_waterway_linestring_store(); + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_waterway_linestring + FOR EACH ROW +EXECUTE PROCEDURE waterway_important.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE + ON osm_waterway_linestring + FOR EACH STATEMENT +EXECUTE PROCEDURE waterway_important.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON waterway_important.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE waterway_important.refresh(); diff --git a/layers/waterway/update_waterway_linestring.sql b/layers/waterway/update_waterway_linestring.sql new file mode 100644 index 0000000..7516f08 --- /dev/null +++ b/layers/waterway/update_waterway_linestring.sql @@ -0,0 +1,29 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_waterway_linestring; +DROP TRIGGER IF EXISTS trigger_refresh ON osm_waterway_linestring; + +DO +$$ + BEGIN + UPDATE osm_waterway_linestring + SET tags = update_tags(tags, geometry); + END +$$; + + +-- Handle updates + +CREATE SCHEMA IF NOT EXISTS waterway_linestring; +CREATE OR REPLACE FUNCTION waterway_linestring.refresh() RETURNS trigger AS +$$ +BEGIN + -- RAISE NOTICE 'Refresh waterway_linestring %', NEW.osm_id; + NEW.tags = update_tags(NEW.tags, NEW.geometry); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_refresh + BEFORE INSERT OR UPDATE + ON osm_waterway_linestring + FOR EACH ROW +EXECUTE PROCEDURE waterway_linestring.refresh(); diff --git a/layers/waterway/waterway.sql b/layers/waterway/waterway.sql new file mode 100644 index 0000000..6b2f7f3 --- /dev/null +++ b/layers/waterway/waterway.sql @@ -0,0 +1,404 @@ +CREATE OR REPLACE FUNCTION waterway_brunnel(is_bridge bool, is_tunnel bool) RETURNS text AS +$$ +SELECT CASE + WHEN is_bridge THEN 'bridge' + WHEN is_tunnel THEN 'tunnel' + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; +-- ne_110m_rivers_lake_centerlines +-- etldoc: ne_110m_rivers_lake_centerlines -> ne_110m_rivers_lake_centerlines_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_rivers_lake_centerlines_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_rivers_lake_centerlines_gen_z3 AS +( +SELECT ST_Simplify(geometry, ZRes(5)) as geometry, + 'river'::text AS class, + NULL::text AS name, + NULL::text AS name_en, + NULL::text AS name_de, + NULL::hstore AS tags, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_intermittent +FROM ne_110m_rivers_lake_centerlines +WHERE featurecla = 'River' + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_rivers_lake_centerlines_gen_z3_idx ON ne_110m_rivers_lake_centerlines_gen_z3 USING gist (geometry); + +-- ne_50m_rivers_lake_centerlines +-- etldoc: ne_50m_rivers_lake_centerlines -> ne_50m_rivers_lake_centerlines_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_rivers_lake_centerlines_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_rivers_lake_centerlines_gen_z5 AS +( +SELECT ST_Simplify(geometry, ZRes(7)) as geometry, + 'river'::text AS class, + NULL::text AS name, + NULL::text AS name_en, + NULL::text AS name_de, + NULL::hstore AS tags, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_intermittent +FROM ne_50m_rivers_lake_centerlines +WHERE featurecla = 'River' + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_rivers_lake_centerlines_gen_z5_idx ON ne_50m_rivers_lake_centerlines_gen_z5 USING gist (geometry); + +-- etldoc: ne_50m_rivers_lake_centerlines_gen_z5 -> ne_50m_rivers_lake_centerlines_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_rivers_lake_centerlines_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_rivers_lake_centerlines_gen_z4 AS +( +SELECT ST_Simplify(geometry, ZRes(6)) as geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM ne_50m_rivers_lake_centerlines_gen_z5 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_rivers_lake_centerlines_gen_z4_idx ON ne_50m_rivers_lake_centerlines_gen_z4 USING gist (geometry); + +-- osm_waterway_relation +-- etldoc: osm_waterway_relation -> waterway_relation +DROP TABLE IF EXISTS waterway_relation CASCADE; +CREATE TABLE waterway_relation AS ( + SELECT ST_Union(geometry) AS geometry, + name, + slice_language_tags(tags) AS tags + FROM osm_waterway_relation + WHERE name <> '' + AND (role = 'main_stream' OR role = '') + AND ST_GeometryType(geometry) = 'ST_LineString' + AND ST_IsClosed(geometry) = FALSE + GROUP BY name, slice_language_tags(tags) +); +CREATE INDEX IF NOT EXISTS waterway_relation_geometry_idx ON waterway_relation USING gist (geometry); + +-- etldoc: waterway_relation -> waterway_relation_gen_z8 +DROP MATERIALIZED VIEW IF EXISTS waterway_relation_gen_z8 CASCADE; +CREATE MATERIALIZED VIEW waterway_relation_gen_z8 AS ( + SELECT ST_Simplify(geometry, ZRes(10)) AS geometry, + 'river'::text AS class, + name, + NULL::text AS name_en, + NULL::text AS name_de, + tags, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_intermittent + FROM waterway_relation + WHERE ST_Length(geometry) > 300000 +) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS waterway_relation_gen_z8_geometry_idx ON waterway_relation_gen_z8 USING gist (geometry); + +-- etldoc: waterway_relation_gen_z8 -> waterway_relation_gen_z7 +DROP MATERIALIZED VIEW IF EXISTS waterway_relation_gen_z7 CASCADE; +CREATE MATERIALIZED VIEW waterway_relation_gen_z7 AS ( + SELECT ST_Simplify(geometry, ZRes(9)) AS geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent + FROM waterway_relation_gen_z8 + WHERE ST_Length(geometry) > 400000 +) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS waterway_relation_gen_z7_geometry_idx ON waterway_relation_gen_z7 USING gist (geometry); + +-- etldoc: waterway_relation_gen_z7 -> waterway_relation_gen_z6 +DROP MATERIALIZED VIEW IF EXISTS waterway_relation_gen_z6 CASCADE; +CREATE MATERIALIZED VIEW waterway_relation_gen_z6 AS ( + SELECT ST_Simplify(geometry, ZRes(8)) AS geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent + FROM waterway_relation_gen_z7 + WHERE ST_Length(geometry) > 500000 +) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS waterway_relation_gen_z6_geometry_idx ON waterway_relation_gen_z6 USING gist (geometry); + + +-- etldoc: ne_110m_rivers_lake_centerlines_gen_z3 -> waterway_z3 +CREATE OR REPLACE VIEW waterway_z3 AS +( +SELECT geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM ne_110m_rivers_lake_centerlines_gen_z3 + ); + +-- etldoc: ne_50m_rivers_lake_centerlines_gen_z4 -> waterway_z4 +CREATE OR REPLACE VIEW waterway_z4 AS +( +SELECT geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM ne_50m_rivers_lake_centerlines_gen_z4 + ); + +-- etldoc: ne_50m_rivers_lake_centerlines_gen_z5 -> waterway_z5 +CREATE OR REPLACE VIEW waterway_z5 AS +( +SELECT geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM ne_50m_rivers_lake_centerlines_gen_z5 + ); + +-- etldoc: waterway_relation_gen_z6 -> waterway_z6 +CREATE OR REPLACE VIEW waterway_z6 AS +( +SELECT geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM waterway_relation_gen_z6 + ); + +-- etldoc: waterway_relation_gen_z7 -> waterway_z7 +CREATE OR REPLACE VIEW waterway_z7 AS +( +SELECT geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM waterway_relation_gen_z7 + ); + +-- etldoc: waterway_relation_gen_z8 -> waterway_z8 +CREATE OR REPLACE VIEW waterway_z8 AS +( +SELECT geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM waterway_relation_gen_z8 + ); + +-- etldoc: osm_important_waterway_linestring_gen_z9 -> waterway_z9 +CREATE OR REPLACE VIEW waterway_z9 AS +( +SELECT geometry, + 'river'::text AS class, + name, + name_en, + name_de, + tags, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_intermittent +FROM osm_important_waterway_linestring_gen_z9 + ); + +-- etldoc: osm_important_waterway_linestring_gen_z10 -> waterway_z10 +CREATE OR REPLACE VIEW waterway_z10 AS +( +SELECT geometry, + 'river'::text AS class, + name, + name_en, + name_de, + tags, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_intermittent +FROM osm_important_waterway_linestring_gen_z10 + ); + +-- etldoc:osm_important_waterway_linestring_gen_z11 -> waterway_z11 +CREATE OR REPLACE VIEW waterway_z11 AS +( +SELECT geometry, + 'river'::text AS class, + name, + name_en, + name_de, + tags, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_intermittent +FROM osm_important_waterway_linestring_gen_z11 + ); + +-- etldoc: osm_waterway_linestring -> waterway_z12 +CREATE OR REPLACE VIEW waterway_z12 AS +( +SELECT geometry, + waterway::text AS class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM osm_waterway_linestring +WHERE waterway IN ('river', 'canal') + ); + +-- etldoc: osm_waterway_linestring -> waterway_z13 +CREATE OR REPLACE VIEW waterway_z13 AS +( +SELECT geometry, + waterway::text AS class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM osm_waterway_linestring +WHERE waterway IN ('river', 'canal', 'stream', 'drain', 'ditch') + ); + +-- etldoc: osm_waterway_linestring -> waterway_z14 +CREATE OR REPLACE VIEW waterway_z14 AS +( +SELECT geometry, + waterway::text AS class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM osm_waterway_linestring + ); + +-- etldoc: layer_waterway[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_waterway | z3 | z4 | z5 | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ]; + +CREATE OR REPLACE FUNCTION layer_waterway(bbox geometry, zoom_level int) + RETURNS TABLE + ( + geometry geometry, + class text, + name text, + name_en text, + name_de text, + brunnel text, + intermittent int, + tags hstore + ) +AS +$$ +SELECT geometry, + class, + NULLIF(name, '') AS name, + COALESCE(NULLIF(name_en, ''), NULLIF(name, '')) AS name_en, + COALESCE(NULLIF(name_de, ''), NULLIF(name, ''), NULLIF(name_en, '')) AS name_de, + waterway_brunnel(is_bridge, is_tunnel) AS brunnel, + is_intermittent::int AS intermittent, + tags +FROM ( + -- etldoc: waterway_z3 -> layer_waterway:z3 + SELECT * + FROM waterway_z3 + WHERE zoom_level = 3 + UNION ALL + -- etldoc: waterway_z4 -> layer_waterway:z4 + SELECT * + FROM waterway_z4 + WHERE zoom_level = 4 + UNION ALL + -- etldoc: waterway_z5 -> layer_waterway:z5 + SELECT * + FROM waterway_z5 + WHERE zoom_level = 5 + UNION ALL + -- etldoc: waterway_z6 -> layer_waterway:z6 + SELECT * + FROM waterway_z6 + WHERE zoom_level = 6 + UNION ALL + -- etldoc: waterway_z7 -> layer_waterway:z7 + SELECT * + FROM waterway_z7 + WHERE zoom_level = 7 + UNION ALL + -- etldoc: waterway_z8 -> layer_waterway:z8 + SELECT * + FROM waterway_z8 + WHERE zoom_level = 8 + UNION ALL + -- etldoc: waterway_z9 -> layer_waterway:z9 + SELECT * + FROM waterway_z9 + WHERE zoom_level = 9 + UNION ALL + -- etldoc: waterway_z10 -> layer_waterway:z10 + SELECT * + FROM waterway_z10 + WHERE zoom_level = 10 + UNION ALL + -- etldoc: waterway_z11 -> layer_waterway:z11 + SELECT * + FROM waterway_z11 + WHERE zoom_level = 11 + UNION ALL + -- etldoc: waterway_z12 -> layer_waterway:z12 + SELECT * + FROM waterway_z12 + WHERE zoom_level = 12 + UNION ALL + -- etldoc: waterway_z13 -> layer_waterway:z13 + SELECT * + FROM waterway_z13 + WHERE zoom_level = 13 + UNION ALL + -- etldoc: waterway_z14 -> layer_waterway:z14 + SELECT * + FROM waterway_z14 + WHERE zoom_level >= 14 + ) AS zoom_levels +WHERE geometry && bbox; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/waterway/waterway.yaml b/layers/waterway/waterway.yaml new file mode 100644 index 0000000..d344085 --- /dev/null +++ b/layers/waterway/waterway.yaml @@ -0,0 +1,53 @@ +layer: + id: "waterway" + # waterway relies on the function waterway_brunnel() defined in water layer + requires: + layers: + - water + tables: + - ne_110m_rivers_lake_centerlines + - ne_50m_rivers_lake_centerlines + description: | + OpenStreetMap [waterways](https://wiki.openstreetmap.org/wiki/Waterways) for higher zoom levels (z9 and more) + and Natural Earth rivers and lake centerlines for low zoom levels (z3 - z8). + Linestrings without a name or which are too short are filtered + out at low zoom levels. + Till z11 there is `river` class only, in z12 there is also `canal` generated, + starting z13 there is no generalization according to `class` field applied. + Waterways do not have a `subclass` field. + buffer_size: 4 + fields: + name: | + The OSM [`name`](http://wiki.openstreetmap.org/wiki/Key:name) value of the waterway. Language-specific values are in `name:xx`. + The `name` field may be empty for NaturalEarth data or at lower zoom levels. + name_en: English name `name:en` if available, otherwise `name`. This is deprecated and will be removed in a future release in favor of `name:en`. + name_de: German name `name:de` if available, otherwise `name` or `name:en`. This is deprecated and will be removed in a future release in favor of `name:de`. + class: + description: | + The original value of the [`waterway`](http://wiki.openstreetmap.org/wiki/Key:waterway) tag. + values: + - stream + - river + - canal + - drain + - ditch + brunnel: + description: | + Mark whether way is a tunnel or bridge. + values: + - bridge + - tunnel + intermittent: + description: | + Mark with `1` if it is an [intermittent](http://wiki.openstreetmap.org/wiki/Key:intermittent) waterway. + values: [0, 1] + datasource: + geometry_field: geometry + query: (SELECT geometry, name, name_en, name_de, {name_languages}, class, brunnel, intermittent FROM layer_waterway(!bbox!, z(!scale_denominator!))) AS t +schema: + - ./update_waterway_linestring.sql + - ./update_important_waterway.sql + - ./waterway.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/vfosnarmaptiles.yaml b/openmaptiles.yaml similarity index 75% rename from vfosnarmaptiles.yaml rename to openmaptiles.yaml index f5fd69d..03d7f3e 100644 --- a/vfosnarmaptiles.yaml +++ b/openmaptiles.yaml @@ -1,11 +1,26 @@ tileset: layers: - - layers/kct/kct.yaml - - layers/outdoor_poi/outdoor_poi.yaml - name: ComplementaryMapTiles - version: 1.0.0 - id: complementarymaptiles - description: "" + - layers/barrier/barrier.yaml + - layers/water/water.yaml + - layers/waterway/waterway.yaml + - layers/landcover/landcover.yaml + - layers/landuse/landuse.yaml + - layers/mountain_peak/mountain_peak.yaml + - layers/park/park.yaml + - layers/boundary/boundary.yaml + - layers/aeroway/aeroway.yaml + - layers/transportation/transportation.yaml + - layers/building/building.yaml + - layers/water_name/water_name.yaml + - layers/transportation_name/transportation_name.yaml + - layers/place/place.yaml + - layers/housenumber/housenumber.yaml + - layers/poi/poi.yaml + - layers/aerodrome_label/aerodrome_label.yaml + name: VfosnarMapTiles + version: 3.15.0 + id: vfosnarmaptiles + description: "A tileset showcasing all layers in OpenMapTiles. https://openmaptiles.org" attribution: '© OpenMapTiles © OpenStreetMap contributors' center: [0, 0, 1] bounds: [-180.0, -85.0511, 180.0, 85.0511] @@ -84,6 +99,7 @@ tileset: - ta # Tamil - te # Telugu - th # Thai + - tok # Toki Pona - tr # Turkish, Latin - uk # Ukrainian - ur # Urdu diff --git a/quickstart.sh b/quickstart.sh new file mode 100755 index 0000000..41585cd --- /dev/null +++ b/quickstart.sh @@ -0,0 +1,361 @@ +#!/bin/bash +set -o errexit +set -o pipefail +set -o nounset + + +########################################### +# OpenMapTiles quickstart.sh for x86_64 linux +# +# Usage: +# ./quickstart.sh [--empty] [area [geofabrik|osmfr|bbbike]] +# +# Use a preloaded docker image to speed up, unless the --empty flag is used. +# +# Servers: +# geofabik: http://download.geofabrik.de (default) +# osmfr: http://download.openstreetmap.fr (default for hierarchical area names) +# bbbike: https://www.bbbike.org (default for capitalized area names) +# +# Example calls ... +# ./quickstart.sh +# ./quickstart.sh africa +# ./quickstart.sh africa geofabrik +# ./quickstart.sh africa osmfr +# ./quickstart.sh alabama +# ./quickstart.sh alaska +# ./quickstart.sh albania +# ./quickstart.sh alberta +# ./quickstart.sh alps +# ./quickstart.sh europe/austria +# ./quickstart.sh europe/austria/salzburg osmfr +# ./quickstart.sh Adelaide +# ./quickstart.sh Adelaide bbbike +# .... +# +# to list geofabrik areas: make list-geofabrik or make list-bbbike +# see more QUICKSTART.md +# + +# If --empty is not given, use preloaded docker image to speed up +if [ $# -gt 0 ] && [[ $1 == --empty ]]; then + export USE_PRELOADED_IMAGE="" + shift +else + export USE_PRELOADED_IMAGE=true +fi + +if [ $# -eq 0 ]; then + # default test area + export area=albania + echo "No parameter - set area=$area " +else + export area=$1 +fi + +if [ $# -eq 2 ]; then + osm_server=$2 +fi + +## Min versions ... +MIN_COMPOSE_VER=1.7.1 +MIN_DOCKER_VER=1.12.3 +STARTTIME=$(date +%s) +STARTDATE=$(date +"%Y-%m-%dT%H:%M%z") + +log_file=./quickstart.log +rm -f $log_file +echo " " +echo "=====================================================================================" +echo " Docker check & Download images " +echo "-------------------------------------------------------------------------------------" +echo "====> : Please check the Docker and docker-compose version!" +echo " : We are using docker-compose v3 file format! see more at https://docs.docker.com/" +echo " : Minimum required Docker version: $MIN_DOCKER_VER+" +echo " : Minimum required docker-compose version: $MIN_COMPOSE_VER+" +echo " : See the .travis build for the currently supported versions." +echo " : Your docker system:" + +if ! command -v docker-compose &> /dev/null; then + DOCKER_COMPOSE_HYPHEN=false +else + DOCKER_COMPOSE_HYPHEN=true +fi + +function docker_compose_command () { + if $DOCKER_COMPOSE_HYPHEN; then + docker-compose $@ + else + docker compose $@ + fi +} + +docker --version +docker_compose_command --version + +# based on: http://stackoverflow.com/questions/16989598/bash-comparing-version-numbers +function version { echo "$@" | tr -d 'v' | tr -cs '0-9.' '.' | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'; } + +COMPOSE_VER=$(docker_compose_command version --short) +if [ "$(version "$COMPOSE_VER")" -lt "$(version "$MIN_COMPOSE_VER")" ]; then + echo "ERR: Your Docker-compose version is known to have bugs, please update docker-compose!" + exit 1 +fi + +DOCKER_VER="$(docker -v | awk -F '[ ,]+' '{ print $3 }')" +if [ "$(version "$DOCKER_VER")" -lt "$(version "$MIN_DOCKER_VER")" ]; then + echo "ERR: Your Docker version is not compatible. Please Update docker!" + exit 1 +fi + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Pulling or refreshing OpenMapTiles docker images " +make refresh-docker-images + + +##### backup log from here ... +exec &> >(tee -a "$log_file") + +echo " " +echo "=====================================================================================" +echo " Start processing " +echo "-------------------------------------------------------------------------------------" +echo "====> : OpenMapTiles quickstart! [ https://github.com/openmaptiles/openmaptiles ] " +echo " : This will be logged to the $log_file file (for debugging) and to the screen" +echo " : Area : $area " +echo " : Download server : ${osm_server:-unset (automatic)} " +echo " : Preloaded image : $USE_PRELOADED_IMAGE " +echo " : Git version : $(git rev-parse HEAD) " +echo " : Started : $STARTDATE " +echo " : Your bash version: $BASH_VERSION" +echo " : Your OS : $OSTYPE" +docker --version +docker_compose_command --version + +if [[ "$OSTYPE" == "linux-gnu" ]]; then + echo " " + echo "-------------------------------------------------------------------------------------" + echo " : This is working on x86_64 ; Your kernel is:" + uname -r + uname -m + + KERNEL_CPU_VER=$(uname -m) + if [ "$KERNEL_CPU_VER" != "x86_64" ]; then + echo "ERR: Sorry this is working only on x86_64!" + exit 1 + fi + + echo " : --- Memory, CPU info ---- " + if [ -n "$(command -v bc)" ]; then + mem=$( grep MemTotal /proc/meminfo | awk '{print $2}' | xargs -I {} echo "scale=4; {}/1024^2" | bc ) + echo "System memory (GB): ${mem}" + else + mem=$( grep MemTotal /proc/meminfo | awk '{print $2}') + echo "System memory (KB): ${mem}" + fi + grep SwapTotal /proc/meminfo + echo "CPU number: $(grep -c processor /proc/cpuinfo) x $(grep "bogomips" /proc/cpuinfo | head -1)" + grep Free /proc/meminfo +else + echo " " + echo "Warning : Platforms other than Linux are less tested" + echo " " +fi + +MBTILES_FILE=${MBTILES_FILE:-$(source .env ; echo "$MBTILES_FILE")} + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Stopping running services & removing old containers" +make destroy-db + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Existing OpenMapTiles docker images. Will use version $(source .env && echo "$TOOLS_VERSION")" +docker images | grep openmaptiles + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Create directories if they don't exist" +make init-dirs + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Removing old MBTILES if exists ( ./data/$MBTILES_FILE ) " +rm -f "./data/$MBTILES_FILE" + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Downloading ${area} from ${osm_server:-any source}..." +make "download${osm_server:+-${osm_server}}" + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Remove old generated source files ( ./build/* ) ( if they exist ) " +make clean + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Code generating from the layer definitions ( ./build/mapping.yaml; ./build/sql/* )" +echo " : The tool source code: https://github.com/openmaptiles/openmaptiles-tools " +echo " : But we generate the tm2source, Imposm mappings and SQL functions from the layer definitions! " +make all + +echo " " +echo "-------------------------------------------------------------------------------------" +if [[ "$USE_PRELOADED_IMAGE" == true ]]; then + echo "====> : Start PostgreSQL service using postgis image preloaded with this data:" + echo " : * Water data from http://osmdata.openstreetmap.de" + echo " : Data license: https://osmdata.openstreetmap.de/info/license.html" + echo " : * Natural Earth from http://www.naturalearthdata.com" + echo " : Terms-of-use: http://www.naturalearthdata.com/about/terms-of-use" + echo " : * OpenStreetMap Lakelines data https://github.com/openmaptiles/osm-lakelines" + echo " :" + echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/import-data" + echo " : includes all data from the import-data image" + echo " :" + echo " : Use the --empty flag to start with an empty database:" + echo " : ./quickstart.sh --empty albania " + echo " : If desired, you can manually import data by using these commands:" + echo " : make destroy-db" + echo " : make start-db" + echo " : make import-data" + echo " :" + echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/postgis-preloaded" + echo " : Thank you https://www.postgresql.org ! Thank you http://postgis.org !" + make start-db-preloaded +else + echo "====> : Start PostgreSQL service using empty database and importing all the data:" + echo " : * Water data from http://osmdata.openstreetmap.de" + echo " : Data license: https://osmdata.openstreetmap.de/info/license.html" + echo " : * Natural Earth from http://www.naturalearthdata.com" + echo " : Terms-of-use: http://www.naturalearthdata.com/about/terms-of-use" + echo " : * OpenStreetMap Lakelines data https://github.com/openmaptiles/osm-lakelines" + echo " :" + echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/import-data" + echo " : includes all data from the import-data image" + echo " :" + echo " : Thank you https://www.postgresql.org ! Thank you http://postgis.org !" + make start-db + make import-data +fi + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Start importing OpenStreetMap data: ${area} -> imposm3[./build/mapping.yaml] -> PostgreSQL" +echo " : Imposm3 documentation: https://imposm.org/docs/imposm3/latest/index.html " +echo " : Thank you Omniscale! " +echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/blob/master/bin/import-osm " +echo " : The OpenstreetMap data license: https://www.openstreetmap.org/copyright (ODBL) " +echo " : Thank you OpenStreetMap Contributors ! " +make import-osm + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Start importing Wikidata: Wikidata Query Service -> PostgreSQL" +echo " : The Wikidata license: CC0 - https://www.wikidata.org/wiki/Wikidata:Main_Page " +echo " : Thank you Wikidata Contributors ! " +make import-wikidata + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Start SQL postprocessing: ./build/sql/* -> PostgreSQL " +echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/blob/master/bin/import-sql" +# If the output contains a WARNING, stop further processing +# Adapted from https://unix.stackexchange.com/questions/307562 +make import-sql + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Analyze PostgreSQL tables" +make analyze-db + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Testing PostgreSQL tables to match layer definitions metadata" +make test-perf-null + +echo " " +echo "-------------------------------------------------------------------------------------" + +if [[ "$(source .env ; echo "$BBOX")" = "-180.0,-85.0511,180.0,85.0511" ]]; then + if [[ "$area" != "planet" ]]; then + echo "====> : Compute bounding box for tile generation" + make generate-bbox-file ${MIN_ZOOM:+MIN_ZOOM="${MIN_ZOOM}"} ${MAX_ZOOM:+MAX_ZOOM="${MAX_ZOOM}"} + else + echo "====> : Skipping bbox calculation when generating the entire planet" + fi + +else + echo "====> : Bounding box is set in .env file" +fi + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Start generating MBTiles (containing gzipped MVT PBF) using PostGIS. " +echo " : Output MBTiles: $MBTILES_FILE " +echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/blob/master/bin/generate-tiles " +make generate-tiles-pg + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Stop PostgreSQL service ( but we keep PostgreSQL data volume for debugging )" +make stop-db + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Inputs - Outputs md5sum for debugging " +rm -f ./data/quickstart_checklist.chk +{ + find build data -type f -exec md5sum {} + | sort -k2 +} >> ./data/quickstart_checklist.chk +cat ./data/quickstart_checklist.chk + +ENDTIME=$(date +%s) + +echo " " +echo " " +echo "-------------------------------------------------------------------------------------" +echo "-- S u m m a r y --" +echo "-------------------------------------------------------------------------------------" +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : (disk space) We have created a lot of docker images: " +echo " : Hint: you can remove with: docker rmi IMAGE " +docker images | grep openmaptiles + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : (disk space) We have created the new vectortiles ( ./data/$MBTILES_FILE ) " +echo " : Please respect the licenses (OdBL for OSM data) of the sources when distributing the MBTiles file." +echo " : Data directory content:" +ls -la ./data + +echo " " +echo "-------------------------------------------------------------------------------------" +echo "The ./quickstart.sh $area is finished! " +echo "It took $((ENDTIME - STARTTIME)) seconds to complete" +echo "We saved the log file to $log_file (for debugging) You can compare with the travis log !" +echo " " +echo "Start experimenting and check the QUICKSTART.MD file!" +echo " " +echo "* Use make start-maputnik to explore tile generation on request" +echo "* Use make start-tileserver to view pre-generated tiles" +echo " " +echo "Available help commands (make help) " +make help + +echo "-------------------------------------------------------------------------------------" +echo " Acknowledgments " +echo " Generated vector tiles are produced work of OpenStreetMap data. " +echo " Such tiles are reusable under CC-BY license granted by OpenMapTiles team: " +echo " https://github.com/openmaptiles/openmaptiles/#license " +echo "-------------------------------------------------------------------------------------" +echo " " +echo -e "\033[1m Maps made with these vector tiles must display a visible credit:\033[0m " +echo " © OpenMapTiles © OpenStreetMap contributors " +echo " " +echo "-------------------------------------------------------------------------------------" +echo " Thanks to all free, open source software developers and Open Data Contributors! " +echo "-------------------------------------------------------------------------------------" diff --git a/style/README.md b/style/README.md new file mode 100644 index 0000000..0058aec --- /dev/null +++ b/style/README.md @@ -0,0 +1,87 @@ +## OSM OpenMapTiles style + +_OSM OpenMapTiles_ is the official style of OpenMapTiles. +Its purpose is to display all features in vector tiles. + +OSM OpenMapTiles style is heavily inspired by +[OSM Carto](https://github.com/gravitystorm/openstreetmap-carto). + +Huge credit belongs to the original +[authors](https://github.com/gravitystorm/openstreetmap-carto/blob/master/LICENSE.txt). + +### Fonts + +OSM OpenMapTiles style used _Noto Sans_ fonts. +To download these fonts run: +```bash +make download-fonts +``` +It downloads _Noto Sans_ fonts (~70MB) and extract them into [openmaptiles/data/fonts](../data/fonts) directory. + +### Icons/sprite + +All icons which are used OpenMapTiles style are located in [openmaptiles/style/icons](icons). +After the style is built, the icons are composed into sprite files located in `build` directory. + +Additional svg icons can be added to [openmaptiles/style/icons](icons) directory. + +To generate new sprite files with added icons, run: +```bash +make build-sprite +``` +Sprite files will be generated into `build` directory. + +### OSM Icons + +Icons in the _OSM OpenMapTiles_ style are based on original +[OSM Carto](https://github.com/gravitystorm/openstreetmap-carto) symbols. + +The original icons can be found in +[openstreetmap-carto/symbols](https://github.com/gravitystorm/openstreetmap-carto/tree/master/symbols). + +Icons used in _OSM OpenMapTiles_ style were scaled down and saved as svg. + +### Build style + +To build style run: +```bash +make build-style +``` +It generates new sprite files and merges all style snippets from each layer, orders them according the `order` value +and saves the complete style into `build/style/style.json`. + +### Tileserver-gl +The tileserver serves both the tiles and the OSM OpenMapTiles map. +#### MBTiles (default) +By default, the tileserver serves OSM OpenMapTiles map based on tiles from `data/tiles.mbtiles` as defined in +[style-header.json](./style-header.json). +```json +"sources": { + "openmaptiles": { + "type": "vector", + "url": "mbtiles:///data/tiles.mbtiles" + }, + ... +} +``` +#### Serve from the db +The tileserver can also serve OSM OpenMapTiles map based on dynamically generated tiles directly from the database. +Start the database container and the postserve container: +```bash +make start-db +make start-postserve +``` +In [style-header.json](./style-header.json) change the source of tiles to PostServe: + +#### Start tileserver +Before you start the tileserver, make sure you have fonts downloaded in [openmaptiles/data/fonts](../data/fonts), +sprites generated and style built: +```bash +make download-fonts +make build-style +``` +Start tileserver: +```bash +make start-tileserver +``` +And go to http://localhost:8080. diff --git a/style/config.json b/style/config.json new file mode 100644 index 0000000..1da3f09 --- /dev/null +++ b/style/config.json @@ -0,0 +1,22 @@ +{ + "options": { + "paths": { + "fonts": "/data/fonts", + "sprites": "/build/style", + "styles": "/build/style" + } + }, + "styles": { + "OSM OpenMapTiles": { + "style": "style.json", + "tilejson": { + "type": "overlay" + } + } + }, + "data": { + "openmaptiles": { + "mbtiles": "/data/tiles.mbtiles" + } + } +} diff --git a/style/icons/_amenity_bench.svg b/style/icons/_amenity_bench.svg new file mode 100644 index 0000000..0839440 --- /dev/null +++ b/style/icons/_amenity_bench.svg @@ -0,0 +1,53 @@ + + diff --git a/style/icons/_playground_roundabout.svg b/style/icons/_playground_roundabout.svg new file mode 100644 index 0000000..a4484c7 --- /dev/null +++ b/style/icons/_playground_roundabout.svg @@ -0,0 +1,125 @@ + + diff --git a/style/icons/advertising_column.svg b/style/icons/advertising_column.svg new file mode 100644 index 0000000..cc0ac6a --- /dev/null +++ b/style/icons/advertising_column.svg @@ -0,0 +1,77 @@ + + diff --git a/style/icons/aerodrome.12.svg b/style/icons/aerodrome.12.svg new file mode 100644 index 0000000..cd18fb4 --- /dev/null +++ b/style/icons/aerodrome.12.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/alcohol.svg b/style/icons/alcohol.svg new file mode 100644 index 0000000..ee909df --- /dev/null +++ b/style/icons/alcohol.svg @@ -0,0 +1,14 @@ + + + + + image/svg+xml + + + + + + + + + diff --git a/style/icons/allotments.svg b/style/icons/allotments.svg new file mode 100644 index 0000000..01c8407 --- /dev/null +++ b/style/icons/allotments.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/alpinehut.svg b/style/icons/alpinehut.svg new file mode 100644 index 0000000..9c9d8af --- /dev/null +++ b/style/icons/alpinehut.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/amusement_arcade.svg b/style/icons/amusement_arcade.svg new file mode 100644 index 0000000..46728f4 --- /dev/null +++ b/style/icons/amusement_arcade.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/style/icons/apartment.svg b/style/icons/apartment.svg new file mode 100644 index 0000000..cc6ca60 --- /dev/null +++ b/style/icons/apartment.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/style/icons/archaeological_site.svg b/style/icons/archaeological_site.svg new file mode 100644 index 0000000..ec769ae --- /dev/null +++ b/style/icons/archaeological_site.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/arete-mid.svg b/style/icons/arete-mid.svg new file mode 100644 index 0000000..a23d320 --- /dev/null +++ b/style/icons/arete-mid.svg @@ -0,0 +1,26 @@ + + + + + + diff --git a/style/icons/arete2.svg b/style/icons/arete2.svg new file mode 100644 index 0000000..6b95f60 --- /dev/null +++ b/style/icons/arete2.svg @@ -0,0 +1,36 @@ + + + + + + Created with Snap + + diff --git a/style/icons/art.svg b/style/icons/art.svg new file mode 100644 index 0000000..237775f --- /dev/null +++ b/style/icons/art.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/arts_centre.svg b/style/icons/arts_centre.svg new file mode 100644 index 0000000..c6fe7e7 --- /dev/null +++ b/style/icons/arts_centre.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/artwork.svg b/style/icons/artwork.svg new file mode 100644 index 0000000..db2df65 --- /dev/null +++ b/style/icons/artwork.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/atm.svg b/style/icons/atm.svg new file mode 100644 index 0000000..b0ac45a --- /dev/null +++ b/style/icons/atm.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/audioguide.svg b/style/icons/audioguide.svg new file mode 100644 index 0000000..58f330e --- /dev/null +++ b/style/icons/audioguide.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/bag.svg b/style/icons/bag.svg new file mode 100644 index 0000000..1de09e7 --- /dev/null +++ b/style/icons/bag.svg @@ -0,0 +1,40 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/style/icons/bakery.svg b/style/icons/bakery.svg new file mode 100644 index 0000000..dace2bd --- /dev/null +++ b/style/icons/bakery.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/bank.svg b/style/icons/bank.svg new file mode 100644 index 0000000..4fe94ec --- /dev/null +++ b/style/icons/bank.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/bar.svg b/style/icons/bar.svg new file mode 100644 index 0000000..040a404 --- /dev/null +++ b/style/icons/bar.svg @@ -0,0 +1,60 @@ + + diff --git a/style/icons/bbq.svg b/style/icons/bbq.svg new file mode 100644 index 0000000..3480229 --- /dev/null +++ b/style/icons/bbq.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/beach.svg b/style/icons/beach.svg new file mode 100644 index 0000000..2eb9362 --- /dev/null +++ b/style/icons/beach.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/style/icons/beach_coarse.svg b/style/icons/beach_coarse.svg new file mode 100644 index 0000000..97606d3 --- /dev/null +++ b/style/icons/beach_coarse.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/style/icons/beach_resort.svg b/style/icons/beach_resort.svg new file mode 100644 index 0000000..649d122 --- /dev/null +++ b/style/icons/beach_resort.svg @@ -0,0 +1,4 @@ + + + + diff --git a/style/icons/beauty.svg b/style/icons/beauty.svg new file mode 100644 index 0000000..f2cfad6 --- /dev/null +++ b/style/icons/beauty.svg @@ -0,0 +1,40 @@ + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/style/icons/bed.svg b/style/icons/bed.svg new file mode 100644 index 0000000..15c95ed --- /dev/null +++ b/style/icons/bed.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/bell_tower.svg b/style/icons/bell_tower.svg new file mode 100644 index 0000000..6341ca4 --- /dev/null +++ b/style/icons/bell_tower.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/bench.svg b/style/icons/bench.svg new file mode 100644 index 0000000..e70cfec --- /dev/null +++ b/style/icons/bench.svg @@ -0,0 +1,65 @@ + + diff --git a/style/icons/beverages.svg b/style/icons/beverages.svg new file mode 100644 index 0000000..209d4b6 --- /dev/null +++ b/style/icons/beverages.svg @@ -0,0 +1,38 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/style/icons/bicycle.svg b/style/icons/bicycle.svg new file mode 100644 index 0000000..3813e62 --- /dev/null +++ b/style/icons/bicycle.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/bicycle_parking.svg b/style/icons/bicycle_parking.svg new file mode 100644 index 0000000..a9bdb44 --- /dev/null +++ b/style/icons/bicycle_parking.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/bicycle_repair_station.svg b/style/icons/bicycle_repair_station.svg new file mode 100644 index 0000000..ba21b24 --- /dev/null +++ b/style/icons/bicycle_repair_station.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/biergarten.svg b/style/icons/biergarten.svg new file mode 100644 index 0000000..153693f --- /dev/null +++ b/style/icons/biergarten.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/bird_hide.svg b/style/icons/bird_hide.svg new file mode 100644 index 0000000..ab9513e --- /dev/null +++ b/style/icons/bird_hide.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/board.svg b/style/icons/board.svg new file mode 100644 index 0000000..ca99d4c --- /dev/null +++ b/style/icons/board.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/boat_rental.svg b/style/icons/boat_rental.svg new file mode 100644 index 0000000..1bf0829 --- /dev/null +++ b/style/icons/boat_rental.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/bookmaker.svg b/style/icons/bookmaker.svg new file mode 100644 index 0000000..7c318a2 --- /dev/null +++ b/style/icons/bookmaker.svg @@ -0,0 +1,4 @@ + + + + diff --git a/style/icons/books.svg b/style/icons/books.svg new file mode 100644 index 0000000..f6a3171 --- /dev/null +++ b/style/icons/books.svg @@ -0,0 +1,57 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/style/icons/bowling_alley.svg b/style/icons/bowling_alley.svg new file mode 100644 index 0000000..8ff35da --- /dev/null +++ b/style/icons/bowling_alley.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/buddhist.svg b/style/icons/buddhist.svg new file mode 100644 index 0000000..3f1b190 --- /dev/null +++ b/style/icons/buddhist.svg @@ -0,0 +1,20 @@ + + + + + + + diff --git a/style/icons/bunker.svg b/style/icons/bunker.svg new file mode 100644 index 0000000..ae9a009 --- /dev/null +++ b/style/icons/bunker.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/bureau_de_change.svg b/style/icons/bureau_de_change.svg new file mode 100644 index 0000000..d0a9405 --- /dev/null +++ b/style/icons/bureau_de_change.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/style/icons/bus_station.svg b/style/icons/bus_station.svg new file mode 100644 index 0000000..f98219e --- /dev/null +++ b/style/icons/bus_station.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/style/icons/bus_stop.12.svg b/style/icons/bus_stop.12.svg new file mode 100644 index 0000000..0bc0cf9 --- /dev/null +++ b/style/icons/bus_stop.12.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/bust.svg b/style/icons/bust.svg new file mode 100644 index 0000000..59b1cf1 --- /dev/null +++ b/style/icons/bust.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/butcher.svg b/style/icons/butcher.svg new file mode 100644 index 0000000..9a61c2d --- /dev/null +++ b/style/icons/butcher.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/cafe.svg b/style/icons/cafe.svg new file mode 100644 index 0000000..c41e48c --- /dev/null +++ b/style/icons/cafe.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/camping.svg b/style/icons/camping.svg new file mode 100644 index 0000000..e82f465 --- /dev/null +++ b/style/icons/camping.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/car.svg b/style/icons/car.svg new file mode 100644 index 0000000..97326bf --- /dev/null +++ b/style/icons/car.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/style/icons/car_parts.svg b/style/icons/car_parts.svg new file mode 100644 index 0000000..7e8c85c --- /dev/null +++ b/style/icons/car_parts.svg @@ -0,0 +1,40 @@ + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/style/icons/car_repair.svg b/style/icons/car_repair.svg new file mode 100644 index 0000000..a84ef78 --- /dev/null +++ b/style/icons/car_repair.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/car_wash.svg b/style/icons/car_wash.svg new file mode 100644 index 0000000..e2105ef --- /dev/null +++ b/style/icons/car_wash.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/caravan_park.svg b/style/icons/caravan_park.svg new file mode 100644 index 0000000..e4e7eeb --- /dev/null +++ b/style/icons/caravan_park.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/carpet.svg b/style/icons/carpet.svg new file mode 100644 index 0000000..f2eef5f --- /dev/null +++ b/style/icons/carpet.svg @@ -0,0 +1,31 @@ + + + + + + + image/svg+xml + + + + + + + diff --git a/style/icons/casino.svg b/style/icons/casino.svg new file mode 100644 index 0000000..78187c6 --- /dev/null +++ b/style/icons/casino.svg @@ -0,0 +1,4 @@ + + + + diff --git a/style/icons/castle.svg b/style/icons/castle.svg new file mode 100644 index 0000000..b299fe0 --- /dev/null +++ b/style/icons/castle.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/cattle_grid.svg b/style/icons/cattle_grid.svg new file mode 100644 index 0000000..2de78b7 --- /dev/null +++ b/style/icons/cattle_grid.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/style/icons/cave.svg b/style/icons/cave.svg new file mode 100644 index 0000000..3215019 --- /dev/null +++ b/style/icons/cave.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/chalet.svg b/style/icons/chalet.svg new file mode 100644 index 0000000..4f413a2 --- /dev/null +++ b/style/icons/chalet.svg @@ -0,0 +1,4 @@ + + + + diff --git a/style/icons/charging_station.svg b/style/icons/charging_station.svg new file mode 100644 index 0000000..cf03ef6 --- /dev/null +++ b/style/icons/charging_station.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/charity.svg b/style/icons/charity.svg new file mode 100644 index 0000000..a3a9ada --- /dev/null +++ b/style/icons/charity.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/chemist.svg b/style/icons/chemist.svg new file mode 100644 index 0000000..fd4f832 --- /dev/null +++ b/style/icons/chemist.svg @@ -0,0 +1,40 @@ + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/style/icons/chimney.svg b/style/icons/chimney.svg new file mode 100644 index 0000000..234bd93 --- /dev/null +++ b/style/icons/chimney.svg @@ -0,0 +1,4 @@ + + + + diff --git a/style/icons/christian.svg b/style/icons/christian.svg new file mode 100644 index 0000000..8461e9c --- /dev/null +++ b/style/icons/christian.svg @@ -0,0 +1,20 @@ + + + + + + + diff --git a/style/icons/cinema.svg b/style/icons/cinema.svg new file mode 100644 index 0000000..bf78010 --- /dev/null +++ b/style/icons/cinema.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/city_gate.svg b/style/icons/city_gate.svg new file mode 100644 index 0000000..2297b17 --- /dev/null +++ b/style/icons/city_gate.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/cliff.svg b/style/icons/cliff.svg new file mode 100644 index 0000000..55b6c8d --- /dev/null +++ b/style/icons/cliff.svg @@ -0,0 +1,38 @@ + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/style/icons/cliff2.svg b/style/icons/cliff2.svg new file mode 100644 index 0000000..5e5e067 --- /dev/null +++ b/style/icons/cliff2.svg @@ -0,0 +1,39 @@ + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/style/icons/clothes.svg b/style/icons/clothes.svg new file mode 100644 index 0000000..075aaa8 --- /dev/null +++ b/style/icons/clothes.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/coffee.svg b/style/icons/coffee.svg new file mode 100644 index 0000000..2a4c162 --- /dev/null +++ b/style/icons/coffee.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/communications_tower.svg b/style/icons/communications_tower.svg new file mode 100644 index 0000000..696ce5b --- /dev/null +++ b/style/icons/communications_tower.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/style/icons/community_centre.svg b/style/icons/community_centre.svg new file mode 100644 index 0000000..b8bc348 --- /dev/null +++ b/style/icons/community_centre.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/computer.svg b/style/icons/computer.svg new file mode 100644 index 0000000..259c2af --- /dev/null +++ b/style/icons/computer.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/confectionery.svg b/style/icons/confectionery.svg new file mode 100644 index 0000000..6df47e2 --- /dev/null +++ b/style/icons/confectionery.svg @@ -0,0 +1,40 @@ + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/style/icons/consulate.svg b/style/icons/consulate.svg new file mode 100644 index 0000000..ee5f6f4 --- /dev/null +++ b/style/icons/consulate.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/convenience.svg b/style/icons/convenience.svg new file mode 100644 index 0000000..d0522da --- /dev/null +++ b/style/icons/convenience.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/copyshop.svg b/style/icons/copyshop.svg new file mode 100644 index 0000000..2fa1cab --- /dev/null +++ b/style/icons/copyshop.svg @@ -0,0 +1,40 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/style/icons/cosmetics.svg b/style/icons/cosmetics.svg new file mode 100644 index 0000000..a7f136b --- /dev/null +++ b/style/icons/cosmetics.svg @@ -0,0 +1,69 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/style/icons/courthouse.svg b/style/icons/courthouse.svg new file mode 100644 index 0000000..a492fc6 --- /dev/null +++ b/style/icons/courthouse.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/crane.svg b/style/icons/crane.svg new file mode 100644 index 0000000..514cc27 --- /dev/null +++ b/style/icons/crane.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/cross.svg b/style/icons/cross.svg new file mode 100644 index 0000000..be75053 --- /dev/null +++ b/style/icons/cross.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/cycle_barrier.svg b/style/icons/cycle_barrier.svg new file mode 100644 index 0000000..8d6a59d --- /dev/null +++ b/style/icons/cycle_barrier.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/dairy.svg b/style/icons/dairy.svg new file mode 100644 index 0000000..c32897d --- /dev/null +++ b/style/icons/dairy.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/danger_red_hatch.svg b/style/icons/danger_red_hatch.svg new file mode 100644 index 0000000..2f33f7e --- /dev/null +++ b/style/icons/danger_red_hatch.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/deli.svg b/style/icons/deli.svg new file mode 100644 index 0000000..0dd7be0 --- /dev/null +++ b/style/icons/deli.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/dentist.svg b/style/icons/dentist.svg new file mode 100644 index 0000000..ea91694 --- /dev/null +++ b/style/icons/dentist.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/department_store.svg b/style/icons/department_store.svg new file mode 100644 index 0000000..1056caf --- /dev/null +++ b/style/icons/department_store.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/diy.svg b/style/icons/diy.svg new file mode 100644 index 0000000..890a15c --- /dev/null +++ b/style/icons/diy.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/doctors.svg b/style/icons/doctors.svg new file mode 100644 index 0000000..b833d34 --- /dev/null +++ b/style/icons/doctors.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/dog_park.svg b/style/icons/dog_park.svg new file mode 100644 index 0000000..1440c1c --- /dev/null +++ b/style/icons/dog_park.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/style/icons/drinking_water.svg b/style/icons/drinking_water.svg new file mode 100644 index 0000000..e5a6426 --- /dev/null +++ b/style/icons/drinking_water.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/electronics.svg b/style/icons/electronics.svg new file mode 100644 index 0000000..f02e3c3 --- /dev/null +++ b/style/icons/electronics.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/elevator.12.svg b/style/icons/elevator.12.svg new file mode 100644 index 0000000..ae34d09 --- /dev/null +++ b/style/icons/elevator.12.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/embankment.svg b/style/icons/embankment.svg new file mode 100644 index 0000000..a1dd277 --- /dev/null +++ b/style/icons/embankment.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/embassy.svg b/style/icons/embassy.svg new file mode 100644 index 0000000..e28f4fc --- /dev/null +++ b/style/icons/embassy.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/emergency_phone.svg b/style/icons/emergency_phone.svg new file mode 100644 index 0000000..b2d2b71 --- /dev/null +++ b/style/icons/emergency_phone.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/entrance.10.svg b/style/icons/entrance.10.svg new file mode 100644 index 0000000..c288a1b --- /dev/null +++ b/style/icons/entrance.10.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/excrement_bags.svg b/style/icons/excrement_bags.svg new file mode 100644 index 0000000..6541d2b --- /dev/null +++ b/style/icons/excrement_bags.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/fabric.svg b/style/icons/fabric.svg new file mode 100644 index 0000000..45b6220 --- /dev/null +++ b/style/icons/fabric.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/fast_food.svg b/style/icons/fast_food.svg new file mode 100644 index 0000000..b494a60 --- /dev/null +++ b/style/icons/fast_food.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/ferry.svg b/style/icons/ferry.svg new file mode 100644 index 0000000..97b30b1 --- /dev/null +++ b/style/icons/ferry.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/firepit.svg b/style/icons/firepit.svg new file mode 100644 index 0000000..dece825 --- /dev/null +++ b/style/icons/firepit.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/firestation.svg b/style/icons/firestation.svg new file mode 100644 index 0000000..f8f0e31 --- /dev/null +++ b/style/icons/firestation.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/fishing.svg b/style/icons/fishing.svg new file mode 100644 index 0000000..57ef82b --- /dev/null +++ b/style/icons/fishing.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/fitness.svg b/style/icons/fitness.svg new file mode 100644 index 0000000..7a7c9de --- /dev/null +++ b/style/icons/fitness.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/florist.svg b/style/icons/florist.svg new file mode 100644 index 0000000..866b599 --- /dev/null +++ b/style/icons/florist.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/flowerbed_high_zoom.svg b/style/icons/flowerbed_high_zoom.svg new file mode 100644 index 0000000..6cbdae3 --- /dev/null +++ b/style/icons/flowerbed_high_zoom.svg @@ -0,0 +1,24 @@ + + + + + + diff --git a/style/icons/ford.svg b/style/icons/ford.svg new file mode 100644 index 0000000..c4a9e3b --- /dev/null +++ b/style/icons/ford.svg @@ -0,0 +1,4 @@ + + + + diff --git a/style/icons/fort.svg b/style/icons/fort.svg new file mode 100644 index 0000000..d729b51 --- /dev/null +++ b/style/icons/fort.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/fortress.svg b/style/icons/fortress.svg new file mode 100644 index 0000000..296a0d7 --- /dev/null +++ b/style/icons/fortress.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/fountain.svg b/style/icons/fountain.svg new file mode 100644 index 0000000..bffcb85 --- /dev/null +++ b/style/icons/fountain.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/fuel.svg b/style/icons/fuel.svg new file mode 100644 index 0000000..08de366 --- /dev/null +++ b/style/icons/fuel.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/full-height_turnstile.svg b/style/icons/full-height_turnstile.svg new file mode 100644 index 0000000..3a45ad6 --- /dev/null +++ b/style/icons/full-height_turnstile.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/furniture.svg b/style/icons/furniture.svg new file mode 100644 index 0000000..9fc9b2a --- /dev/null +++ b/style/icons/furniture.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/gallery.svg b/style/icons/gallery.svg new file mode 100644 index 0000000..ef93c52 --- /dev/null +++ b/style/icons/gallery.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/garden_centre.svg b/style/icons/garden_centre.svg new file mode 100644 index 0000000..a51ceb6 --- /dev/null +++ b/style/icons/garden_centre.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/gate.svg b/style/icons/gate.svg new file mode 100644 index 0000000..ddfdce0 --- /dev/null +++ b/style/icons/gate.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/style/icons/generator_wind.svg b/style/icons/generator_wind.svg new file mode 100644 index 0000000..04828ea --- /dev/null +++ b/style/icons/generator_wind.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/gift.svg b/style/icons/gift.svg new file mode 100644 index 0000000..b803ca3 --- /dev/null +++ b/style/icons/gift.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/golf.svg b/style/icons/golf.svg new file mode 100644 index 0000000..29c8055 --- /dev/null +++ b/style/icons/golf.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/golf_pin.svg b/style/icons/golf_pin.svg new file mode 100644 index 0000000..2e975ce --- /dev/null +++ b/style/icons/golf_pin.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/golf_rough.svg b/style/icons/golf_rough.svg new file mode 100644 index 0000000..5d791fe --- /dev/null +++ b/style/icons/golf_rough.svg @@ -0,0 +1,9 @@ + + + + diff --git a/style/icons/grave_yard_generic.svg b/style/icons/grave_yard_generic.svg new file mode 100644 index 0000000..e2f7890 --- /dev/null +++ b/style/icons/grave_yard_generic.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/greengrocer.svg b/style/icons/greengrocer.svg new file mode 100644 index 0000000..84edc7f --- /dev/null +++ b/style/icons/greengrocer.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/grey_vertical_hatch.svg b/style/icons/grey_vertical_hatch.svg new file mode 100644 index 0000000..d1e3fe0 --- /dev/null +++ b/style/icons/grey_vertical_hatch.svg @@ -0,0 +1,4 @@ + + + + diff --git a/style/icons/grit_bin.svg b/style/icons/grit_bin.svg new file mode 100644 index 0000000..1a381d5 --- /dev/null +++ b/style/icons/grit_bin.svg @@ -0,0 +1,77 @@ + + diff --git a/style/icons/guest_house.svg b/style/icons/guest_house.svg new file mode 100644 index 0000000..e6295e2 --- /dev/null +++ b/style/icons/guest_house.svg @@ -0,0 +1,4 @@ + + + + diff --git a/style/icons/guidepost.svg b/style/icons/guidepost.svg new file mode 100644 index 0000000..54f45e8 --- /dev/null +++ b/style/icons/guidepost.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/hairdresser.svg b/style/icons/hairdresser.svg new file mode 100644 index 0000000..fd9d706 --- /dev/null +++ b/style/icons/hairdresser.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/helipad.16.svg b/style/icons/helipad.16.svg new file mode 100644 index 0000000..ad132d7 --- /dev/null +++ b/style/icons/helipad.16.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/hifi.svg b/style/icons/hifi.svg new file mode 100644 index 0000000..08831a3 --- /dev/null +++ b/style/icons/hifi.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/hinduist.svg b/style/icons/hinduist.svg new file mode 100644 index 0000000..fcaf03c --- /dev/null +++ b/style/icons/hinduist.svg @@ -0,0 +1,20 @@ + + + + + + + diff --git a/style/icons/hospital.svg b/style/icons/hospital.svg new file mode 100644 index 0000000..e73fd05 --- /dev/null +++ b/style/icons/hospital.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/hostel.svg b/style/icons/hostel.svg new file mode 100644 index 0000000..e3b0340 --- /dev/null +++ b/style/icons/hostel.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/style/icons/hotel.svg b/style/icons/hotel.svg new file mode 100644 index 0000000..f4afc46 --- /dev/null +++ b/style/icons/hotel.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/houseware.svg b/style/icons/houseware.svg new file mode 100644 index 0000000..630aa24 --- /dev/null +++ b/style/icons/houseware.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/hunting_stand.svg b/style/icons/hunting_stand.svg new file mode 100644 index 0000000..8c8cffe --- /dev/null +++ b/style/icons/hunting_stand.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/ice_cream.svg b/style/icons/ice_cream.svg new file mode 100644 index 0000000..84859e7 --- /dev/null +++ b/style/icons/ice_cream.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/interior_decoration.svg b/style/icons/interior_decoration.svg new file mode 100644 index 0000000..fd117a4 --- /dev/null +++ b/style/icons/interior_decoration.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/intermittent_water.svg b/style/icons/intermittent_water.svg new file mode 100644 index 0000000..78d95e5 --- /dev/null +++ b/style/icons/intermittent_water.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/style/icons/internet_cafe.svg b/style/icons/internet_cafe.svg new file mode 100644 index 0000000..0452c6f --- /dev/null +++ b/style/icons/internet_cafe.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/jewelry.svg b/style/icons/jewelry.svg new file mode 100644 index 0000000..96e9a8b --- /dev/null +++ b/style/icons/jewelry.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/jewish.svg b/style/icons/jewish.svg new file mode 100644 index 0000000..ca78e7d --- /dev/null +++ b/style/icons/jewish.svg @@ -0,0 +1,38 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/style/icons/kissing_gate.svg b/style/icons/kissing_gate.svg new file mode 100644 index 0000000..134fafa --- /dev/null +++ b/style/icons/kissing_gate.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/laundry.svg b/style/icons/laundry.svg new file mode 100644 index 0000000..0e436e5 --- /dev/null +++ b/style/icons/laundry.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/leaftype_broadleaved.svg b/style/icons/leaftype_broadleaved.svg new file mode 100644 index 0000000..a9ffc58 --- /dev/null +++ b/style/icons/leaftype_broadleaved.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/leaftype_leafless.svg b/style/icons/leaftype_leafless.svg new file mode 100644 index 0000000..2e59fad --- /dev/null +++ b/style/icons/leaftype_leafless.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/leaftype_mixed.svg b/style/icons/leaftype_mixed.svg new file mode 100644 index 0000000..c464ee2 --- /dev/null +++ b/style/icons/leaftype_mixed.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/leaftype_needleleaved.svg b/style/icons/leaftype_needleleaved.svg new file mode 100644 index 0000000..19aaec3 --- /dev/null +++ b/style/icons/leaftype_needleleaved.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/leaftype_unknown.svg b/style/icons/leaftype_unknown.svg new file mode 100644 index 0000000..4c1fbd2 --- /dev/null +++ b/style/icons/leaftype_unknown.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/library.svg b/style/icons/library.svg new file mode 100644 index 0000000..489dd9b --- /dev/null +++ b/style/icons/library.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/lift_gate.svg b/style/icons/lift_gate.svg new file mode 100644 index 0000000..58e23fd --- /dev/null +++ b/style/icons/lift_gate.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/lighthouse.svg b/style/icons/lighthouse.svg new file mode 100644 index 0000000..3bbc253 --- /dev/null +++ b/style/icons/lighthouse.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/manor.svg b/style/icons/manor.svg new file mode 100644 index 0000000..fe1fa8b --- /dev/null +++ b/style/icons/manor.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/map.svg b/style/icons/map.svg new file mode 100644 index 0000000..bdfd5e6 --- /dev/null +++ b/style/icons/map.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/marketplace.svg b/style/icons/marketplace.svg new file mode 100644 index 0000000..39c9578 --- /dev/null +++ b/style/icons/marketplace.svg @@ -0,0 +1,4 @@ + + + + diff --git a/style/icons/massage.svg b/style/icons/massage.svg new file mode 100644 index 0000000..dec15bf --- /dev/null +++ b/style/icons/massage.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/mast.svg b/style/icons/mast.svg new file mode 100644 index 0000000..faed669 --- /dev/null +++ b/style/icons/mast.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/mast_communications.svg b/style/icons/mast_communications.svg new file mode 100644 index 0000000..06c7b4e --- /dev/null +++ b/style/icons/mast_communications.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/mast_lighting.svg b/style/icons/mast_lighting.svg new file mode 100644 index 0000000..390bf43 --- /dev/null +++ b/style/icons/mast_lighting.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/medical_supply.svg b/style/icons/medical_supply.svg new file mode 100644 index 0000000..0e11675 --- /dev/null +++ b/style/icons/medical_supply.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/memorial.svg b/style/icons/memorial.svg new file mode 100644 index 0000000..dc93b2f --- /dev/null +++ b/style/icons/memorial.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/military_red_hatch.svg b/style/icons/military_red_hatch.svg new file mode 100644 index 0000000..e9f3846 --- /dev/null +++ b/style/icons/military_red_hatch.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/miniature_golf.svg b/style/icons/miniature_golf.svg new file mode 100644 index 0000000..ef3b95f --- /dev/null +++ b/style/icons/miniature_golf.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/style/icons/mobile_phone.svg b/style/icons/mobile_phone.svg new file mode 100644 index 0000000..98049ac --- /dev/null +++ b/style/icons/mobile_phone.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/monument.svg b/style/icons/monument.svg new file mode 100644 index 0000000..981b960 --- /dev/null +++ b/style/icons/monument.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/motel.svg b/style/icons/motel.svg new file mode 100644 index 0000000..0cfbd38 --- /dev/null +++ b/style/icons/motel.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/motorcycle.svg b/style/icons/motorcycle.svg new file mode 100644 index 0000000..009467c --- /dev/null +++ b/style/icons/motorcycle.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/motorcycle_barrier.svg b/style/icons/motorcycle_barrier.svg new file mode 100644 index 0000000..5ac858e --- /dev/null +++ b/style/icons/motorcycle_barrier.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/motorcycle_parking.svg b/style/icons/motorcycle_parking.svg new file mode 100644 index 0000000..4e111d4 --- /dev/null +++ b/style/icons/motorcycle_parking.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/multifaith.svg b/style/icons/multifaith.svg new file mode 100644 index 0000000..9181970 --- /dev/null +++ b/style/icons/multifaith.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/style/icons/museum.svg b/style/icons/museum.svg new file mode 100644 index 0000000..ac993f6 --- /dev/null +++ b/style/icons/museum.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/music.svg b/style/icons/music.svg new file mode 100644 index 0000000..4fe1327 --- /dev/null +++ b/style/icons/music.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/musical_instrument.svg b/style/icons/musical_instrument.svg new file mode 100644 index 0000000..3010f2e --- /dev/null +++ b/style/icons/musical_instrument.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/muslim.svg b/style/icons/muslim.svg new file mode 100644 index 0000000..7bc5aa0 --- /dev/null +++ b/style/icons/muslim.svg @@ -0,0 +1,20 @@ + + + + + + + diff --git a/style/icons/newsagent.svg b/style/icons/newsagent.svg new file mode 100644 index 0000000..db162cb --- /dev/null +++ b/style/icons/newsagent.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/nightclub.svg b/style/icons/nightclub.svg new file mode 100644 index 0000000..4fb44a9 --- /dev/null +++ b/style/icons/nightclub.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/obelisk.svg b/style/icons/obelisk.svg new file mode 100644 index 0000000..867620e --- /dev/null +++ b/style/icons/obelisk.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/office.svg b/style/icons/office.svg new file mode 100644 index 0000000..6031629 --- /dev/null +++ b/style/icons/office.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/oneway-path.svg b/style/icons/oneway-path.svg new file mode 100644 index 0000000..63b0537 --- /dev/null +++ b/style/icons/oneway-path.svg @@ -0,0 +1,31 @@ + + + + + + + image/svg+xml + + + + + + + diff --git a/style/icons/oneway-reverse.svg b/style/icons/oneway-reverse.svg new file mode 100644 index 0000000..7838527 --- /dev/null +++ b/style/icons/oneway-reverse.svg @@ -0,0 +1,32 @@ + + + + + + + + + image/svg+xml + + + + + + + diff --git a/style/icons/oneway.svg b/style/icons/oneway.svg new file mode 100644 index 0000000..1a37cca --- /dev/null +++ b/style/icons/oneway.svg @@ -0,0 +1,32 @@ + + + + + + + + + image/svg+xml + + + + + + + diff --git a/style/icons/optician.svg b/style/icons/optician.svg new file mode 100644 index 0000000..308f219 --- /dev/null +++ b/style/icons/optician.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/orchard.svg b/style/icons/orchard.svg new file mode 100644 index 0000000..a6f9558 --- /dev/null +++ b/style/icons/orchard.svg @@ -0,0 +1,4 @@ + + + + diff --git a/style/icons/outdoor.svg b/style/icons/outdoor.svg new file mode 100644 index 0000000..d98b343 --- /dev/null +++ b/style/icons/outdoor.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/outdoor_seating.svg b/style/icons/outdoor_seating.svg new file mode 100644 index 0000000..c0dd268 --- /dev/null +++ b/style/icons/outdoor_seating.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/paint.svg b/style/icons/paint.svg new file mode 100644 index 0000000..31653ee --- /dev/null +++ b/style/icons/paint.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/palace.svg b/style/icons/palace.svg new file mode 100644 index 0000000..96744d4 --- /dev/null +++ b/style/icons/palace.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/parking.svg b/style/icons/parking.svg new file mode 100644 index 0000000..dc019e3 --- /dev/null +++ b/style/icons/parking.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/parking_entrance_multistorey.svg b/style/icons/parking_entrance_multistorey.svg new file mode 100644 index 0000000..6e78485 --- /dev/null +++ b/style/icons/parking_entrance_multistorey.svg @@ -0,0 +1,4 @@ + + + + diff --git a/style/icons/parking_entrance_underground.svg b/style/icons/parking_entrance_underground.svg new file mode 100644 index 0000000..6d1ad65 --- /dev/null +++ b/style/icons/parking_entrance_underground.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/style/icons/parking_subtle.svg b/style/icons/parking_subtle.svg new file mode 100644 index 0000000..1d99510 --- /dev/null +++ b/style/icons/parking_subtle.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/parking_tickets.svg b/style/icons/parking_tickets.svg new file mode 100644 index 0000000..e07490f --- /dev/null +++ b/style/icons/parking_tickets.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/peak.svg b/style/icons/peak.svg new file mode 100644 index 0000000..b4b3695 --- /dev/null +++ b/style/icons/peak.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/perfumery.svg b/style/icons/perfumery.svg new file mode 100644 index 0000000..7237d5e --- /dev/null +++ b/style/icons/perfumery.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/pet.svg b/style/icons/pet.svg new file mode 100644 index 0000000..26ffc49 --- /dev/null +++ b/style/icons/pet.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/pharmacy.svg b/style/icons/pharmacy.svg new file mode 100644 index 0000000..5743568 --- /dev/null +++ b/style/icons/pharmacy.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/photo.svg b/style/icons/photo.svg new file mode 100644 index 0000000..bfc8a86 --- /dev/null +++ b/style/icons/photo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/picnic.svg b/style/icons/picnic.svg new file mode 100644 index 0000000..3ddc15a --- /dev/null +++ b/style/icons/picnic.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/place-4.svg b/style/icons/place-4.svg new file mode 100644 index 0000000..fdfb3dc --- /dev/null +++ b/style/icons/place-4.svg @@ -0,0 +1,30 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/style/icons/place-6.svg b/style/icons/place-6.svg new file mode 100644 index 0000000..bb0c967 --- /dev/null +++ b/style/icons/place-6.svg @@ -0,0 +1,30 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/style/icons/place-capital-6.svg b/style/icons/place-capital-6.svg new file mode 100644 index 0000000..8d81dc4 --- /dev/null +++ b/style/icons/place-capital-6.svg @@ -0,0 +1,13 @@ + + + + + diff --git a/style/icons/place-capital-8.svg b/style/icons/place-capital-8.svg new file mode 100644 index 0000000..42a9906 --- /dev/null +++ b/style/icons/place-capital-8.svg @@ -0,0 +1,13 @@ + + + + + diff --git a/style/icons/place_of_worship.svg b/style/icons/place_of_worship.svg new file mode 100644 index 0000000..81a8a64 --- /dev/null +++ b/style/icons/place_of_worship.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/plant_nursery.svg b/style/icons/plant_nursery.svg new file mode 100644 index 0000000..d8bbf38 --- /dev/null +++ b/style/icons/plant_nursery.svg @@ -0,0 +1,10 @@ + + + + diff --git a/style/icons/plaque.svg b/style/icons/plaque.svg new file mode 100644 index 0000000..74e8888 --- /dev/null +++ b/style/icons/plaque.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/playground.svg b/style/icons/playground.svg new file mode 100644 index 0000000..af629fa --- /dev/null +++ b/style/icons/playground.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/police.svg b/style/icons/police.svg new file mode 100644 index 0000000..06510fe --- /dev/null +++ b/style/icons/police.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/post_box.svg b/style/icons/post_box.svg new file mode 100644 index 0000000..54c2fe7 --- /dev/null +++ b/style/icons/post_box.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/post_office.svg b/style/icons/post_office.svg new file mode 100644 index 0000000..91da45b --- /dev/null +++ b/style/icons/post_office.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/power_tower.svg b/style/icons/power_tower.svg new file mode 100644 index 0000000..d1c1a29 --- /dev/null +++ b/style/icons/power_tower.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/style/icons/power_tower_small.svg b/style/icons/power_tower_small.svg new file mode 100644 index 0000000..d1084f0 --- /dev/null +++ b/style/icons/power_tower_small.svg @@ -0,0 +1,4 @@ + + + + diff --git a/style/icons/prison.svg b/style/icons/prison.svg new file mode 100644 index 0000000..c3bdbdc --- /dev/null +++ b/style/icons/prison.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/pub.svg b/style/icons/pub.svg new file mode 100644 index 0000000..a6311e5 --- /dev/null +++ b/style/icons/pub.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/public_bath.svg b/style/icons/public_bath.svg new file mode 100644 index 0000000..e551161 --- /dev/null +++ b/style/icons/public_bath.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/public_bookcase.svg b/style/icons/public_bookcase.svg new file mode 100644 index 0000000..22d08ca --- /dev/null +++ b/style/icons/public_bookcase.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/public_building.svg b/style/icons/public_building.svg new file mode 100644 index 0000000..cbc6356 --- /dev/null +++ b/style/icons/public_building.svg @@ -0,0 +1,56 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/style/icons/public_transport_tickets.svg b/style/icons/public_transport_tickets.svg new file mode 100644 index 0000000..b1f9d37 --- /dev/null +++ b/style/icons/public_transport_tickets.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/quarry.svg b/style/icons/quarry.svg new file mode 100644 index 0000000..96190fb --- /dev/null +++ b/style/icons/quarry.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/recycling.svg b/style/icons/recycling.svg new file mode 100644 index 0000000..4ece7f2 --- /dev/null +++ b/style/icons/recycling.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/rental_bicycle.svg b/style/icons/rental_bicycle.svg new file mode 100644 index 0000000..3a284c7 --- /dev/null +++ b/style/icons/rental_bicycle.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/rental_car.svg b/style/icons/rental_car.svg new file mode 100644 index 0000000..02afe8d --- /dev/null +++ b/style/icons/rental_car.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/restaurant.svg b/style/icons/restaurant.svg new file mode 100644 index 0000000..44b4928 --- /dev/null +++ b/style/icons/restaurant.svg @@ -0,0 +1,59 @@ + + diff --git a/style/icons/ridge-mid.svg b/style/icons/ridge-mid.svg new file mode 100644 index 0000000..7d94f14 --- /dev/null +++ b/style/icons/ridge-mid.svg @@ -0,0 +1,65 @@ + + + + + + + image/svg+xml + + + + + + + + + Created with Snap + diff --git a/style/icons/ridge2.svg b/style/icons/ridge2.svg new file mode 100644 index 0000000..417a61d --- /dev/null +++ b/style/icons/ridge2.svg @@ -0,0 +1,39 @@ + + + + + + image/svg+xml + + + + + + + + + Created with Snap + diff --git a/style/icons/road_motorway.svg b/style/icons/road_motorway.svg new file mode 100644 index 0000000..aefcbcf --- /dev/null +++ b/style/icons/road_motorway.svg @@ -0,0 +1,68 @@ + +image/svg+xml + + + + + + + diff --git a/style/icons/road_primary.svg b/style/icons/road_primary.svg new file mode 100644 index 0000000..4b2f110 --- /dev/null +++ b/style/icons/road_primary.svg @@ -0,0 +1,76 @@ + +image/svg+xml + + + + + + + diff --git a/style/icons/road_secondary.svg b/style/icons/road_secondary.svg new file mode 100644 index 0000000..4038659 --- /dev/null +++ b/style/icons/road_secondary.svg @@ -0,0 +1,76 @@ + +image/svg+xml + + + + + + + diff --git a/style/icons/road_tertiary.svg b/style/icons/road_tertiary.svg new file mode 100644 index 0000000..5a0de75 --- /dev/null +++ b/style/icons/road_tertiary.svg @@ -0,0 +1,76 @@ + +image/svg+xml + + + + + + + diff --git a/style/icons/rock_overlay.svg b/style/icons/rock_overlay.svg new file mode 100644 index 0000000..10d25b6 --- /dev/null +++ b/style/icons/rock_overlay.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/style/icons/saddle.svg b/style/icons/saddle.svg new file mode 100644 index 0000000..b46dc69 --- /dev/null +++ b/style/icons/saddle.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/salt_pond.svg b/style/icons/salt_pond.svg new file mode 100644 index 0000000..9718c32 --- /dev/null +++ b/style/icons/salt_pond.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/sauna.svg b/style/icons/sauna.svg new file mode 100644 index 0000000..efea151 --- /dev/null +++ b/style/icons/sauna.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/style/icons/scree_overlay.svg b/style/icons/scree_overlay.svg new file mode 100644 index 0000000..4ed9815 --- /dev/null +++ b/style/icons/scree_overlay.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/style/icons/scrub.svg b/style/icons/scrub.svg new file mode 100644 index 0000000..77674dc --- /dev/null +++ b/style/icons/scrub.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/style/icons/seafood.svg b/style/icons/seafood.svg new file mode 100644 index 0000000..08963c6 --- /dev/null +++ b/style/icons/seafood.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/second_hand.svg b/style/icons/second_hand.svg new file mode 100644 index 0000000..571a056 --- /dev/null +++ b/style/icons/second_hand.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/shelter.svg b/style/icons/shelter.svg new file mode 100644 index 0000000..122c52b --- /dev/null +++ b/style/icons/shelter.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/shintoist.svg b/style/icons/shintoist.svg new file mode 100644 index 0000000..77b2d0f --- /dev/null +++ b/style/icons/shintoist.svg @@ -0,0 +1,20 @@ + + + + + + + diff --git a/style/icons/shoes.svg b/style/icons/shoes.svg new file mode 100644 index 0000000..005a003 --- /dev/null +++ b/style/icons/shoes.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/shop.svg b/style/icons/shop.svg new file mode 100644 index 0000000..707fcdc --- /dev/null +++ b/style/icons/shop.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/style/icons/shower.svg b/style/icons/shower.svg new file mode 100644 index 0000000..ac80459 --- /dev/null +++ b/style/icons/shower.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style/icons/shrine.svg b/style/icons/shrine.svg new file mode 100644 index 0000000..f8e131e --- /dev/null +++ b/style/icons/shrine.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/sikhist.svg b/style/icons/sikhist.svg new file mode 100644 index 0000000..35524f2 --- /dev/null +++ b/style/icons/sikhist.svg @@ -0,0 +1,20 @@ + + + + + + + diff --git a/style/icons/slipway.svg b/style/icons/slipway.svg new file mode 100644 index 0000000..a76e861 --- /dev/null +++ b/style/icons/slipway.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/social_facility.svg b/style/icons/social_facility.svg new file mode 100644 index 0000000..c3800fa --- /dev/null +++ b/style/icons/social_facility.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/sports.svg b/style/icons/sports.svg new file mode 100644 index 0000000..1d889ce --- /dev/null +++ b/style/icons/sports.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/spring.svg b/style/icons/spring.svg new file mode 100644 index 0000000..0d6228d --- /dev/null +++ b/style/icons/spring.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/style/icons/square.svg b/style/icons/square.svg new file mode 100644 index 0000000..f06ea2b --- /dev/null +++ b/style/icons/square.svg @@ -0,0 +1,54 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/style/icons/square_train.svg b/style/icons/square_train.svg new file mode 100644 index 0000000..ab32852 --- /dev/null +++ b/style/icons/square_train.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/stationery.svg b/style/icons/stationery.svg new file mode 100644 index 0000000..12330a6 --- /dev/null +++ b/style/icons/stationery.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/statue.svg b/style/icons/statue.svg new file mode 100644 index 0000000..1963305 --- /dev/null +++ b/style/icons/statue.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/stile.svg b/style/icons/stile.svg new file mode 100644 index 0000000..c283723 --- /dev/null +++ b/style/icons/stile.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/stone.svg b/style/icons/stone.svg new file mode 100644 index 0000000..b1742c2 --- /dev/null +++ b/style/icons/stone.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/storage_tank.svg b/style/icons/storage_tank.svg new file mode 100644 index 0000000..075194f --- /dev/null +++ b/style/icons/storage_tank.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/supermarket.svg b/style/icons/supermarket.svg new file mode 100644 index 0000000..e7c2748 --- /dev/null +++ b/style/icons/supermarket.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/style/icons/taoist.svg b/style/icons/taoist.svg new file mode 100644 index 0000000..0f179fa --- /dev/null +++ b/style/icons/taoist.svg @@ -0,0 +1,20 @@ + + + + + + + diff --git a/style/icons/taxi.svg b/style/icons/taxi.svg new file mode 100644 index 0000000..c4a7eaf --- /dev/null +++ b/style/icons/taxi.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/tea.svg b/style/icons/tea.svg new file mode 100644 index 0000000..ca9b0b9 --- /dev/null +++ b/style/icons/tea.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/telephone.svg b/style/icons/telephone.svg new file mode 100644 index 0000000..aeacd07 --- /dev/null +++ b/style/icons/telephone.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/telescope_dish.svg b/style/icons/telescope_dish.svg new file mode 100644 index 0000000..47077f0 --- /dev/null +++ b/style/icons/telescope_dish.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/telescope_dome.svg b/style/icons/telescope_dome.svg new file mode 100644 index 0000000..b9ab9fe --- /dev/null +++ b/style/icons/telescope_dome.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/terminal.svg b/style/icons/terminal.svg new file mode 100644 index 0000000..52576ad --- /dev/null +++ b/style/icons/terminal.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/theatre.svg b/style/icons/theatre.svg new file mode 100644 index 0000000..9e2f517 --- /dev/null +++ b/style/icons/theatre.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/ticket.svg b/style/icons/ticket.svg new file mode 100644 index 0000000..dead6c3 --- /dev/null +++ b/style/icons/ticket.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/tobacco.svg b/style/icons/tobacco.svg new file mode 100644 index 0000000..ec0b808 --- /dev/null +++ b/style/icons/tobacco.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/toilets.svg b/style/icons/toilets.svg new file mode 100644 index 0000000..01daa6c --- /dev/null +++ b/style/icons/toilets.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/toll_booth.svg b/style/icons/toll_booth.svg new file mode 100644 index 0000000..b55b489 --- /dev/null +++ b/style/icons/toll_booth.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/tower_cantilever_communication.svg b/style/icons/tower_cantilever_communication.svg new file mode 100644 index 0000000..8fd6ce6 --- /dev/null +++ b/style/icons/tower_cantilever_communication.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/tower_cooling.svg b/style/icons/tower_cooling.svg new file mode 100644 index 0000000..d3b39d2 --- /dev/null +++ b/style/icons/tower_cooling.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/style/icons/tower_defensive.svg b/style/icons/tower_defensive.svg new file mode 100644 index 0000000..19eead1 --- /dev/null +++ b/style/icons/tower_defensive.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/tower_dish.svg b/style/icons/tower_dish.svg new file mode 100644 index 0000000..aabc27e --- /dev/null +++ b/style/icons/tower_dish.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/tower_dome.svg b/style/icons/tower_dome.svg new file mode 100644 index 0000000..72533f5 --- /dev/null +++ b/style/icons/tower_dome.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/style/icons/tower_generic.svg b/style/icons/tower_generic.svg new file mode 100644 index 0000000..4c6115f --- /dev/null +++ b/style/icons/tower_generic.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/tower_lattice.svg b/style/icons/tower_lattice.svg new file mode 100644 index 0000000..1b9a982 --- /dev/null +++ b/style/icons/tower_lattice.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/tower_lattice_communication.svg b/style/icons/tower_lattice_communication.svg new file mode 100644 index 0000000..ee52d2a --- /dev/null +++ b/style/icons/tower_lattice_communication.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/tower_lattice_lighting.svg b/style/icons/tower_lattice_lighting.svg new file mode 100644 index 0000000..55836c6 --- /dev/null +++ b/style/icons/tower_lattice_lighting.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/tower_lighting.svg b/style/icons/tower_lighting.svg new file mode 100644 index 0000000..4631101 --- /dev/null +++ b/style/icons/tower_lighting.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/tower_observation.svg b/style/icons/tower_observation.svg new file mode 100644 index 0000000..1c7ee24 --- /dev/null +++ b/style/icons/tower_observation.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/town_hall.svg b/style/icons/town_hall.svg new file mode 100644 index 0000000..085d53d --- /dev/null +++ b/style/icons/town_hall.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/townhall.svg b/style/icons/townhall.svg new file mode 100644 index 0000000..085d53d --- /dev/null +++ b/style/icons/townhall.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/toys.svg b/style/icons/toys.svg new file mode 100644 index 0000000..f75b35d --- /dev/null +++ b/style/icons/toys.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/trade.svg b/style/icons/trade.svg new file mode 100644 index 0000000..675673a --- /dev/null +++ b/style/icons/trade.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/traffic_light.13.svg b/style/icons/traffic_light.13.svg new file mode 100644 index 0000000..a02d2b3 --- /dev/null +++ b/style/icons/traffic_light.13.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/travel_agency.svg b/style/icons/travel_agency.svg new file mode 100644 index 0000000..7947e31 --- /dev/null +++ b/style/icons/travel_agency.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/tyres.svg b/style/icons/tyres.svg new file mode 100644 index 0000000..2dac756 --- /dev/null +++ b/style/icons/tyres.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/variety_store.svg b/style/icons/variety_store.svg new file mode 100644 index 0000000..f97cc77 --- /dev/null +++ b/style/icons/variety_store.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/vehicle_inspection.svg b/style/icons/vehicle_inspection.svg new file mode 100644 index 0000000..67ada97 --- /dev/null +++ b/style/icons/vehicle_inspection.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/veterinary.svg b/style/icons/veterinary.svg new file mode 100644 index 0000000..350c633 --- /dev/null +++ b/style/icons/veterinary.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/video.svg b/style/icons/video.svg new file mode 100644 index 0000000..30943b5 --- /dev/null +++ b/style/icons/video.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/video_games.svg b/style/icons/video_games.svg new file mode 100644 index 0000000..9ed702d --- /dev/null +++ b/style/icons/video_games.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/style/icons/viewpoint.svg b/style/icons/viewpoint.svg new file mode 100644 index 0000000..9fef5e4 --- /dev/null +++ b/style/icons/viewpoint.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/vineyard.svg b/style/icons/vineyard.svg new file mode 100644 index 0000000..26907e1 --- /dev/null +++ b/style/icons/vineyard.svg @@ -0,0 +1,12 @@ + + + + diff --git a/style/icons/volcano.svg b/style/icons/volcano.svg new file mode 100644 index 0000000..ca1a721 --- /dev/null +++ b/style/icons/volcano.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/waste_basket.svg b/style/icons/waste_basket.svg new file mode 100644 index 0000000..5dd1e36 --- /dev/null +++ b/style/icons/waste_basket.svg @@ -0,0 +1,74 @@ + + diff --git a/style/icons/waste_basket_with_excrement_bags.svg b/style/icons/waste_basket_with_excrement_bags.svg new file mode 100644 index 0000000..caee54e --- /dev/null +++ b/style/icons/waste_basket_with_excrement_bags.svg @@ -0,0 +1,83 @@ + + diff --git a/style/icons/waste_disposal.svg b/style/icons/waste_disposal.svg new file mode 100644 index 0000000..9901de6 --- /dev/null +++ b/style/icons/waste_disposal.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/water_park.svg b/style/icons/water_park.svg new file mode 100644 index 0000000..14a9067 --- /dev/null +++ b/style/icons/water_park.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/water_tower.svg b/style/icons/water_tower.svg new file mode 100644 index 0000000..837c7bf --- /dev/null +++ b/style/icons/water_tower.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/waterfall.svg b/style/icons/waterfall.svg new file mode 100644 index 0000000..b4e31cb --- /dev/null +++ b/style/icons/waterfall.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/wetland.svg b/style/icons/wetland.svg new file mode 100644 index 0000000..018e2a5 --- /dev/null +++ b/style/icons/wetland.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/style/icons/wetland_bog.svg b/style/icons/wetland_bog.svg new file mode 100644 index 0000000..afe15c3 --- /dev/null +++ b/style/icons/wetland_bog.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/style/icons/wetland_mangrove.svg b/style/icons/wetland_mangrove.svg new file mode 100644 index 0000000..4b6b673 --- /dev/null +++ b/style/icons/wetland_mangrove.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/style/icons/wetland_marsh.svg b/style/icons/wetland_marsh.svg new file mode 100644 index 0000000..76b5b96 --- /dev/null +++ b/style/icons/wetland_marsh.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/style/icons/wetland_reed.svg b/style/icons/wetland_reed.svg new file mode 100644 index 0000000..d61fbc8 --- /dev/null +++ b/style/icons/wetland_reed.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/style/icons/wetland_swamp.svg b/style/icons/wetland_swamp.svg new file mode 100644 index 0000000..32013dd --- /dev/null +++ b/style/icons/wetland_swamp.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/style/icons/wilderness_hut.svg b/style/icons/wilderness_hut.svg new file mode 100644 index 0000000..afe9110 --- /dev/null +++ b/style/icons/wilderness_hut.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/icons/windmill.svg b/style/icons/windmill.svg new file mode 100644 index 0000000..f01fdbe --- /dev/null +++ b/style/icons/windmill.svg @@ -0,0 +1,3 @@ + + + diff --git a/style/style-header.json b/style/style-header.json new file mode 100644 index 0000000..ba3ec2d --- /dev/null +++ b/style/style-header.json @@ -0,0 +1,36 @@ +{ + "version": 8, + "name": "OSM OpenMapTiles", + "id": "openmaptiles", + "center": [ + 0, + 0 + ], + "zoom": 1, + "bearing": 0, + "pitch": 0, + "sources": { + "openmaptiles": { + "type": "vector", + "url": "mbtiles:///data/tiles.mbtiles" + }, + "attribution": { + "attribution": "© OpenMapTiles © OpenStreetMap contributors", + "type": "vector" + } + }, + "glyphs": "{fontstack}/{range}.pbf", + "sprite": "sprite", + "layers": [ + { + "id": "background", + "type": "background", + "layout": { + "visibility": "visible" + }, + "paint": { + "background-color": "#f2efe9" + } + } + ] +} diff --git a/tests/changes.repl.json b/tests/changes.repl.json new file mode 100644 index 0000000..7805b99 --- /dev/null +++ b/tests/changes.repl.json @@ -0,0 +1,4 @@ +{ + "replication_interval": "24h", + "replication_url": "dummy" +} diff --git a/tests/changes.state.txt b/tests/changes.state.txt new file mode 100644 index 0000000..630648a --- /dev/null +++ b/tests/changes.state.txt @@ -0,0 +1,3 @@ +#Sat Sep 25 23:23:00 UTC 2021 +sequenceNumber=4730693 +timestamp=2021-09-25T23\:22\:58Z diff --git a/tests/import/100_import-large-park.osm b/tests/import/100_import-large-park.osm new file mode 100644 index 0000000..79740b4 --- /dev/null +++ b/tests/import/100_import-large-park.osm @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/import/200_import-aerodrome.osm b/tests/import/200_import-aerodrome.osm new file mode 100644 index 0000000..0667a36 --- /dev/null +++ b/tests/import/200_import-aerodrome.osm @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/import/300_import-landcover.osm b/tests/import/300_import-landcover.osm new file mode 100644 index 0000000..19cf6db --- /dev/null +++ b/tests/import/300_import-landcover.osm @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/tests/import/400_import-boundary.osm b/tests/import/400_import-boundary.osm new file mode 100644 index 0000000..fda6bee --- /dev/null +++ b/tests/import/400_import-boundary.osm @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/import/500_import-highway.osm b/tests/import/500_import-highway.osm new file mode 100644 index 0000000..a3c8915 --- /dev/null +++ b/tests/import/500_import-highway.osm @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/import/600_import-poi.osm b/tests/import/600_import-poi.osm new file mode 100644 index 0000000..17d2ffe --- /dev/null +++ b/tests/import/600_import-poi.osm @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/last.state.txt b/tests/last.state.txt new file mode 100644 index 0000000..a2a0c28 --- /dev/null +++ b/tests/last.state.txt @@ -0,0 +1,3 @@ +#Sat Sep 25 23:23:00 UTC 2021 +sequenceNumber=4730692 +timestamp=2021-09-25T23\:21\:58Z diff --git a/tests/test-post-import.sql b/tests/test-post-import.sql new file mode 100644 index 0000000..3a06f5f --- /dev/null +++ b/tests/test-post-import.sql @@ -0,0 +1,229 @@ +-- Store test results + +DROP TABLE IF EXISTS omt_test_failures; +CREATE TABLE omt_test_failures( + test_id integer, + test_type varchar(6), + error_message text +); + +-- Checks to ensure that test data was imported correctly +DO $$ + +DECLARE + cnt integer; + +BEGIN + + -- Test 100 + SELECT COUNT(*) INTO cnt FROM osm_park_polygon; + IF cnt <> 3 THEN + INSERT INTO omt_test_failures VALUES(100, 'import', 'osm_park_polygon expected 3, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_park_polygon_gen_z5; + IF cnt <> 3 THEN + INSERT INTO omt_test_failures VALUES(100, 'import', 'osm_park_polygon_gen_z5 expected 3, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_park_polygon_gen_z5 WHERE leisure='nature_reserve'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(100, 'import', 'osm_park_polygon_gen_z5 nature_reserve expected 1, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_park_polygon_gen_z5 WHERE boundary='protected_area'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(100, 'import', 'osm_park_polygon_gen_z5 protected_area expected 1, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_park_polygon_gen_z5 WHERE boundary='national_park'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(100, 'import', 'osm_park_polygon_gen_z5 national_park expected 1, got ' || cnt); + END IF; + + -- Test 200 + SELECT COUNT(*) INTO cnt FROM osm_aerodrome_label_point; + IF cnt <> 3 THEN + INSERT INTO omt_test_failures VALUES(200, 'import', 'osm_aerodrome_label expected 3, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_aerodrome_label_point WHERE ele='123'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(200, 'import', 'osm_aerodrome_label ele=123 expected 1, got ' || cnt); + END IF; + + -- Test 300 + SELECT COUNT(*) INTO cnt FROM osm_landcover_polygon WHERE mapping_key='natural' AND subclass='wood'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(300, 'import', 'osm_landcover_polygon natural=wood expected 1, got ' || cnt); + END IF; + + -- Test 400 + SELECT COUNT(DISTINCT relation_id) INTO cnt FROM osm_border_linestring WHERE admin_level=8; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(400, 'import', 'osm_border_linestring city count expected 1, got ' || cnt); + END IF; + + SELECT COUNT(DISTINCT relation_id) INTO cnt FROM osm_border_linestring WHERE admin_level=2; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(400, 'import', 'osm_border_linestring country count expected 1, got ' || cnt); + END IF; + + -- Test 500 + + -- Verify that road classifications show up at the right zoom level + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z4 WHERE osm_national_network(network); + IF cnt < 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z4 national network count expected >=1, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z4 WHERE NOT osm_national_network(network); + IF cnt <> 0 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z4 not national network count expected 0, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z5 WHERE highway='motorway'; + IF cnt < 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z5 motorway count expected >=1, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z5 WHERE highway='trunk' AND osm_national_network(network); + IF cnt < 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z5 trunk and national network count expected >=1, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z6 WHERE highway='primary'; + IF cnt <> 0 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z6 primary count expected 0, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z7 WHERE highway='primary'; + IF cnt < 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z7 primary count expected >=1, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z8 WHERE highway='secondary'; + IF cnt <> 0 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z8 secondary count expected 0, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z9 WHERE highway='secondary'; + IF cnt < 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z9 secondary count expected >=1, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z10 WHERE highway='tertiary'; + IF cnt <> 0 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z10 tertiary count expected 0, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z11 WHERE highway='tertiary'; + IF cnt < 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z11 tertiary count expected >=1, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z11 WHERE highway IN ('service', 'track'); + IF cnt <> 0 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z11 minor road count expected 0, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z9 + WHERE is_bridge = TRUE + AND toll = TRUE + AND layer = 1 + AND bicycle = 'no' + AND foot = 'no' + AND horse = 'no'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z9 import tags expected 1, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z9 + WHERE highway = 'trunk' + AND expressway = TRUE; + IF cnt < 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z9 import expressway expected >=1, got ' || cnt); + END IF; + + -- Same-named road split into 3 parts, because the middle segment is tagged toll=yes + SELECT COUNT(*) INTO cnt FROM osm_transportation_name_linestring WHERE tags->'name' = 'OpenMapTiles Secondary 3'; + IF cnt <> 2 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring split road count expected 2, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_name_linestring + WHERE tags->'name' = 'OpenMapTiles Path z13' + AND route_rank = 2; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_name_linestring z13 route_rank expected 1, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_name_linestring + WHERE tags->'name' = 'OpenMapTiles Track z12' + AND route_rank = 1; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_name_linestring z12 route_rank expected 1, got ' || cnt); + END IF; + + -- Duplicate route concurrencies collapsed + SELECT COUNT(*) INTO cnt FROM transportation_route_member_coalesced + WHERE network='US:I' AND ref='95'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'transportation_route_member_coalesced 1 route membership expected, got ' || cnt); + END IF; + + -- Test 600 + + -- verify that atms are imported with correct name which can come from tags like operator or network + SELECT COUNT(*) INTO cnt FROM osm_poi_point + WHERE subclass = 'atm' + AND tags->'name' = 'OpenMapTiles ATM'; + IF cnt <> 3 THEN + INSERT INTO omt_test_failures VALUES(600, 'import', 'osm_poi_point atm with name "OpenMapTiles ATM" expected 3, got ' || cnt); + END IF; + + -- verify that parcel lockers are imported with correct name which can come from tags like brand or operator and can contain ref + SELECT COUNT(*) INTO cnt FROM osm_poi_point + WHERE subclass = 'parcel_locker' + AND tags->'name' like 'OpenMapTiles Parcel Locker%'; + IF cnt <> 3 THEN + INSERT INTO omt_test_failures VALUES(600, 'import', 'osm_poi_point parcel_locker with name like "OpenMapTiles Parcel Locker%" expected 3, got ' || cnt); + END IF; + SELECT COUNT(*) INTO cnt FROM osm_poi_point + WHERE subclass = 'parcel_locker' + AND tags->'name' like 'OpenMapTiles Parcel Locker PL00%'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(600, 'import', 'osm_poi_point parcel_locker with name like "OpenMapTiles Parcel Locker PL00%" expected 1, got ' || cnt); + END IF; + + -- verify that charging stations are imported with correct name which can come from tags like brand or operator and can contain ref + SELECT COUNT(*) INTO cnt FROM osm_poi_point + WHERE subclass = 'charging_station' + AND tags->'name' = 'OpenMapTiles Charging Station'; + IF cnt <> 2 THEN + INSERT INTO omt_test_failures VALUES(600, 'import', 'osm_poi_point charging_station with name "OpenMapTiles Charging Station" expected 2, got ' || cnt); + END IF; + SELECT COUNT(*) INTO cnt FROM osm_poi_polygon + WHERE subclass = 'charging_station' + AND tags->'name' = 'OpenMapTiles Charging Station Brand'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(600, 'import', 'osm_poi_polygon charging_station with name "OpenMapTiles Charging Station Brand" expected 1, got ' || cnt); + END IF; + +END; + +$$ +LANGUAGE plpgsql; + +DO $$ + +DECLARE + cnt integer; +BEGIN + SELECT COUNT(*) INTO cnt FROM omt_test_failures; + IF cnt > 0 THEN + RAISE '% unit test(s) failed on imports. Details can be found in table omt_test_failures.', cnt USING ERRCODE = '0Z000'; + END IF; +END; + +$$; diff --git a/tests/test-post-update.sql b/tests/test-post-update.sql new file mode 100644 index 0000000..d712ac0 --- /dev/null +++ b/tests/test-post-update.sql @@ -0,0 +1,146 @@ +-- Checks to ensure that test data was imported correctly +DO $$ + +DECLARE + cnt integer; + +BEGIN + + -- Clear prior results + DELETE FROM omt_test_failures WHERE test_type='update'; + + -- Test 100: Verify re-tag of national_park to protected_area worked + SELECT COUNT(*) INTO cnt FROM osm_park_polygon_gen_z5 WHERE boundary='national_park'; + IF cnt <> 0 THEN + INSERT INTO omt_test_failures VALUES(100, 'update', 'osm_park_polygon_gen_z5 national_park expected 0, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_park_polygon_gen_z5 WHERE boundary='protected_area'; + IF cnt <> 2 THEN + INSERT INTO omt_test_failures VALUES(100, 'update', 'osm_park_polygon_gen_z5 protected_area expected 2, got ' || cnt); + END IF; + + -- Test 200: Verify aerodrome deleted and modified + SELECT COUNT(*) INTO cnt FROM osm_aerodrome_label_point; + IF cnt <> 2 THEN + INSERT INTO omt_test_failures VALUES(200, 'update', 'osm_aerodrome_label_point expected 2, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_aerodrome_label_point WHERE icao='KOMT' AND ele='124' AND name='OpenMapTiles International Airport'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(200, 'update', 'osm_aerodrome_label_point failed to update attributes'); + END IF; + + -- Test 300: Verify landuse modified + SELECT COUNT(*) INTO cnt FROM osm_landcover_polygon WHERE mapping_key='natural' AND subclass='scrub'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(300, 'update', 'osm_landcover_polygon natural=scrub expected 1, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_landcover_polygon WHERE mapping_key='natural' AND subclass='wood'; + IF cnt <> 0 THEN + INSERT INTO omt_test_failures VALUES(300, 'update', 'osm_landcover_polygon natural=wood expected 0, got ' || cnt); + END IF; + + -- Test 400: Verify new city added + SELECT COUNT(DISTINCT relation_id) INTO cnt FROM osm_border_linestring WHERE admin_level=8; + IF cnt <> 2 THEN + INSERT INTO omt_test_failures VALUES(400, 'update', 'osm_border_linestring city count expected 2, got ' || cnt); + END IF; + + -- Test 500: Highways + -- Same-named road previous split into 3 parts, now merged because the middle segment had toll=yes removed + SELECT COUNT(*) INTO cnt FROM osm_transportation_name_linestring WHERE tags->'name' = 'OpenMapTiles Secondary 3'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'update', 'osm_transportation_linestring unsplit road count expected 1, got ' || cnt); + END IF; + + -- Verify expressway tag updated + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z9 + WHERE highway = 'primary' + AND expressway = TRUE; + IF cnt < 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z9 update expressway expected >=1, got ' || cnt); + END IF; + + -- Verify tags changed + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z9 + WHERE is_tunnel = TRUE + AND is_bridge = FALSE + AND toll = FALSE + AND layer = -1 + AND bicycle = 'yes' + AND foot = 'yes' + AND horse = 'yes'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'update', 'osm_transportation_linestring z9 update tags expected 1, got ' || cnt); + END IF; + + -- Test 600 + + -- check if name was applied correctly + -- for atm + SELECT COUNT(*) INTO cnt FROM osm_poi_point + WHERE subclass = 'atm' + AND tags->'name' = 'OpenMapTiles ATM'; + IF cnt <> 2 THEN + INSERT INTO omt_test_failures VALUES(600, 'update', 'osm_poi_point atm with name "OpenMapTiles ATM" expected 2, got ' || cnt); + END IF; + SELECT COUNT(*) INTO cnt FROM osm_poi_point + WHERE subclass = 'atm' + AND tags->'name' = 'New name'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(600, 'update', 'osm_poi_point atm with name "New name" expected 1, got ' || cnt); + END IF; + + -- for parcel_locker + SELECT COUNT(*) INTO cnt FROM osm_poi_point + WHERE subclass = 'parcel_locker' + AND tags->'name' like 'OpenMapTiles Parcel Locker%'; + IF cnt <> 2 THEN + INSERT INTO omt_test_failures VALUES(600, 'update', 'osm_poi_point atm with name "OpenMapTiles Parcel Locker%" expected 2, got ' || cnt); + END IF; + SELECT COUNT(*) INTO cnt FROM osm_poi_point + WHERE subclass = 'parcel_locker' + AND tags->'name' = 'Different operator PL001'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(600, 'update', 'osm_poi_point parcel_locker with name "Different operator PL001" expected 1, got ' || cnt); + END IF; + + -- for charging_station + SELECT COUNT(*) INTO cnt FROM osm_poi_point + WHERE subclass = 'charging_station' + AND tags->'name' = 'OpenMapTiles Charging Station'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(600, 'update', 'osm_poi_point charging_station with name "OpenMapTiles Charging Station" expected 1, got ' || cnt); + END IF; + SELECT COUNT(*) INTO cnt FROM osm_poi_point + WHERE subclass = 'charging_station' + AND tags->'name' = 'OpenMapTiles Charging Station Brand'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(600, 'update', 'osm_poi_point charging_station with name "OpenMapTiles Charging Station Brand" expected 1, got ' || cnt); + END IF; + SELECT COUNT(*) INTO cnt FROM osm_poi_polygon + WHERE subclass = 'charging_station' + AND tags->'name' = 'OpenMapTiles Charging Station'; + IF cnt <> 1 THEN + INSERT INTO omt_test_failures VALUES(600, 'update', 'osm_poi_polygon charging_station with name "OpenMapTiles Charging Station" expected 1, got ' || cnt); + END IF; + +END; + +$$; + + +DO $$ + +DECLARE + cnt integer; +BEGIN + SELECT COUNT(*) INTO cnt FROM omt_test_failures; + IF cnt > 0 THEN + RAISE '% unit test(s) failed on updates. Details can be found in table omt_test_failures.', cnt USING ERRCODE = '0Z000'; + END IF; +END; + +$$; diff --git a/tests/update/100_update-large-park.osc b/tests/update/100_update-large-park.osc new file mode 100644 index 0000000..ca67695 --- /dev/null +++ b/tests/update/100_update-large-park.osc @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/update/200_update-aerodrome.osc b/tests/update/200_update-aerodrome.osc new file mode 100644 index 0000000..c6fc222 --- /dev/null +++ b/tests/update/200_update-aerodrome.osc @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/tests/update/300_update-landcover.osc b/tests/update/300_update-landcover.osc new file mode 100644 index 0000000..4f3fe15 --- /dev/null +++ b/tests/update/300_update-landcover.osc @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/tests/update/400_update-boundary.osc b/tests/update/400_update-boundary.osc new file mode 100644 index 0000000..9d7e99c --- /dev/null +++ b/tests/update/400_update-boundary.osc @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/update/500_update-highway.osc b/tests/update/500_update-highway.osc new file mode 100644 index 0000000..f564517 --- /dev/null +++ b/tests/update/500_update-highway.osc @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/update/600_update-poi.osc b/tests/update/600_update-poi.osc new file mode 100644 index 0000000..2dfed16 --- /dev/null +++ b/tests/update/600_update-poi.osc @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +