diff --git a/.env b/.env index 9411f8f..55c3f32 100644 --- a/.env +++ b/.env @@ -3,8 +3,8 @@ # Layers definition and meta data TILESET_FILE=openmaptiles.yaml -# Use 3-part patch version to ignore patch updates, e.g. 5.0.0 -TOOLS_VERSION=5.3 +# Use 3-part patch version to ignore patch updates, e.g. 7.0.0 +TOOLS_VERSION=7.0 # Make sure these values are in sync with the ones in .env-postgres file PGDATABASE=openmaptiles @@ -20,16 +20,17 @@ PGPORT=5432 BBOX=4.827919,50.740472,5.798035,51.095291 -# Which zooms to generate in make generate-tiles +# Which zooms to generate with make generate-tiles-pg MIN_ZOOM=0 MAX_ZOOM=14 +# `MID_ZOOM` setting only works with `make generate-tiles-pg` command. Make sure MID_ZOOM < MAX_ZOOM. +# See https://github.com/openmaptiles/openmaptiles-tools/pull/383 +# MID_ZOOM=11 + # Use true (case sensitive) to allow data updates DIFF_MODE=false -# Hide some output from Mapnik tile generation for clarity -FILTER_MAPNIK_OUTPUT=1 - # Some area data like openstreetmap.fr can contain invalid references # that must be cleaned up before using it for borders -- set it to true. BORDERS_CLEANUP=false @@ -69,7 +70,5 @@ COPY_CONCURRENCY=1024 UV_THREADPOOL_SIZE=16 #UV_THREADPOOL_SIZE=24 -# Variables for generate tiles using PGquery +# Variables for generate tiles using tilelive-pgquery PGHOSTS_LIST= -NO_GZIP=1 -USE_KEY_COLUMN=1 diff --git a/.env-postgres b/.env-postgres deleted file mode 100644 index 692e16e..0000000 --- a/.env-postgres +++ /dev/null @@ -1,8 +0,0 @@ -# This file defines environment variables for the PostgreSQL image. -# The main docker PostgreSQL image requires these vars rather than -# the standard PG* ones that all PostgreSQL tools use. - -# Make sure these values are in sync with the ones in .env file -POSTGRES_DB=openmaptiles -POSTGRES_USER=openmaptiles -POSTGRES_PASSWORD=openmaptiles diff --git a/.github/workflows/integrity.yml b/.github/workflows/integrity.yml new file mode 100644 index 0000000..5ba9503 --- /dev/null +++ b/.github/workflows/integrity.yml @@ -0,0 +1,71 @@ +# Workflow to run basic integrity checks on OMT`s new Pull Requests and commits pushed into OMT repo + +name: OpenMapTiles Integrity CI + +on: + push: + branches: [ master, master-tools ] + pull_request: + +jobs: + + integrity_test: + name: Run integrity test + runs-on: ubuntu-latest + steps: + + - name: Checkout the changes + uses: actions/checkout@v2 + + - name: Run quickstart for a small area + env: + area: monaco + QUIET: 1 + run: | + echo MIN_ZOOM=0 >> .env + echo MAX_ZOOM=14 >> .env + ./quickstart.sh $area + + - name: Save quickstart.log + uses: actions/upload-artifact@v2 + with: + name: quickstart.log + path: quickstart.log + + - name: Test etldoc images + run: | + export TEST_MODE=yes + make generate-devdoc + + - name: Run quickstart and update in DIFF mode + env: + area: europe/monaco + QUIET: 1 + run: | + echo MIN_ZOOM=0 >> .env + echo MAX_ZOOM=14 >> .env + echo DIFF_MODE=true >> .env + # Cleanup + rm -fr data build cache + # Create data/$area.repl.json + make download-geofabrik area=$area + # Download 2+ month old data + export old_date=$(date --date="$(date +%Y-%m-15) -2 month" +'%y%m01') + echo Downloading $old_date extract of $area + docker-compose run --rm --user=$(id -u):$(id -g) openmaptiles-tools sh -c "wget -O data/$area.osm.pbf http://download.geofabrik.de/$area-$old_date.osm.pbf" + # Initial import and tile generation + ./quickstart.sh $area + sleep 2 + echo Downloading updates + # Loop to recover from potential "ERROR 429: Too Many Requests" + docker-compose run --rm --user=$(id -u):$(id -g) openmaptiles-tools sh -c " + while ! osmupdate --keep-tempfiles --base-url=$(sed -n 's/ *\"replication_url\": //p' data/$area.repl.json) data/$area.osm.pbf data/changes.osc.gz ; do + sleep 2; + echo Sleeping...; + sleep 630; + done" + echo Downloading updates completed + echo Importing updates + make import-diff + echo Generating new tiles + make generate-tiles-pg diff --git a/.github/workflows/tests.yml b/.github/workflows/performance.yml similarity index 89% rename from .github/workflows/tests.yml rename to .github/workflows/performance.yml index 9eb8a73..3266dbd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/performance.yml @@ -1,6 +1,6 @@ -# Workflow to validate OMT`s new Pull Requests and commits pushed into OMT repo +# Workflow to run performance tests OMT`s new Pull Requests and commits pushed into OMT repo -name: OpenMapTiles CI +name: OpenMapTiles Performance CI on: push: @@ -8,35 +8,6 @@ on: pull_request: jobs: - - integrity_test: - name: Run integrity test - runs-on: ubuntu-latest - steps: - - - name: Checkout the changes - uses: actions/checkout@v2 - - - name: Run quickstart for a small area - env: - area: monaco - MIN_ZOOM: 0 - MAX_ZOOM: 14 - QUIET: 1 - run: | - ./quickstart.sh $area - - - name: Save quickstart.log - uses: actions/upload-artifact@v1 - with: - name: quickstart.log - path: quickstart.log - - - name: Test etldoc images - run: | - export TEST_MODE=yes - make generate-devdoc - performance: name: Evaluate performance runs-on: self-hosted @@ -63,9 +34,18 @@ jobs: # TEST_DATA_URL: "https://drive.google.com/uc?export=download&id=1kw7XPDPd1Rc-Zi2XxGLTXdinUSq-S4pT" # TEST_PERF_PARAMS: "--minzoom 0 --maxzoom 14 --test hungary --test isle-of-man" steps: + - name: Cleanup workdir + id: cleanup + run: | + set -euo pipefail + pwd + ls -al . + shopt -s dotglob + rm -rf * + - name: Cache test data download id: cache-testdata - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ci_cache key: "v2-${{ env.TEST_DATA_URL }}" @@ -99,11 +79,11 @@ jobs: echo "::set-output name=hash::$REV_HASH" - name: Set up caching for the performance results - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: perf_cache # If profiling result cache has incompatible format, increase this "v" number - key: "v12-${{ steps.calc.outputs.hash }}-${{ env.TEST_DATA_URL }}" + key: "v13-${{ steps.calc.outputs.hash }}-${{ env.TEST_DATA_URL }}" - name: Load test data into DB and run performance test id: main @@ -173,27 +153,23 @@ jobs: make start-db profile 1_data make import-data profile 2_osm make import-osm - profile 3_borders make import-borders if [ -f ../ci_cache/wikidata-cache.json ]; then cp ../ci_cache/wikidata-cache.json cache/wikidata-cache.json fi - profile 4_wikidata make import-wikidata - profile 5_sql make import-sql + profile 3_wikidata make import-wikidata + profile 4_sql make import-sql # Get database total size, in MB # Once Makefile has a few more improvements, we can use this approach instead: # echo $'\\set QUIET on \\a \\x off \\t \\\\ select pg_database_size(current_database())/1024/1024;' | make -s psql - if grep -qE '^ import-osm:$' docker-compose.yml; then - # old version using dedicated import-osm docker image - DB_SIZE_MB=$(docker-compose run --rm -u $(id -u):$(id -g) import-osm ./psql.sh -qtAc 'select pg_database_size(current_database())/1024/1024;') - else - DB_SIZE_MB=$(docker-compose run --rm -u $(id -u):$(id -g) openmaptiles-tools psql.sh -qtAc 'select pg_database_size(current_database())/1024/1024;') - fi + DB_SIZE_MB=$(docker-compose run --rm -u $(id -u):$(id -g) openmaptiles-tools psql.sh -qtAc 'select pg_database_size(current_database())/1024/1024;') docker-compose run --rm -u $(id -u):$(id -g) openmaptiles-tools pg_dump --schema-only > "${PROFILE_DIR}/schema.sql" echo "$DB_SIZE_MB" > "${PROFILE_DIR}/db_size.tsv" } + echo "Ensuring we have the needed dirs" + pwd mkdir -p perf_cache mkdir -p artifacts mkdir -p pr_message @@ -207,6 +183,7 @@ jobs: git reset --hard ${CURRENT_SHA}^1 fi + docker-compose pull PROFILE_DIR=../perf_cache create_db if [ ! -f ../ci_cache/wikidata-cache.json ]; then @@ -216,6 +193,8 @@ jobs: (set -x; profile test-perf docker-compose run --rm -T openmaptiles-tools \ test-perf openmaptiles.yaml $TEST_PERF_PARAMS \ --record /tileset/results.json) + echo "Done generating base perf results, moving them to ../perf_cache" + pwd mv results.json ../perf_cache if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then @@ -226,7 +205,10 @@ jobs: echo "Found cached performance results" fi + docker-compose pull pushd ../perf_cache + echo "Should be in perf_cache" + pwd if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then cp results.json ../artifacts/base-results.json # Copy all tsv files, not just the ones with "profile-" prefix. @@ -246,6 +228,8 @@ jobs: PROFILE_DIR=../artifacts create_db + echo "Copying existing perf_cache results to current dir" + pwd cp ../perf_cache/results.json . OUTPUT="$(set -x; profile test-perf docker-compose run --rm -T openmaptiles-tools \ test-perf openmaptiles.yaml $TEST_PERF_PARAMS \ @@ -303,14 +287,14 @@ jobs: fi - name: Save performance artifacts - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2 with: name: performance_results path: artifacts - name: Save PR message artifact if: github.event_name == 'pull_request' - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2 with: name: pr_message path: pr_message diff --git a/.github/workflows/pr-updater.yml b/.github/workflows/pr-updater.yml index d309e0e..c116400 100644 --- a/.github/workflows/pr-updater.yml +++ b/.github/workflows/pr-updater.yml @@ -1,21 +1,20 @@ name: Update PR comments on: - # This number should correspond to the IGNORE_RUNS_OLDER_THAN value below. - # When setting up for the first time, use "on: push" instead of "on: schedule" - # and set IGNORE_RUNS_OLDER_THAN to a very high number until it runs once. - schedule: - - cron: '*/6 * * * *' + workflow_run: + workflows: ["OpenMapTiles Performance CI"] + types: [completed] jobs: update_PRs: runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: main env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - WORKFLOW_NAME: "OpenMapTiles CI" + WORKFLOW_NAME: "OpenMapTiles Performance CI" # the name of the artifact whose content comment published by PR. Must have a single markdown file inside. MSG_ARTIFACT_NAME: "pr_message" # How far back to look for finished runs, in minutes. diff --git a/.github/workflows/sql-tests.yml b/.github/workflows/sql-tests.yml new file mode 100644 index 0000000..b301f05 --- /dev/null +++ b/.github/workflows/sql-tests.yml @@ -0,0 +1,22 @@ +# Workflow to run unit tests on OMT`s new Pull Requests and commits pushed into OMT repo + +name: OpenMapTiles SQL Test CI + +on: + push: + branches: [ master, master-tools ] + pull_request: + +jobs: + + unit_tests: + name: Run unit test + runs-on: ubuntu-latest + steps: + + - name: Checkout the changes + uses: actions/checkout@v2 + + - name: Run unit tests + run: | + make clean && make test-sql diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 535f3c1..18d75c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # 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://osmus-slack.herokuapp.com/)). +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. @@ -41,3 +41,48 @@ When you modify import data rules in `mapping.yaml` or `*.sql`, please update: 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/LICENSE.md b/LICENSE.md index 245afac..626555b 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors. +Copyright (c) 2023, MapTiler.com & OpenMapTiles contributors. All rights reserved. The vector tile schema has been developed by Klokan Technologies GmbH and @@ -55,6 +55,6 @@ 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. Exceptions to OpenMapTiles attribution requirement can be in a written form granted -by Klokan Technologies GmbH (info@klokantech.com). -The project contributors grant Klokan Technologies GmbH the license to give such +by MapTiler (info@maptiler.com). +The project contributors grant MapTiler AG the license to give such exceptions on a commercial basis. diff --git a/Makefile b/Makefile index 9e4b8ac..71b6510 100644 --- a/Makefile +++ b/Makefile @@ -6,11 +6,8 @@ SHELL = /bin/bash .SHELLFLAGS = -o pipefail -c -# Make all .env variables available for make targets -include .env - # Layers definition and meta data -TILESET_FILE ?= openmaptiles.yaml +TILESET_FILE := $(or $(TILESET_FILE),$(shell (. .env; echo $${TILESET_FILE})),openmaptiles.yaml) # Options to run with docker and docker-compose - ensure the container is destroyed on exit # Containers run as the current user rather than root (so that created files are not root-owned) @@ -28,17 +25,30 @@ export PPORT # Local port to use with tileserver TPORT ?= 8081 export TPORT +STYLE_FILE := build/style/style.json +STYLE_HEADER_FILE := style/style-header.json + +# Support newer `docker compose` syntax in addition to `docker-compose` + +ifeq (, $(shell which docker-compose)) + DOCKER_COMPOSE_COMMAND := docker compose + $(info Using docker compose V2 (docker compose)) +else + DOCKER_COMPOSE_COMMAND := docker-compose + $(info Using docker compose V1 (docker-compose)) +endif # Allow a custom docker-compose project name -ifeq ($(strip $(DC_PROJECT)),) +DC_PROJECT := $(or $(DC_PROJECT),$(shell (. .env; echo $${DC_PROJECT}))) +ifeq ($(DC_PROJECT),) DC_PROJECT := $(notdir $(shell pwd)) - DOCKER_COMPOSE := docker-compose + DOCKER_COMPOSE := $(DOCKER_COMPOSE_COMMAND) else - DOCKER_COMPOSE := docker-compose --project-name $(DC_PROJECT) + DOCKER_COMPOSE := $(DOCKER_COMPOSE_COMMAND) --project-name $(DC_PROJECT) endif # Make some operations quieter (e.g. inside the test script) -ifeq ($(strip $(QUIET)),) +ifeq ($(or $(QUIET),$(shell (. .env; echo $${QUIET})))),) QUIET_FLAG := else QUIET_FLAG := --quiet @@ -58,8 +68,7 @@ else endif # Set OpenMapTiles host -OMT_HOST := http://$(firstword $(subst :, ,$(subst tcp://,,$(DOCKER_HOST))) localhost) -export OMT_HOST +export OMT_HOST := http://$(firstword $(subst :, ,$(subst tcp://,,$(DOCKER_HOST))) localhost) # This defines an easy $(newline) value to act as a "\n". Make sure to keep exactly two empty lines after newline. define newline @@ -67,6 +76,12 @@ define newline endef +# Use the old Postgres connection values as a fallback +PGHOST := $(or $(PGHOST),$(shell (. .env; echo $${PGHOST})),$(POSTGRES_HOST),$(shell (. .env; echo $${POSTGRES_HOST})),postgres) +PGPORT := $(or $(PGPORT),$(shell (. .env; echo $${PGPORT})),$(POSTGRES_PORT),$(shell (. .env; echo $${POSTGRES_PORT})),postgres) +PGDATABASE := $(or $(PGDATABASE),$(shell (. .env; echo $${PGDATABASE})),$(POSTGRES_DB),$(shell (. .env; echo $${POSTGRES_DB})),postgres) +PGUSER := $(or $(PGUSER),$(shell (. .env; echo $${PGUSER})),$(POSTGRES_USER),$(shell (. .env; echo $${POSTGRES_USER})),postgres) +PGPASSWORD := $(or $(PGPASSWORD),$(shell (. .env; echo $${PGPASSWORD})),$(POSTGRES_PASSWORD),$(shell (. .env; echo $${POSTGRES_PASSWORD})),postgres) # # Determine area to work on @@ -79,7 +94,7 @@ endef # historically we have been using $(area) rather than $(AREA), so make both work area ?= $(AREA) # Ensure the $(area) param is set, or try to automatically determine it based on available data files -ifeq ($(strip $(area)),) +ifeq ($(area),) # An $(area) parameter is not set. If only one *.osm.pbf file is found in ./data, use it as $(area). data_files := $(shell find data -name '*.osm.pbf' 2>/dev/null) ifneq ($(word 2,$(data_files)),) @@ -124,7 +139,7 @@ ifeq ($(strip $(area)),) endif endif -ifneq ($(strip $(AREA_INFO)),) +ifneq ($(AREA_INFO),) define assert_area_is_given @echo "$(AREA_INFO)" endef @@ -134,25 +149,17 @@ endif PBF_FILE ?= data/$(area).osm.pbf # For download-osm, allow URL parameter to download file from a given URL. Area param must still be provided. -ifneq ($(strip $(url)),) - DOWNLOAD_AREA := $(url) -else - DOWNLOAD_AREA := $(area) -endif +DOWNLOAD_AREA := $(or $(url), $(area)) -# import-borders uses these temp files during border parsing/import -export BORDERS_CLEANUP_FILE ?= data/borders/$(area).cleanup.pbf -export BORDERS_PBF_FILE ?= data/borders/$(area).filtered.pbf -export BORDERS_CSV_FILE ?= data/borders/$(area).lines.csv - -# The file is placed into the $EXPORT_DIR=/export (mapped to ./data) -export MBTILES_FILE ?= $(area).mbtiles +# The mbtiles file is placed into the $EXPORT_DIR=/export (mapped to ./data) +MBTILES_FILE := $(or $(MBTILES_FILE),$(shell (. .env; echo $${MBTILES_FILE})),$(area).mbtiles) MBTILES_LOCAL_FILE = data/$(MBTILES_FILE) -ifeq ($(strip $(DIFF_MODE)),true) +DIFF_MODE := $(or $(DIFF_MODE),$(shell (. .env; echo $${DIFF_MODE}))) +ifeq ($(DIFF_MODE),true) # import-osm implementation requires IMPOSM_CONFIG_FILE to be set to a valid file - # For static (no-updates) import, we don't need to override the default value - # For the update mode, set location of the dynamically-generated area-based config file + # For one-time only imports, the default value is fine. + # For diff mode updates, use the dynamically-generated area-based config file export IMPOSM_CONFIG_FILE = data/$(area).repl.json endif @@ -164,90 +171,119 @@ ifneq (,$(wildcard $(AREA_BBOX_FILE))) export BBOX endif -ifeq ($(strip $(area)),) - define assert_area_is_given - @echo "" - @echo "ERROR: $(AREA_ERROR)" - @echo "" - @echo " make $@ area=" - @echo "" - @echo "To download an area, use make download " - @echo "To list downloadable areas, use make list-geofabrik and/or make list-bbbike" - @exit 1 - endef -else - ifneq ($(strip $(AREA_INFO)),) - define assert_area_is_given - @echo "$(AREA_INFO)" - endef - endif -endif +# Consult .env if needed +MIN_ZOOM := $(or $(MIN_ZOOM),$(shell (. .env; echo $${MIN_ZOOM})),0) +MAX_ZOOM := $(or $(MAX_ZOOM),$(shell (. .env; echo $${MAX_ZOOM})),7) +PPORT := $(or $(PPORT),$(shell (. .env; echo $${PPORT})),7) +TPORT := $(or $(TPORT),$(shell (. .env; echo $${TPORT})),7) + +define HELP_MESSAGE +============================================================================== +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 $(OMT_HOST):8088 ] + make stop-maputnik # stop Maputnik Editor + dynamic tile server + make start-postserve # start dynamic tile server [ see $(OMT_HOST):$(PPORT) ] + make stop-postserve # stop dynamic tile server + make start-tileserver # start maptiler/tileserver-gl [ see $(OMT_HOST):$(TPORT) ] + 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 +============================================================================== +endef +export HELP_MESSAGE # # TARGETS # .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: - @echo "==============================================================================" - @echo " OpenMapTiles https://github.com/openmaptiles/openmaptiles " - @echo "Hints for testing areas " - @echo " make list-geofabrik # list actual geofabrik OSM extracts for download -> <> " - @echo " ./quickstart.sh <> # example: ./quickstart.sh madagascar " - @echo " " - @echo "Hints for designers:" - @echo " make start-maputnik # start Maputnik Editor + dynamic tile server [ see $(OMT_HOST):8088 ]" - @echo " make start-postserve # start dynamic tile server [ see $(OMT_HOST):$(PPORT) ]" - @echo " make start-tileserver # start maptiler/tileserver-gl [ see $(OMT_HOST):$(TPORT) ]" - @echo " " - @echo "Hints for developers:" - @echo " make # build source code" - @echo " make list-geofabrik # list actual geofabrik OSM extracts for download" - @echo " make list-bbbike # list actual BBBike OSM extracts for download" - @echo " make download area=albania # download OSM data from any source and create config file" - @echo " make download-geofabrik area=albania # download OSM data from geofabrik.de and create config file" - @echo " make download-osmfr area=asia/qatar # download OSM data from openstreetmap.fr and create config file" - @echo " make download-bbbike area=Amsterdam # download OSM data from bbbike.org and create config file" - @echo " make generate-bbox-file # compute bounding box of a data file and store it in a file" - @echo " make psql # start PostgreSQL console" - @echo " make psql-list-tables # list all PostgreSQL tables" - @echo " make vacuum-db # PostgreSQL: VACUUM ANALYZE" - @echo " make analyze-db # PostgreSQL: ANALYZE" - @echo " make generate-qa # statistics for a given layer's field" - @echo " make generate-devdoc # generate devdoc including graphs for all layers [./layers/...]" - @echo " make bash # start openmaptiles-tools /bin/bash terminal" - @echo " make destroy-db # remove docker containers and PostgreSQL data volume" - @echo " make start-db # start PostgreSQL, creating it if it doesn't exist" - @echo " make start-db-preloaded # start PostgreSQL, creating data-prepopulated one if it doesn't exist" - @echo " make stop-db # stop PostgreSQL database without destroying the data" - @echo " make clean-unnecessary-docker # clean unnecessary docker image(s) and container(s)" - @echo " make refresh-docker-images # refresh openmaptiles docker images from Docker HUB" - @echo " make remove-docker-images # remove openmaptiles docker images" - @echo " make list-views # list PostgreSQL public schema views" - @echo " make list-tables # list PostgreSQL public schema tables" - @echo " cat .env # list PG database and MIN_ZOOM and MAX_ZOOM information" - @echo " cat quickstart.log # transcript of the last ./quickstart.sh run" - @echo " make help # help about available commands" - @echo "==============================================================================" + @echo "$$HELP_MESSAGE" | less + +define win_fs_error + ( \ + echo "" ;\ + echo "ERROR: Windows native filesystem" ;\ + echo "" ;\ + echo "Please avoid running OpenMapTiles in a Windows filesystem." ;\ + echo "See https://github.com/openmaptiles/openmaptiles/issues/1095#issuecomment-817095465" ;\ + echo "" ;\ + exit 1 ;\ + ) +endef .PHONY: init-dirs init-dirs: @mkdir -p build/sql/parallel @mkdir -p build/openmaptiles.tm2source - @mkdir -p data/borders + @mkdir -p build/style + @mkdir -p data @mkdir -p cache + @ ! ($(DOCKER_COMPOSE) 2>/dev/null run $(DC_OPTS) openmaptiles-tools df --output=fstype /tileset| grep -q 9p) < /dev/null || ($(win_fs_error)) build/openmaptiles.tm2source/data.yml: init-dirs ifeq (,$(wildcard build/openmaptiles.tm2source/data.yml)) - $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools generate-tm2source $(TILESET_FILE) --host="postgres" --port=5432 --database="openmaptiles" --user="$(DC_USER)" --password="$(DC_PASSWORD)" > $@ + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools bash -c \ + 'generate-tm2source $(TILESET_FILE) > $@' + #OLD: $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools generate-tm2source $(TILESET_FILE) --host="postgres" --port=5432 --database="openmaptiles" --user="$(DC_USER)" --password="$(DC_PASSWORD)" > $@ endif build/mapping.yaml: init-dirs ifeq (,$(wildcard build/mapping.yaml)) - $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools generate-imposm3 $(TILESET_FILE) > $@ + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools bash -c \ + 'generate-imposm3 $(TILESET_FILE) > $@' endif .PHONY: build-sql @@ -261,18 +297,42 @@ ifeq (,$(wildcard build/sql/run_last.sql)) --function --fname=getmvt >> ./build/sql/run_last.sql' endif +.PHONY: build-sprite +build-sprite: init-dirs + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools bash -c 'spritezero build/style/sprite /style/icons && \ + spritezero --retina build/style/sprite@2x /style/icons' + +.PHONY: build-style +build-style: init-dirs + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools bash -c 'style-tools recompose $(TILESET_FILE) $(STYLE_FILE) \ + $(STYLE_HEADER_FILE) && \ + spritezero build/style/sprite /style/icons && spritezero --retina build/style/sprite@2x /style/icons' + +.PHONY: download-fonts +download-fonts: + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools bash -c '[ ! -d "/export/fonts" ] && mkdir /export/fonts && \ + echo "Downloading fonts..." && wget -qO /export/noto-sans.zip --show-progress \ + https://github.com/openmaptiles/fonts/releases/download/v2.0/noto-sans.zip && \ + echo "Unzipping fonts..." && unzip -q /export/noto-sans.zip -d /export/fonts && rm /export/noto-sans.zip || \ + echo "Fonts already exist."' + .PHONY: clean -clean: +clean: clean-test-data rm -rf build +clean-test-data: + rm -rf data/changes.state.txt + rm -rf data/last.state.txt + rm -rf data/changes.repl.json + .PHONY: destroy-db -# TODO: Use https://stackoverflow.com/a/27852388/177275 -destroy-db: DC_PROJECT := $(shell echo $(DC_PROJECT) | tr A-Z a-z) +DOCKER_PROJECT = $(shell echo $(DC_PROJECT) | tr A-Z a-z | tr -cd '[:alnum:]') destroy-db: $(DOCKER_COMPOSE) down -v --remove-orphans $(DOCKER_COMPOSE) rm -fv - docker volume ls -q -f "name=^$(DC_PROJECT)_" | $(XARGS) docker volume rm + docker volume ls -q -f "name=^$(DOCKER_PROJECT)_" | $(XARGS) docker volume rm rm -rf cache + mkdir cache .PHONY: start-db-nowait start-db-nowait: init-dirs @@ -313,11 +373,11 @@ OSM_SERVER=$(patsubst download,,$(patsubst download-%,%,$@)) .PHONY: $(ALL_DOWNLOADS) $(ALL_DOWNLOADS): init-dirs @$(assert_area_is_given) -ifneq ($(strip $(url)),) +ifneq ($(url),) $(if $(OSM_SERVER),$(error url parameter can only be used with non-specific download target:$(newline) make download area=$(area) url="$(url)"$(newline))) endif ifeq (,$(wildcard $(PBF_FILE))) - ifeq ($(strip $(DIFF_MODE)),true) + ifeq ($(DIFF_MODE),true) @echo "Downloading $(DOWNLOAD_AREA) with replication support into $(PBF_FILE) and $(IMPOSM_CONFIG_FILE) from $(if $(OSM_SERVER),$(OSM_SERVER),any source)" @$(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools download-osm $(OSM_SERVER) "$(DOWNLOAD_AREA)" \ --imposm-cfg "$(IMPOSM_CONFIG_FILE)" \ @@ -331,7 +391,7 @@ ifeq (,$(wildcard $(PBF_FILE))) endif @echo "" else - ifeq ($(strip $(DIFF_MODE)),true) + ifeq ($(DIFF_MODE),true) ifeq (,$(wildcard $(IMPOSM_CONFIG_FILE))) $(error \ $(newline) Data files $(PBF_FILE) already exists, but $(IMPOSM_CONFIG_FILE) does not. \ @@ -364,7 +424,7 @@ psql: start-db-nowait # Special cache handling for Docker Toolbox on Windows ifeq ($(MSYSTEM),MINGW64) DC_CONFIG_CACHE := -f docker-compose.yml -f docker-compose-$(MSYSTEM).yml - DC_OPTS_CACHE := $(strip $(filter-out --user=%,$(DC_OPTS))) + DC_OPTS_CACHE := $(filter-out --user=%,$(DC_OPTS)) else DC_OPTS_CACHE := $(DC_OPTS) endif @@ -374,13 +434,17 @@ import-osm: all start-db-nowait @$(assert_area_is_given) $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) openmaptiles-tools sh -c 'pgwait && import-osm $(PBF_FILE)' -.PHONY: update-osm -update-osm: all start-db-nowait +.PHONY: start-update-osm +start-update-osm: start-db @$(assert_area_is_given) - $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) openmaptiles-tools sh -c 'pgwait && import-update' + $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) up -d update-osm + +.PHONY: stop-update-osm +stop-update-osm: + $(DOCKER_COMPOSE) stop update-osm .PHONY: import-diff -import-diff: all start-db-nowait +import-diff: start-db-nowait @$(assert_area_is_given) $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) openmaptiles-tools sh -c 'pgwait && import-diff' @@ -388,17 +452,11 @@ import-diff: all start-db-nowait import-data: start-db $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) import-data -.PHONY: import-borders -import-borders: start-db-nowait - @$(assert_area_is_given) - # If CSV borders file already exists, use it without re-parsing - $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools sh -c \ - 'pgwait && import-borders $$([ -f "$(BORDERS_CSV_FILE)" ] && echo load $(BORDERS_CSV_FILE) || echo import $(PBF_FILE))' - .PHONY: import-sql import-sql: all start-db-nowait $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools sh -c 'pgwait && import-sql' | \ - awk -v s=": WARNING:" '1{print; fflush()} $$0~s{print "\n*** WARNING detected, aborting"; exit(1)}' + awk -v s=": WARNING:" '1{print; fflush()} $$0~s{print "\n*** WARNING detected, aborting"; exit(1)}' | \ + awk '1{print; fflush()} $$0~".*ERROR" {txt=$$0} END{ if(txt){print "\n*** ERROR detected, aborting:"; print txt; exit(1)} }' .PHONY: merge-pbf merge-pbf: @@ -408,7 +466,7 @@ merge-pbf: .PHONY: generate-tiles generate-tiles: all start-db - @$(assert_area_is_given) + @echo "WARNING: This Mapnik-based method of tile generation is obsolete. Use generate-tiles-pg instead." @echo "Generating tiles into $(MBTILES_LOCAL_FILE) (will delete if already exists)..." @rm -rf "$(MBTILES_LOCAL_FILE)" $(DOCKER_COMPOSE) run $(DC_OPTS) generate-vectortiles @@ -418,16 +476,31 @@ generate-tiles: all start-db .PHONY: generate-tiles-pg generate-tiles-pg: all start-db - @$(assert_area_is_given) - @echo "Generating tiles into $(MBTILES_LOCAL_FILE) (will delete if already exists)..." + @echo "Generating tiles into $(MBTILES_LOCAL_FILE) (will delete if already exists) using PostGIS ST_MVT()..." @rm -rf "$(MBTILES_LOCAL_FILE)" - $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools generate-tiles +# For some reason Ctrl+C doesn't work here without the -T. Must be pressed twice to stop. + $(DOCKER_COMPOSE) run -T $(DC_OPTS) openmaptiles-tools generate-tiles @echo "Updating generated tile metadata ..." $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools \ mbtiles-tools meta-generate "$(MBTILES_LOCAL_FILE)" $(TILESET_FILE) --auto-minmax --show-ranges +.PHONY: data/tiles.txt +data/tiles.txt: + find ./data -name "*.tiles" -exec cat {} \; -exec rm {} \; | \ + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools \ + tile_multiplier $(MIN_ZOOM) $(MAX_ZOOM) >> data/tiles.txt + +.PHONY: generate-changed-tiles +generate-changed-tiles: data/tiles.txt + # Re-generating updated tiles, if needed + if [ -s data/tiles.txt ] ; then \ + $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) openmaptiles-tools refresh-views; \ + $(DOCKER_COMPOSE) run $(DC_OPTS) -e LIST_FILE=data/tiles.txt openmaptiles-tools generate-tiles; \ + rm data/tiles.txt; \ + fi + .PHONY: start-tileserver -start-tileserver: init-dirs +start-tileserver: init-dirs build-style download-fonts @echo " " @echo "***********************************************************" @echo "* " @@ -445,7 +518,11 @@ start-tileserver: init-dirs @echo "* " @echo "***********************************************************" @echo " " - docker run $(DC_OPTS) -it --name tileserver-gl -v $$(pwd)/data:/data -p $(TPORT):$(TPORT) maptiler/tileserver-gl --port $(TPORT) + $(DOCKER_COMPOSE) up -d tileserver-gl + +.PHONY: stop-tileserver +stop-tileserver: + $(DOCKER_COMPOSE) stop tileserver-gl .PHONY: start-postserve start-postserve: start-db @@ -477,11 +554,11 @@ start-maputnik: stop-maputnik start-postserve @echo "* " @echo "***********************************************************" @echo " " - docker run $(DC_OPTS) --name maputnik_editor -d -p 8088:8888 maputnik/editor + $(DOCKER_COMPOSE) up -d maputnik_editor .PHONY: stop-maputnik stop-maputnik: - -docker rm -f maputnik_editor + -$(DOCKER_COMPOSE) stop maputnik_editor # STAT_FUNCTION=frequency|toplength|variance .PHONY: generate-qa @@ -542,16 +619,16 @@ list-docker-images: .PHONY: refresh-docker-images refresh-docker-images: init-dirs -ifneq ($(strip $(NO_REFRESH)),) +ifneq ($(NO_REFRESH),) @echo "Skipping docker image refresh" else @echo "" @echo "Refreshing docker images... Use NO_REFRESH=1 to skip." -ifneq ($(strip $(USE_PRELOADED_IMAGE)),) +ifneq ($(USE_PRELOADED_IMAGE),) POSTGIS_IMAGE=openmaptiles/postgis-preloaded \ - docker-compose pull --ignore-pull-failures $(QUIET_FLAG) openmaptiles-tools generate-vectortiles postgres + $(DOCKER_COMPOSE_COMMAND) pull --ignore-pull-failures $(QUIET_FLAG) openmaptiles-tools generate-vectortiles postgres else - docker-compose pull --ignore-pull-failures $(QUIET_FLAG) openmaptiles-tools generate-vectortiles postgres import-data + $(DOCKER_COMPOSE_COMMAND) pull --ignore-pull-failures $(QUIET_FLAG) openmaptiles-tools generate-vectortiles postgres import-data endif endif @@ -576,7 +653,7 @@ test-perf-null: init-dirs .PHONY: build-test-pbf build-test-pbf: init-dirs - docker-compose run $(DC_OPTS) openmaptiles-tools /tileset/.github/workflows/build-test-data.sh + $(DOCKER_COMPOSE_COMMAND) run $(DC_OPTS) openmaptiles-tools /tileset/.github/workflows/build-test-data.sh .PHONY: debug debug: ## Use this target when developing Makefile itself to verify loaded environment variables @@ -586,3 +663,45 @@ debug: ## Use this target when developing Makefile itself to verify loaded envi @echo BBOX = $(BBOX) , $$BBOX @echo MIN_ZOOM = $(MIN_ZOOM) , $$MIN_ZOOM @echo MAX_ZOOM = $(MAX_ZOOM) , $$MAX_ZOOM + +build/import-tests.osm.pbf: init-dirs + $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) openmaptiles-tools sh -c 'osmconvert tests/import/*.osm -o=build/import-tests.osm.pbf' + +data/changes.state.txt: + cp -f tests/changes.state.txt data/ + +data/last.state.txt: + cp -f tests/last.state.txt data/ + +data/changes.repl.json: + cp -f tests/changes.repl.json data/ + +data/changes.osc.gz: init-dirs + @echo " UPDATE unit test data..." + $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) openmaptiles-tools sh -c 'osmconvert tests/update/*.osc --merge-versions -o=data/changes.osc && gzip -f data/changes.osc' + +test-sql: clean refresh-docker-images destroy-db start-db-nowait build/import-tests.osm.pbf data/changes.state.txt data/last.state.txt data/changes.repl.json build/mapping.yaml data/changes.osc.gz build/openmaptiles.tm2source/data.yml build/mapping.yaml build-sql + $(eval area := changes) + + @echo "Load IMPORT test data" + sed -ir "s/^[#]*\s*MAX_ZOOM=.*/MAX_ZOOM=14/" .env + sed -ir "s/^[#]*\s*DIFF_MODE=.*/DIFF_MODE=false/" .env + $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) openmaptiles-tools sh -c 'pgwait && import-osm build/import-tests.osm.pbf' + $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) import-data + + @echo "Apply OpenMapTiles SQL schema to test data @ Zoom 14..." + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools sh -c 'pgwait && import-sql' | \ + awk -v s=": WARNING:" '1{print; fflush()} $$0~s{print "\n*** WARNING detected, aborting"; exit(1)}' | \ + awk '1{print; fflush()} $$0~".*ERROR" {txt=$$0} END{ if(txt){print "\n*** ERROR detected, aborting:"; print txt; exit(1)} }' + + @echo "Test SQL output for Import Test Data" + $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) openmaptiles-tools sh -c 'pgwait && psql.sh < tests/test-post-import.sql' 2>&1 | \ + awk -v s="ERROR:" '1{print; fflush()} $$0~s{print "*** ERROR detected, aborting"; exit(1)}' + + @echo "Run UPDATE process on test data..." + sed -ir "s/^[#]*\s*DIFF_MODE=.*/DIFF_MODE=true/" .env + $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) openmaptiles-tools sh -c 'pgwait && import-diff' + + @echo "Test SQL output for Update Test Data" + $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) openmaptiles-tools sh -c 'pgwait && psql.sh < tests/test-post-update.sql' 2>&1 | \ + awk -v s="ERROR:" '1{print; fflush()} $$0~s{print "*** ERROR detected, aborting"; exit(1)}' diff --git a/QUICKSTART.md b/QUICKSTART.md index 1532ed0..75ec389 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -2,7 +2,7 @@ ### Req: * CPU: AMD64 ( = Intel 64 bit) - * The base docker debian images are x86_64 based, so the ARM,MIPS currently not supported! + * 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. @@ -59,7 +59,7 @@ If you have problems with the quickstart 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` + * 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" ] @@ -415,33 +415,63 @@ the current output: ``` ============================================================================== - OpenMapTiles https://github.com/openmaptiles/openmaptiles +OpenMapTiles https://github.com/openmaptiles/openmaptiles + Hints for testing areas - make download-geofabrik-list # list actual geofabrik OSM extracts for download -> <> + make list-geofabrik # list actual geofabrik OSM extracts for download -> <> ./quickstart.sh <> # example: ./quickstart.sh madagascar Hints for designers: - make start-postserve # start Postserver + Maputnik Editor [ see localhost:8088 ] - make start-tileserver # start maptiler/tileserver-gl [ see localhost:8081 ] + 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:8081 ] + make stop-tileserver # stop maptiler/tileserver-gl Hints for developers: make # build source code - make download-geofabrik area=albania # download OSM data from geofabrik, and create config file + 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 psql-vacuum-analyze # PostgreSQL: VACUUM ANALYZE - make psql-analyze # PostgreSQL: ANALYZE - make generate-qa # statistics for a given layer's field - make generate-devdoc # generate devdoc [./build/devdoc] - make tools-dev # start import-sql /bin/bash terminal - make db-destroy # remove docker containers, PG data volume - make docker-unnecessary-clean # 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-views # list PostgreSQL public schema views make list-tables # list PostgreSQL public schema tables - cat .env # list PG database and MIN_ZOOM and MAX_ZOOM information - cat ./quickstart.log # backup of the last ./quickstart.sh - make help # help about available commands + 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 ed03640..7bccd3b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## OpenMapTiles [![Build Status](https://github.com/openmaptiles/openmaptiles/workflows/OMT_CI/badge.svg?branch=master)](https://github.com/openmaptiles/openmaptiles/actions) +## OpenMapTiles [![Build Status](https://github.com/openmaptiles/openmaptiles/workflows/OpenMapTiles%20Integrity%20CI/badge.svg?branch=master)](https://github.com/openmaptiles/openmaptiles/actions) 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/). @@ -20,14 +20,15 @@ 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) -- [Klokantech Basic](https://github.com/openmaptiles/klokantech-basic-gl-style) -- [Klokantech 3D](https://github.com/openmaptiles/klokantech-3d-gl-style) -- [Fiord Color](https://github.com/openmaptiles/fiord-color-gl-style) -- [Toner](https://github.com/openmaptiles/toner-gl-style) -- [OSM Liberty](https://github.com/maputnik/osm-liberty) We also ported over our favorite old raster styles (TM2). @@ -71,6 +72,10 @@ 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. @@ -83,7 +88,7 @@ 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. +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 @@ -97,7 +102,7 @@ Now start up the database container. 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/lukasmartinelli/osm-lakelines). +Import external data from [OpenStreetMapData](http://osmdata.openstreetmap.de/), [Natural Earth](http://www.naturalearthdata.com/) and [OpenStreetMap Lake Labels](https://github.com/lukasmartinelli/osm-lakelines). Natural Earth country boundaries are used in the few lowest zoom levels. ```bash make import-data @@ -109,12 +114,11 @@ Download OpenStreetMap data extracts from any source like [Geofabrik](http://dow make download area=albania ``` -[Import OpenStreetMap data](https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/import-osm) with the mapping rules from -`build/mapping.yaml` (which has been created by `make`). Run after any change in layers definition. Also create borders table using extra processing with [osmborder](https://github.com/pnorman/osmborder) tool. +[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 -make import-borders ``` 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. @@ -126,6 +130,13 @@ 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`. @@ -135,16 +146,43 @@ 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 -make generate-tiles # generate tiles +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 +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` + + ## License -All code in this repository is under the [BSD license](./LICENSE.md) and the cartography decisions encoded in the schema and SQL are licensed under [CC-BY](./LICENSE.md). +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. 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 index b78b788..7b29c1b 100644 --- a/UPDATE.md +++ b/UPDATE.md @@ -1,42 +1,116 @@ -# Keep the vector tiles updated +# 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 OSM change feed +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 the new imposm3 feature to keep the database updated (thanks to the [work by @stirringhalo](https://github.com/openmaptiles/openmaptiles/pull/131)). This will automatically download -the OSM change feed and import it into the database. -After each run you should also have a list of tiles that have 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 ``` -make update-osm +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 -Given you have a file `changes.osc.gz` in your import folder. Once you ran the import command you should also have a list of tiles that have updated. +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 -After the import has finished **imposm3** will store lists of tiles in text format in subfolders of the `diffdir`, -named for the date(s) on which the import took place (`YYYYMMDD`). -Copy and merge the files to `tiles.txt` in the import folder (`data`), either manually or with the following command, which also removes duplicate tiles so they are only generated once: -``` -cd data && sort ./*/*.tiles | uniq > tiles.txt -``` - -Now run the command to read the tilelist and write the vector tiles for it to a new MBTiles. +To generate all changed tiles, based on the lists of all updated tiles, and update the existing MBtiles file, please use ``` -docker-compose run generate-changed-vectortiles +make generate-changed-tiles ``` diff --git a/docker-compose.yml b/docker-compose.yml index d47ef5b..2c78c8e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,17 +11,24 @@ networks: services: postgres: - #image: "${POSTGIS_IMAGE:-openmaptiles/postgis}:${TOOLS_VERSION}" - image: harbor.cerxes.net/openmaptiles/postgis:5.3 + image: "${POSTGIS_IMAGE:-openmaptiles/postgis}:${TOOLS_VERSION}" + # image: harbor.cerxes.net/openmaptiles/postgis:5.3 # Use "command: postgres -c jit=off" for PostgreSQL 11+ because of slow large MVT query processing + # Use "shm_size: 512m" if you want to prevent a possible 'No space left on device' during 'make generate-tiles-pg' volumes: - pgdata:/var/lib/postgresql/data networks: - postgres ports: - - "5432" + - "${PGPORT:-5432}:${PGPORT:-5432}" + env_file: .env + environment: + # postgress container uses old variable names + POSTGRES_DB: ${PGDATABASE:-openmaptiles} + POSTGRES_USER: ${PGUSER:-openmaptiles} + POSTGRES_PASSWORD: ${PGPASSWORD:-openmaptiles} + PGPORT: ${PGPORT:-5432} shm_size: 4g - env_file: .env-postgres #command: /docker-entrypoint-initdb.d/01_tune-postgis.sh #command: /docker-entrypoint-initdb.d/01_tune-postgis.sh && echo 'tuned' && postgres -c 'config_file=/etc/postgresql/postgresql.conf' @@ -31,7 +38,7 @@ services: networks: - postgres - openmaptiles-tools: + openmaptiles-tools: &openmaptiles-tools image: "openmaptiles/openmaptiles-tools:${TOOLS_VERSION}" env_file: .env environment: @@ -46,12 +53,13 @@ services: BBOX: ${BBOX} # Imposm configuration file describes how to load updates when enabled IMPOSM_CONFIG_FILE: ${IMPOSM_CONFIG_FILE} - # Which files to use during import-borders processing - BORDERS_CLEANUP_FILE: ${BORDERS_CLEANUP_FILE} - BORDERS_PBF_FILE: ${BORDERS_PBF_FILE} - BORDERS_CSV_FILE: ${BORDERS_CSV_FILE} # Control import-sql processes MAX_PARALLEL_PSQL: ${MAX_PARALLEL_PSQL} + PGDATABASE: ${PGDATABASE:-openmaptiles} + PGUSER: ${PGUSER:-openmaptiles} + PGPASSWORD: ${PGPASSWORD:-openmaptiles} + PGPORT: ${PGPORT:-5432} + MBTILES_FILE: ${MBTILES_FILE} networks: - postgres volumes: @@ -61,6 +69,11 @@ services: - ./build/sql:/sql - ./build:/mapping - ./cache:/cache + - ./style:/style + + update-osm: + <<: *openmaptiles-tools + command: import-update generate-changed-vectortiles: image: "openmaptiles/generate-vectortiles:${TOOLS_VERSION}" @@ -72,10 +85,13 @@ services: - postgres env_file: .env environment: - FILTER_MAPNIK_OUTPUT: ${FILTER_MAPNIK_OUTPUT} MBTILES_NAME: ${MBTILES_FILE} # Control tilelive-copy threads COPY_CONCURRENCY: ${COPY_CONCURRENCY} + PGDATABASE: ${PGDATABASE:-openmaptiles} + PGUSER: ${PGUSER:-openmaptiles} + PGPASSWORD: ${PGPASSWORD:-openmaptiles} + PGPORT: ${PGPORT:-5432} generate-vectortiles: image: "openmaptiles/generate-vectortiles:${TOOLS_VERSION}" @@ -86,7 +102,6 @@ services: - postgres env_file: .env environment: - FILTER_MAPNIK_OUTPUT: ${FILTER_MAPNIK_OUTPUT} MBTILES_NAME: ${MBTILES_FILE} BBOX: ${BBOX} MIN_ZOOM: ${MIN_ZOOM} @@ -94,6 +109,10 @@ services: # Control tilelive-copy threads COPY_CONCURRENCY: ${COPY_CONCURRENCY} # + PGDATABASE: ${PGDATABASE:-openmaptiles} + PGUSER: ${PGUSER:-openmaptiles} + PGPASSWORD: ${PGPASSWORD:-openmaptiles} + PGPORT: ${PGPORT:-5432} postserve: image: "openmaptiles/openmaptiles-tools:${TOOLS_VERSION}" @@ -107,3 +126,22 @@ services: - "${PPORT:-8090}:${PPORT:-8090}" volumes: - .:/tileset + + maputnik_editor: + image: "maputnik/editor" + ports: + - "8088:8888" + + tileserver-gl: + image: "maptiler/tileserver-gl:latest" + command: + - --port + - "${TPORT:-8080}" + - --config + - "/style/config.json" + ports: + - "${TPORT:-8080}:${TPORT:-8080}" + volumes: + - ./data:/data + - ./style:/style + - ./build:/build diff --git a/integrity.sh b/integrity.sh new file mode 100755 index 0000000..831f14a --- /dev/null +++ b/integrity.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +# A script to run the "integrity" continuous integration script. + +area=monaco +echo MIN_ZOOM=0 >> .env +echo MAX_ZOOM=14 >> .env +./quickstart.sh $area +export TEST_MODE=yes +make generate-devdoc +area=europe/monaco +echo DIFF_MODE=true >> .env + +# Cleanup +rm -fr data build cache +# Create data/$area.repl.json +make download-geofabrik area=$area +# Download 2+ month old data +export old_date=$(date --date="$(date +%Y-%m-15) -2 month" +'%y%m01') +echo Downloading $old_date extract of $area +docker-compose run --rm --user=$(id -u):$(id -g) openmaptiles-tools sh -c "wget -O data/$area.osm.pbf http://download.geofabrik.de/$area-$old_date.osm.pbf" +# Initial import and tile generation +./quickstart.sh $area +sleep 2 +echo Downloading updates +# Loop to recover from potential "ERROR 429: Too Many Requests" +docker-compose run --rm --user=$(id -u):$(id -g) openmaptiles-tools sh -c " +while ! osmupdate --keep-tempfiles --base-url=$(sed -n 's/ *\"replication_url\": //p' data/$area.repl.json) data/$area.osm.pbf data/changes.osc.gz ; do + sleep 2; + echo Sleeping...; + sleep 630; +done" +echo Downloading updates completed +echo Importing updates +make import-diff +echo Generating new tiles +make generate-tiles-pg diff --git a/layers/aerodrome_label/aerodrome_label.sql b/layers/aerodrome_label/aerodrome_label.sql index 7c2c41f..b3c3625 100644 --- a/layers/aerodrome_label/aerodrome_label.sql +++ b/layers/aerodrome_label/aerodrome_label.sql @@ -1,11 +1,11 @@ --- etldoc: layer_aerodrome_label[shape=record fillcolor=lightpink, style="rounded,filled", label="layer_aerodrome_label | z10+" ] ; +-- 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 ( - osm_id bigint, + id bigint, geometry geometry, name text, name_en text, @@ -20,17 +20,36 @@ CREATE OR REPLACE FUNCTION layer_aerodrome_label(bbox geometry, AS $$ SELECT - -- etldoc: osm_aerodrome_label_point -> layer_aerodrome_label:z10_ - osm_id, + -- 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, - CASE - %%FIELD_MAPPING: class %% - ELSE 'other' - END AS class, + 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, diff --git a/layers/aerodrome_label/aerodrome_label.yaml b/layers/aerodrome_label/aerodrome_label.yaml index 6d5626c..3a48cf6 100644 --- a/layers/aerodrome_label/aerodrome_label.yaml +++ b/layers/aerodrome_label/aerodrome_label.yaml @@ -1,5 +1,5 @@ layer: - id: "aerodrome_label" + id: aerodrome_label description: | [Aerodrome labels](http://wiki.openstreetmap.org/wiki/Tag:aeroway%3Daerodrome) buffer_size: 64 @@ -38,10 +38,10 @@ layer: ele_ft: Elevation (`ele`) in feets. datasource: geometry_field: geometry - key_field: osm_id + key_field: id key_field_as_attribute: no srid: 900913 - query: (SELECT osm_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 + 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 diff --git a/layers/aerodrome_label/etl_diagram.png b/layers/aerodrome_label/etl_diagram.png index 3879664..c659ab4 100644 Binary files a/layers/aerodrome_label/etl_diagram.png and b/layers/aerodrome_label/etl_diagram.png differ diff --git a/layers/aerodrome_label/mapping_diagram.png b/layers/aerodrome_label/mapping_diagram.png index cbbb899..7596a7c 100644 Binary files a/layers/aerodrome_label/mapping_diagram.png 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 index a0031e0..c5d675a 100644 --- a/layers/aerodrome_label/update_aerodrome_label_point.sql +++ b/layers/aerodrome_label/update_aerodrome_label_point.sql @@ -2,11 +2,17 @@ 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 + osm_id bigint PRIMARY KEY ); -- etldoc: osm_aerodrome_label_point -> osm_aerodrome_label_point @@ -22,6 +28,17 @@ $$ 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); @@ -31,11 +48,7 @@ SELECT update_aerodrome_label_point(true); CREATE OR REPLACE FUNCTION aerodrome_label.store() RETURNS trigger AS $$ BEGIN - IF (tg_op = 'DELETE') THEN - INSERT INTO aerodrome_label.osm_ids VALUES (OLD.osm_id); - ELSE - INSERT INTO aerodrome_label.osm_ids VALUES (NEW.osm_id); - END IF; + INSERT INTO aerodrome_label.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; RETURN NULL; END; $$ LANGUAGE plpgsql; @@ -60,6 +73,11 @@ 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; @@ -72,13 +90,13 @@ END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_store - AFTER INSERT OR UPDATE OR DELETE + 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 OR DELETE + AFTER INSERT OR UPDATE ON osm_aerodrome_label_point FOR EACH STATEMENT EXECUTE PROCEDURE aerodrome_label.flag(); diff --git a/layers/aeroway/etl_diagram.png b/layers/aeroway/etl_diagram.png index de81e54..20e7622 100644 Binary files a/layers/aeroway/etl_diagram.png and b/layers/aeroway/etl_diagram.png differ diff --git a/layers/aeroway/mapping_diagram.png b/layers/aeroway/mapping_diagram.png index e76d4d0..7536f47 100644 Binary files a/layers/aeroway/mapping_diagram.png 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/boundary/boundary.sql b/layers/boundary/boundary.sql index a20d8cd..cfe3953 100644 --- a/layers/boundary/boundary.sql +++ b/layers/boundary/boundary.sql @@ -1,207 +1,187 @@ --- This statement can be deleted after the border importer image stops creating this object as a table -DO -$$ - BEGIN - DROP TABLE IF EXISTS osm_border_linestring_gen_z13 CASCADE; - EXCEPTION - WHEN wrong_object_type THEN - END; -$$ LANGUAGE plpgsql; - -- 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(geometry, ZRes(14)) AS geometry, NULL::text AS adm0_l, NULL::text AS adm0_r, admin_level, disputed, maritime -FROM osm_border_linestring -WHERE admin_level BETWEEN 3 AND 10 -UNION ALL -SELECT ST_Simplify(geometry, ZRes(14)) AS geometry, adm0_l, adm0_r, admin_level, disputed, maritime -FROM osm_border_linestring_adm - ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; -CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z13_idx ON osm_border_linestring_gen_z13 USING gist (geometry); +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 --- This statement can be deleted after the border importer image stops creating this object as a table -DO -$$ - BEGIN - DROP TABLE IF EXISTS osm_border_linestring_gen_z12 CASCADE; - EXCEPTION - WHEN wrong_object_type THEN - END; -$$ LANGUAGE plpgsql; + 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, maritime +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); --- This statement can be deleted after the border importer image stops creating this object as a table -DO -$$ - BEGIN - DROP TABLE IF EXISTS osm_border_linestring_gen_z11 CASCADE; - EXCEPTION - WHEN wrong_object_type THEN - END; -$$ LANGUAGE plpgsql; - -- 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, maritime +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); --- This statement can be deleted after the border importer image stops creating this object as a table -DO -$$ - BEGIN - DROP TABLE IF EXISTS osm_border_linestring_gen_z10 CASCADE; - EXCEPTION - WHEN wrong_object_type THEN - END; -$$ LANGUAGE plpgsql; - -- 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, maritime +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); --- This statement can be deleted after the border importer image stops creating this object as a table -DO -$$ - BEGIN - DROP TABLE IF EXISTS osm_border_linestring_gen_z9 CASCADE; - EXCEPTION - WHEN wrong_object_type THEN - END; -$$ LANGUAGE plpgsql; - -- 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, maritime +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 +-- 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); --- This statement can be deleted after the border importer image stops creating this object as a table -DO -$$ - BEGIN - DROP TABLE IF EXISTS osm_border_linestring_gen_z8 CASCADE; - EXCEPTION - WHEN wrong_object_type THEN - END; -$$ LANGUAGE plpgsql; - -- 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, maritime +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); --- This statement can be deleted after the border importer image stops creating this object as a table -DO -$$ - BEGIN - DROP TABLE IF EXISTS osm_border_linestring_gen_z7 CASCADE; - EXCEPTION - WHEN wrong_object_type THEN - END; -$$ LANGUAGE plpgsql; - -- 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, maritime +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 +-- 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); --- This statement can be deleted after the border importer image stops creating this object as a table -DO -$$ - BEGIN - DROP TABLE IF EXISTS osm_border_linestring_gen_z6 CASCADE; - EXCEPTION - WHEN wrong_object_type THEN - END; -$$ LANGUAGE plpgsql; - -- 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, maritime +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 +-- 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); --- This statement can be deleted after the border importer image stops creating this object as a table -DO -$$ - BEGIN - DROP TABLE IF EXISTS osm_border_linestring_gen_z5 CASCADE; - EXCEPTION - WHEN wrong_object_type THEN - END; -$$ LANGUAGE plpgsql; - -- 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, maritime +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 +-- 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); --- This statement can be deleted after the border importer image stops creating this object as a table -DO -$$ - BEGIN - DROP TABLE IF EXISTS osm_border_linestring_gen_z4 CASCADE; - EXCEPTION - WHEN wrong_object_type THEN - END; -$$ LANGUAGE plpgsql; - -- 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, maritime +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 +WHERE admin_level = 2 AND (maritime OR disputed) ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z4_idx ON osm_border_linestring_gen_z4 USING gist (geometry); +-- etldoc: osm_border_linestring_gen_z4 -> osm_border_disp_linestring_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS osm_border_disp_linestring_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW osm_border_disp_linestring_gen_z3 AS +( +SELECT ST_Simplify(geometry, ZRes(4)) AS geometry, adm0_l, adm0_r, admin_level, TRUE AS disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z4 +WHERE disputed -- AND admin_level = 2 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_disp_linestring_gen_z3_idx ON osm_border_disp_linestring_gen_z3 USING gist (geometry); + +-- etldoc: osm_border_disp_linestring_gen_z3 -> osm_border_disp_linestring_gen_z2 +DROP MATERIALIZED VIEW IF EXISTS osm_border_disp_linestring_gen_z2 CASCADE; +CREATE MATERIALIZED VIEW osm_border_disp_linestring_gen_z2 AS +( +SELECT ST_Simplify(geometry, ZRes(3)) AS geometry, adm0_l, adm0_r, admin_level, TRUE AS disputed, name, claimed_by, maritime +FROM osm_border_disp_linestring_gen_z3 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_disp_linestring_gen_z2_idx ON osm_border_disp_linestring_gen_z2 USING gist (geometry); + +-- etldoc: osm_border_disp_linestring_gen_z2 -> osm_border_disp_linestring_gen_z1 +DROP MATERIALIZED VIEW IF EXISTS osm_border_disp_linestring_gen_z1 CASCADE; +CREATE MATERIALIZED VIEW osm_border_disp_linestring_gen_z1 AS +( +SELECT ST_Simplify(geometry, ZRes(2)) AS geometry, adm0_l, adm0_r, admin_level, TRUE AS disputed, name, claimed_by, maritime +FROM osm_border_disp_linestring_gen_z2 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_disp_linestring_gen_z1_idx ON osm_border_disp_linestring_gen_z1 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; @@ -228,9 +208,10 @@ SELECT ST_Simplify(geometry, ZRes(6)) as geometry, FALSE AS disputed, NULL::text AS disputed_name, NULL::text AS claimed_by, - FALSE AS maritime + FALSE AS maritime, + min_zoom FROM ne_10m_admin_1_states_provinces_lines -WHERE min_zoom <= 7 +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); @@ -246,6 +227,7 @@ SELECT ST_Simplify(geometry, ZRes(5)) as geometry, 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); @@ -475,7 +457,6 @@ FROM osm_border_disp_linestring_gen_z3 -- 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 --- etldoc: osm_border_disp_linestring_gen_z4 -> boundary_z4 CREATE OR REPLACE VIEW boundary_z4 AS ( SELECT geometry, @@ -503,26 +484,13 @@ SELECT geometry, adm0_l, adm0_r, disputed, - NULL::text AS disputed_name, - NULL::text AS claimed_by, - maritime -FROM osm_border_linestring_gen_z4 -WHERE maritime = TRUE - AND admin_level <= 2 -UNION ALL -SELECT geometry, - admin_level, - NULL::text AS adm0_l, - NULL::text AS adm0_r, - TRUE AS disputed, - edit_name(name) AS disputed_name, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, claimed_by, maritime -FROM osm_border_disp_linestring_gen_z4 +FROM osm_border_linestring_gen_z4 ); -- etldoc: osm_border_linestring_gen_z5 -> boundary_z5 --- etldoc: osm_border_disp_linestring_gen_z5 -> boundary_z5 CREATE OR REPLACE VIEW boundary_z5 AS ( SELECT geometry, @@ -530,27 +498,14 @@ SELECT geometry, adm0_l, adm0_r, disputed, - NULL::text AS disputed_name, - NULL::text AS claimed_by, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, maritime FROM osm_border_linestring_gen_z5 WHERE admin_level <= 4 --- already not included in osm_border_linestring_adm --- AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen_z5) -UNION ALL -SELECT geometry, - admin_level, - NULL::text AS adm0_l, - NULL::text AS adm0_r, - TRUE AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime -FROM osm_border_disp_linestring_gen_z5 ); -- etldoc: osm_border_linestring_gen_z6 -> boundary_z6 --- etldoc: osm_border_disp_linestring_gen_z6 -> boundary_z6 CREATE OR REPLACE VIEW boundary_z6 AS ( SELECT geometry, @@ -558,26 +513,14 @@ SELECT geometry, adm0_l, adm0_r, disputed, - NULL::text AS disputed_name, - NULL::text AS claimed_by, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, maritime FROM osm_border_linestring_gen_z6 WHERE admin_level <= 4 --- AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen_z6) -UNION ALL -SELECT geometry, - admin_level, - NULL::text AS adm0_l, - NULL::text AS adm0_r, - TRUE AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime -FROM osm_border_disp_linestring_gen_z6 ); -- etldoc: osm_border_linestring_gen_z7 -> boundary_z7 --- etldoc: osm_border_disp_linestring_gen_z7 -> boundary_z7 CREATE OR REPLACE VIEW boundary_z7 AS ( SELECT geometry, @@ -585,26 +528,14 @@ SELECT geometry, adm0_l, adm0_r, disputed, - NULL::text AS disputed_name, - NULL::text AS claimed_by, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, maritime FROM osm_border_linestring_gen_z7 WHERE admin_level <= 6 --- AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen_z7) -UNION ALL -SELECT geometry, - admin_level, - NULL::text AS adm0_l, - NULL::text AS adm0_r, - TRUE AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime -FROM osm_border_disp_linestring_gen_z7 ); -- etldoc: osm_border_linestring_gen_z8 -> boundary_z8 --- etldoc: osm_border_disp_linestring_gen_z8 -> boundary_z8 CREATE OR REPLACE VIEW boundary_z8 AS ( SELECT geometry, @@ -612,26 +543,14 @@ SELECT geometry, adm0_l, adm0_r, disputed, - NULL::text AS disputed_name, - NULL::text AS claimed_by, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, maritime FROM osm_border_linestring_gen_z8 WHERE admin_level <= 6 --- AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen_z8) -UNION ALL -SELECT geometry, - admin_level, - NULL::text AS adm0_l, - NULL::text AS adm0_r, - TRUE AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime -FROM osm_border_disp_linestring_gen_z8 ); -- etldoc: osm_border_linestring_gen_z9 -> boundary_z9 --- etldoc: osm_border_disp_linestring_gen_z9 -> boundary_z9 CREATE OR REPLACE VIEW boundary_z9 AS ( SELECT geometry, @@ -639,26 +558,14 @@ SELECT geometry, adm0_l, adm0_r, disputed, - NULL::text AS disputed_name, - NULL::text AS claimed_by, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, maritime FROM osm_border_linestring_gen_z9 WHERE admin_level <= 6 --- AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen_z9) -UNION ALL -SELECT geometry, - admin_level, - NULL::text AS adm0_l, - NULL::text AS adm0_r, - TRUE AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime -FROM osm_border_disp_linestring_gen_z9 ); -- etldoc: osm_border_linestring_gen_z10 -> boundary_z10 --- etldoc: osm_border_disp_linestring_gen_z10 -> boundary_z10 CREATE OR REPLACE VIEW boundary_z10 AS ( SELECT geometry, @@ -666,26 +573,14 @@ SELECT geometry, adm0_l, adm0_r, disputed, - NULL::text AS disputed_name, - NULL::text AS claimed_by, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, maritime FROM osm_border_linestring_gen_z10 WHERE admin_level <= 6 --- AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen_z10) -UNION ALL -SELECT geometry, - admin_level, - NULL::text AS adm0_l, - NULL::text AS adm0_r, - TRUE AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime -FROM osm_border_disp_linestring_gen_z10 ); -- etldoc: osm_border_linestring_gen_z11 -> boundary_z11 --- etldoc: osm_border_disp_linestring_gen_z11 -> boundary_z11 CREATE OR REPLACE VIEW boundary_z11 AS ( SELECT geometry, @@ -693,26 +588,14 @@ SELECT geometry, adm0_l, adm0_r, disputed, - NULL::text AS disputed_name, - NULL::text AS claimed_by, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, maritime FROM osm_border_linestring_gen_z11 WHERE admin_level <= 8 --- AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen_z11) -UNION ALL -SELECT geometry, - admin_level, - NULL::text AS adm0_l, - NULL::text AS adm0_r, - TRUE AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime -FROM osm_border_disp_linestring_gen_z11 ); -- etldoc: osm_border_linestring_gen_z12 -> boundary_z12 --- etldoc: osm_border_disp_linestring_gen_z12 -> boundary_z12 CREATE OR REPLACE VIEW boundary_z12 AS ( SELECT geometry, @@ -720,25 +603,13 @@ SELECT geometry, adm0_l, adm0_r, disputed, - NULL::text AS disputed_name, - NULL::text AS claimed_by, - maritime -FROM osm_border_linestring_gen_z12 ---WHERE osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen_z12) -UNION ALL -SELECT geometry, - admin_level, - NULL::text AS adm0_l, - NULL::text AS adm0_r, - TRUE AS disputed, - edit_name(name) AS disputed_name, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, claimed_by, maritime -FROM osm_border_disp_linestring_gen_z12 +FROM osm_border_linestring_gen_z12 ); -- etldoc: osm_border_linestring_gen_z13 -> boundary_z13 --- etldoc: osm_border_disp_linestring_gen_z13 -> boundary_z13 CREATE OR REPLACE VIEW boundary_z13 AS ( SELECT geometry, @@ -746,21 +617,10 @@ SELECT geometry, adm0_l, adm0_r, disputed, - NULL::text AS disputed_name, - NULL::text AS claimed_by, - maritime -FROM osm_border_linestring_gen_z13 ---WHERE osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen_z13) -UNION ALL -SELECT geometry, - admin_level, - NULL::text AS adm0_l, - NULL::text AS adm0_r, - TRUE AS disputed, - edit_name(name) AS disputed_name, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, claimed_by, maritime -FROM osm_border_disp_linestring_gen_z13 +FROM osm_border_linestring_gen_z13 ); -- etldoc: layer_boundary[shape=record fillcolor=lightpink, style="rounded,filled", diff --git a/layers/boundary/boundary.yaml b/layers/boundary/boundary.yaml index be124af..78f8955 100644 --- a/layers/boundary/boundary.yaml +++ b/layers/boundary/boundary.yaml @@ -1,5 +1,13 @@ 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. Until z4 [Natural Earth data](http://www.naturalearthdata.com/downloads/) is used after which diff --git a/layers/boundary/boundary_name.sql b/layers/boundary/boundary_name.sql index 56e1296..4be7dac 100644 --- a/layers/boundary/boundary_name.sql +++ b/layers/boundary/boundary_name.sql @@ -1,23 +1,25 @@ 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 ST_Node(ST_Collect(geometry)) AS geometry, - maritime, - disputed + 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 + 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 maritime, - disputed + GROUP BY osm_id ), mergedline AS ( - SELECT (ST_Dump( - ST_LineMerge(geometry))).geom AS geometry, + SELECT osm_id, + (ST_Dump(ST_LineMerge(geometry))).geom AS geometry, maritime, disputed FROM multiline @@ -32,7 +34,7 @@ CREATE TABLE IF NOT EXISTS osm_border_linestring_adm AS ( FROM (SELECT ST_Node( ST_Collect(geometry)) AS geometry FROM osm_border_linestring - WHERE admin_level = 2 + WHERE admin_level = 2 AND ST_Dimension(geometry) = 1 ) nodes ) linemerge ), @@ -55,12 +57,14 @@ CREATE TABLE IF NOT EXISTS osm_border_linestring_adm AS ( ), rights AS ( - SELECT adm0_r, + SELECT osm_id, + adm0_r, geometry, maritime, disputed FROM ( - SELECT b.adm0_a3 AS adm0_r, + SELECT a.osm_id AS osm_id, + b.adm0_a3 AS adm0_r, a.geometry, a.maritime, a.disputed @@ -73,14 +77,16 @@ CREATE TABLE IF NOT EXISTS osm_border_linestring_adm AS ( ) line_rights ) - SELECT adm0_l, + SELECT osm_id, + adm0_l, adm0_r, geometry, maritime, 2::integer AS admin_level, disputed FROM ( - SELECT b.adm0_a3 AS adm0_l, + SELECT r.osm_id AS osm_id, + b.adm0_a3 AS adm0_l, r.adm0_r AS adm0_r, r.geometry, r.maritime, @@ -96,4 +102,4 @@ CREATE TABLE IF NOT EXISTS osm_border_linestring_adm AS ( CREATE INDEX IF NOT EXISTS osm_border_linestring_adm_geom_idx ON osm_border_linestring_adm - USING GIST (geometry); \ No newline at end of file + USING GIST (geometry); diff --git a/layers/boundary/etl_diagram.png b/layers/boundary/etl_diagram.png index a4841c2..f45adcf 100644 Binary files a/layers/boundary/etl_diagram.png and b/layers/boundary/etl_diagram.png differ diff --git a/layers/boundary/mapping.yaml b/layers/boundary/mapping.yaml index d2c7abf..b06ce00 100644 --- a/layers/boundary/mapping.yaml +++ b/layers/boundary/mapping.yaml @@ -1,107 +1,34 @@ generalized_tables: - - # etldoc: osm_border_disp_linestring_gen_z2 -> osm_border_disp_linestring_gen_z1 - border_disp_linestring_gen_z1: - source: border_disp_linestring_gen_z2 - sql_filter: admin_level = 2 - tolerance: ZRES2 - - # etldoc: osm_border_disp_linestring_gen_z3 -> osm_border_disp_linestring_gen_z2 - border_disp_linestring_gen_z2: - source: border_disp_linestring_gen_z3 - sql_filter: admin_level = 2 - tolerance: ZRES3 - - # etldoc: osm_border_disp_linestring_gen_z4 -> osm_border_disp_linestring_gen_z3 - border_disp_linestring_gen_z3: - source: border_disp_linestring_gen_z4 - sql_filter: admin_level = 2 - tolerance: ZRES4 - - # etldoc: osm_border_disp_linestring_gen_z5 -> osm_border_disp_linestring_gen_z4 - border_disp_linestring_gen_z4: - source: border_disp_linestring_gen_z5 - sql_filter: admin_level = 2 - tolerance: ZRES5 - - # etldoc: osm_border_disp_linestring_gen_z6 -> osm_border_disp_linestring_gen_z5 - border_disp_linestring_gen_z5: - source: border_disp_linestring_gen_z6 - sql_filter: admin_level = 2 - tolerance: ZRES6 - - # etldoc: osm_border_disp_linestring_gen_z7 -> osm_border_disp_linestring_gen_z6 - border_disp_linestring_gen_z6: - source: border_disp_linestring_gen_z7 - sql_filter: admin_level = 2 - tolerance: ZRES7 - - # etldoc: osm_border_disp_linestring_gen_z8 -> osm_border_disp_linestring_gen_z7 - border_disp_linestring_gen_z7: - source: border_disp_linestring_gen_z8 - sql_filter: admin_level = 2 - tolerance: ZRES8 - - # etldoc: osm_border_disp_linestring_gen_z9 -> osm_border_disp_linestring_gen_z8 - border_disp_linestring_gen_z8: - source: border_disp_linestring_gen_z9 - sql_filter: admin_level = 2 - tolerance: ZRES9 - - # etldoc: osm_border_disp_linestring_gen_z10 -> osm_border_disp_linestring_gen_z9 - border_disp_linestring_gen_z9: - source: border_disp_linestring_gen_z10 - sql_filter: admin_level = 2 - tolerance: ZRES10 - - # etldoc: osm_border_disp_linestring_gen_z11 -> osm_border_disp_linestring_gen_z10 - border_disp_linestring_gen_z10: - source: border_disp_linestring_gen_z11 - sql_filter: admin_level = 2 - tolerance: ZRES11 - - # etldoc: osm_border_disp_linestring_gen_z12 -> osm_border_disp_linestring_gen_z11 - border_disp_linestring_gen_z11: - source: border_disp_linestring_gen_z12 - sql_filter: admin_level = 2 - tolerance: ZRES12 - - # etldoc: osm_border_disp_linestring_gen_z13 -> osm_border_disp_linestring_gen_z12 - border_disp_linestring_gen_z12: - source: border_disp_linestring_gen_z13 - sql_filter: admin_level = 2 - tolerance: ZRES13 - - # etldoc: osm_border_disp_linestring -> osm_border_disp_linestring_gen_z13 - border_disp_linestring_gen_z13: - source: border_disp_linestring - sql_filter: admin_level = 2 - tolerance: ZRES14 - - # etldoc: osm_border_disp_relation -> osm_border_disp_linestring + # etldoc: osm_border_linestring -> osm_border_disp_linestring border_disp_linestring: - source: border_disp_relation - sql_filter: ST_GeometryType(geometry) = 'ST_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 tables: - # etldoc: imposm3 -> osm_border_disp_relation - border_disp_relation: + # 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 - - key: boundary - name: boundary - type: string + # Used for disputed boundary, e.g. "Line of actual control" + from_member: true - key: admin_level name: admin_level type: integer @@ -111,6 +38,19 @@ tables: - 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 @@ -121,16 +61,24 @@ tables: type: member_role - name: type type: member_type + - key: boundary_type + name: boundary_type + type: string + from_member: true + - key: natural + name: natural + type: string + from_member: true + relation_types: [boundary] mapping: - type: [boundary] - filters: - require: - #admin_level: ['2'] # this used to be specified, re-enable if bugs show up with country borders - admin_level: [__any__] - boundary: ['administrative'] # Filters out boundary administrative_fraction and religious_administration + boundary: + - administrative + border_status: + - dispute + boundary_type: + - maritime - -# FOr NUTS in linestring version + # FOr NUTS in linestring version administrative_relation: type: relation columns: @@ -208,34 +156,4 @@ tables: boundary: [ 'administrative' ] filters: require: - admin_level: [ __any__ ] - - -# not currently used -# # etldoc: imposm3 -> osm_adm_boundary_relation -# adm_boundary_relation: -# type: relation -# columns: -# - name: relation_id -# type: id -# - key: name -# name: name -# type: string -# - key: admin_level -# name: admin_level -# type: integer -# - key: border_type -# name: border_type -# type: string -# - key: default_language -# name: default_language -# type: string -# - key: website -# name: website -# type: string -# mapping: -# type: [boundary] -# filters: -# require: -# boundary: ['administrative'] -# admin_level: [__any__] + admin_level: [ __any__ ] \ No newline at end of file diff --git a/layers/boundary/mapping_diagram.png b/layers/boundary/mapping_diagram.png index 1eb7fb2..db85cc4 100644 Binary files a/layers/boundary/mapping_diagram.png 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/building/building.yaml b/layers/building/building.yaml index ea2bc67..2cde58b 100644 --- a/layers/building/building.yaml +++ b/layers/building/building.yaml @@ -1,8 +1,8 @@ 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)). The buildings are not yet ready for 3D rendering support and any help to improve - this is welcomed. + 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 @@ -12,9 +12,9 @@ layer: query: (SELECT osm_id, geometry, render_height, render_min_height, colour, hide_3d 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 after the method of Paul Norman in [OSM Clear](https://github.com/ClearTables/osm-clear). For future 3D rendering of buildings. + An approximated height from levels and height of the building or building:part. render_min_height: | - An approximated height from levels and height of the bottom of the building or building:part after the method of Paul Norman in [OSM Clear](https://github.com/ClearTables/osm-clear). For future 3D rendering of buildings. + An approximated height from minimum levels or minimum height of the bottom of the building or building:part. colour: | Colour hide_3d: | diff --git a/layers/building/etl_diagram.png b/layers/building/etl_diagram.png index 6e16c11..097a964 100644 Binary files a/layers/building/etl_diagram.png and b/layers/building/etl_diagram.png differ diff --git a/layers/building/mapping.yaml b/layers/building/mapping.yaml index 858899b..e32e48e 100644 --- a/layers/building/mapping.yaml +++ b/layers/building/mapping.yaml @@ -60,11 +60,14 @@ tables: 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 @@ -157,4 +160,4 @@ tables: type: member_type mapping: type: [building] - type: relation_member \ No newline at end of file + type: relation_member diff --git a/layers/building/mapping_diagram.png b/layers/building/mapping_diagram.png index f27e789..767b00e 100644 Binary files a/layers/building/mapping_diagram.png 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/housenumber/etl_diagram.png b/layers/housenumber/etl_diagram.png index add427d..d93f971 100644 Binary files a/layers/housenumber/etl_diagram.png and b/layers/housenumber/etl_diagram.png differ diff --git a/layers/housenumber/housenumber.sql b/layers/housenumber/housenumber.sql index 84cb4d8..54cccef 100644 --- a/layers/housenumber/housenumber.sql +++ b/layers/housenumber/housenumber.sql @@ -15,9 +15,19 @@ SELECT osm_id, geometry, housenumber -FROM osm_housenumber_point -WHERE zoom_level >= 14 - AND geometry && bbox; +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 index e3df878..7fa1e6e 100644 --- a/layers/housenumber/housenumber.yaml +++ b/layers/housenumber/housenumber.yaml @@ -3,6 +3,7 @@ layer: 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: diff --git a/layers/housenumber/housenumber_centroid.sql b/layers/housenumber/housenumber_centroid.sql index c7a0bf2..425cd90 100644 --- a/layers/housenumber/housenumber_centroid.sql +++ b/layers/housenumber/housenumber_centroid.sql @@ -6,7 +6,7 @@ CREATE SCHEMA IF NOT EXISTS housenumber; CREATE TABLE IF NOT EXISTS housenumber.osm_ids ( - osm_id bigint + osm_id bigint PRIMARY KEY ); -- etldoc: osm_housenumber_point -> osm_housenumber_point @@ -22,6 +22,16 @@ $$ 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); @@ -31,11 +41,7 @@ SELECT convert_housenumber_point(true); CREATE OR REPLACE FUNCTION housenumber.store() RETURNS trigger AS $$ BEGIN - IF (tg_op = 'DELETE') THEN - INSERT INTO housenumber.osm_ids VALUES (OLD.osm_id); - ELSE - INSERT INTO housenumber.osm_ids VALUES (NEW.osm_id); - END IF; + INSERT INTO housenumber.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; RETURN NULL; END; $$ LANGUAGE plpgsql; @@ -60,6 +66,11 @@ 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; @@ -72,13 +83,13 @@ END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_store - AFTER INSERT OR UPDATE OR DELETE + AFTER INSERT OR UPDATE ON osm_housenumber_point FOR EACH ROW EXECUTE PROCEDURE housenumber.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE + AFTER INSERT OR UPDATE ON osm_housenumber_point FOR EACH STATEMENT EXECUTE PROCEDURE housenumber.flag(); diff --git a/layers/housenumber/mapping.yaml b/layers/housenumber/mapping.yaml index 174335b..4bb0fbe 100644 --- a/layers/housenumber/mapping.yaml +++ b/layers/housenumber/mapping.yaml @@ -12,6 +12,15 @@ tables: - 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: diff --git a/layers/housenumber/mapping_diagram.png b/layers/housenumber/mapping_diagram.png index a30e718..0892169 100644 Binary files a/layers/housenumber/mapping_diagram.png 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/landcover/etl_diagram.png b/layers/landcover/etl_diagram.png index 55c7972..03b3e19 100644 Binary files a/layers/landcover/etl_diagram.png and b/layers/landcover/etl_diagram.png differ diff --git a/layers/landcover/generalized.sql b/layers/landcover/generalized.sql index 66032c6..1dc52f0 100644 --- a/layers/landcover/generalized.sql +++ b/layers/landcover/generalized.sql @@ -13,7 +13,7 @@ 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 -> osm_landcover_gen_z13 +-- etldoc: osm_landcover_polygon -> simplify_vw_z13 CREATE TABLE simplify_vw_z13 AS ( SELECT subclass, @@ -22,32 +22,19 @@ CREATE TABLE simplify_vw_z13 AS ST_SimplifyVW(geometry, power(zres(13),2)), 0.001)) AS geometry FROM osm_landcover_polygon - WHERE ST_Area(geometry) > power(zres(10),2) + 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) < 50 - AND subclass IN ('wood', 'forest')) union_geom50 - 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_z13 - WHERE ST_NPoints(geometry) >= 50 - AND ST_NPoints(geometry) < 300 + WHERE ST_NPoints(geometry) < 300 AND subclass IN ('wood', 'forest')) union_geom300 GROUP BY subclass, cid @@ -62,7 +49,7 @@ SELECT subclass, CREATE INDEX ON osm_landcover_gen_z13 USING GIST (geometry); --- etldoc: osm_landcover_gen_z13 -> osm_landcover_gen_z12 +-- etldoc: simplify_vw_z13 -> simplify_vw_z12 CREATE TABLE simplify_vw_z12 AS ( SELECT subclass, @@ -71,32 +58,19 @@ CREATE TABLE simplify_vw_z12 AS ST_SimplifyVW(geometry, power(zres(12),2)), 0.001)) AS geometry FROM simplify_vw_z13 - WHERE ST_Area(geometry) > power(zres(9),2) + 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) < 50 - AND subclass IN ('wood', 'forest')) union_geom50 - 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_z12 - WHERE ST_NPoints(geometry) >= 50 - AND ST_NPoints(geometry) < 300 + WHERE ST_NPoints(geometry) < 300 AND subclass IN ('wood', 'forest')) union_geom300 GROUP BY subclass, cid @@ -111,7 +85,7 @@ SELECT subclass, CREATE INDEX ON osm_landcover_gen_z12 USING GIST (geometry); --- etldoc: osm_landcover_gen_z12 -> osm_landcover_gen_z11 +-- etldoc: simplify_vw_z12 -> simplify_vw_z11 CREATE TABLE simplify_vw_z11 AS ( SELECT subclass, @@ -120,32 +94,19 @@ CREATE TABLE simplify_vw_z11 AS ST_SimplifyVW(geometry, power(zres(11),2)), 0.001)) AS geometry FROM simplify_vw_z12 - WHERE ST_Area(geometry) > power(zres(8),2) + 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) < 50 - AND subclass IN ('wood', 'forest')) union_geom50 - 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_z11 - WHERE ST_NPoints(geometry) >= 50 - AND ST_NPoints(geometry) < 300 + WHERE ST_NPoints(geometry) < 300 AND subclass IN ('wood', 'forest')) union_geom300 GROUP BY subclass, cid @@ -160,7 +121,7 @@ SELECT subclass, CREATE INDEX ON osm_landcover_gen_z11 USING GIST (geometry); --- etldoc: osm_landcover_gen_z11 -> osm_landcover_gen_z10 +-- etldoc: simplify_vw_z11 -> simplify_vw_z10 CREATE TABLE simplify_vw_z10 AS ( SELECT subclass, @@ -169,32 +130,19 @@ CREATE TABLE simplify_vw_z10 AS ST_SimplifyVW(geometry, power(zres(10),2)), 0.001)) AS geometry FROM simplify_vw_z11 - WHERE ST_Area(geometry) > power(zres(8),2) + 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) < 50 - AND subclass IN ('wood', 'forest')) union_geom50 - 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_z10 - WHERE ST_NPoints(geometry) >= 50 - AND ST_NPoints(geometry) < 300 + WHERE ST_NPoints(geometry) < 300 AND subclass IN ('wood', 'forest')) union_geom300 GROUP BY subclass, cid @@ -209,7 +157,7 @@ SELECT subclass, CREATE INDEX ON osm_landcover_gen_z10 USING GIST (geometry); --- etldoc: osm_landcover_gen_z10 -> osm_landcover_gen_z9 +-- etldoc: simplify_vw_z10 -> simplify_vw_z9 CREATE TABLE simplify_vw_z9 AS ( SELECT subclass, @@ -218,32 +166,19 @@ CREATE TABLE simplify_vw_z9 AS ST_SimplifyVW(geometry, power(zres(9),2)), 0.001)) AS geometry FROM simplify_vw_z10 - WHERE ST_Area(geometry) > power(zres(7),2) + 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) < 50 - AND subclass IN ('wood', 'forest')) union_geom50 - 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) >= 50 - AND ST_NPoints(geometry) < 300 + WHERE ST_NPoints(geometry) < 300 AND subclass IN ('wood', 'forest')) union_geom300 GROUP BY subclass, cid @@ -270,7 +205,7 @@ SELECT subclass, CREATE INDEX ON osm_landcover_gen_z9 USING GIST (geometry); --- etldoc: osm_landcover_gen_z9 -> osm_landcover_gen_z8 +-- etldoc: simplify_vw_z9 -> simplify_vw_z8 CREATE TABLE simplify_vw_z8 AS ( SELECT subclass, @@ -279,10 +214,11 @@ CREATE TABLE simplify_vw_z8 AS ST_SimplifyVW(geometry, power(zres(8),2)), 0.001)) AS geometry FROM simplify_vw_z9 - WHERE ST_Area(geometry) > power(zres(6),2) + 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, @@ -295,6 +231,7 @@ 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 @@ -308,7 +245,7 @@ SELECT subclass, CREATE INDEX ON osm_landcover_gen_z8 USING GIST (geometry); --- etldoc: osm_landcover_gen_z8 -> osm_landcover_gen_z7 +-- etldoc: simplify_vw_z8 -> simplify_vw_z7 CREATE TABLE simplify_vw_z7 AS ( SELECT subclass, @@ -317,10 +254,11 @@ CREATE TABLE simplify_vw_z7 AS ST_SimplifyVW(geometry, power(zres(7),2)), 0.001)) AS geometry FROM simplify_vw_z8 - WHERE ST_Area(geometry) > power(zres(5),2) + 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, diff --git a/layers/landcover/landcover.yaml b/layers/landcover/landcover.yaml index 56536d0..ca876db 100644 --- a/layers/landcover/landcover.yaml +++ b/layers/landcover/landcover.yaml @@ -1,5 +1,12 @@ 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 @@ -19,7 +26,7 @@ layer: rock: subclass: ['bare_rock', 'scree'] grass: - subclass: ['fell', 'grassland', 'heath', 'scrub', 'tundra', 'grass', 'meadow', 'allotments', 'park', 'village_green', 'recreation_ground', 'garden', 'golf_course'] + subclass: ['fell', '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: @@ -39,6 +46,7 @@ layer: - bog - dune - scrub + - shrubbery - farm - farmland - fell diff --git a/layers/landcover/mapping.yaml b/layers/landcover/mapping.yaml index 4707188..351bf3a 100644 --- a/layers/landcover/mapping.yaml +++ b/layers/landcover/mapping.yaml @@ -26,8 +26,6 @@ tables: - forest - village_green - recreation_ground - # There are 600 parks tagged with landuse=park instead of leisure=park - - park natural: - wood - wetland @@ -35,6 +33,7 @@ tables: - grassland - heath - scrub + - shrubbery - tundra - glacier - bare_rock diff --git a/layers/landcover/mapping_diagram.png b/layers/landcover/mapping_diagram.png index 2f693dc..b738d9a 100644 Binary files a/layers/landcover/mapping_diagram.png 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..6b3c567 --- /dev/null +++ b/layers/landcover/style.json @@ -0,0 +1,476 @@ +{ + "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", + "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", + "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", + "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", + "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/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 index a4dfa7c..74be179 100644 Binary files a/layers/landuse/etl_diagram.png and b/layers/landuse/etl_diagram.png differ diff --git a/layers/landuse/landuse.sql b/layers/landuse/landuse.sql index d578a85..35fa2f2 100644 --- a/layers/landuse/landuse.sql +++ b/layers/landuse/landuse.sql @@ -37,6 +37,188 @@ 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+" ] ; @@ -51,7 +233,8 @@ AS $$ SELECT osm_id, geometry, - COALESCE( + landuse_unify( + COALESCE( NULLIF(landuse, ''), NULLIF(amenity, ''), NULLIF(leisure, ''), @@ -59,7 +242,7 @@ SELECT osm_id, NULLIF(place, ''), NULLIF(waterway, ''), NULLIF(man_made, '') - ) AS class + )) AS class FROM ( -- etldoc: ne_50m_urban_areas_gen_z4 -> layer_landuse:z4 SELECT osm_id, @@ -87,7 +270,7 @@ FROM ( FROM ne_50m_urban_areas_gen_z5 WHERE zoom_level = 5 UNION ALL - -- etldoc: osm_landuse_polygon_gen_z6 -> layer_landuse:z6 + -- etldoc: osm_landuse_polygon_gen_z6_union -> layer_landuse:z6 SELECT osm_id, geometry, landuse, @@ -97,10 +280,10 @@ FROM ( place, waterway, man_made - FROM osm_landuse_polygon_gen_z6 + FROM osm_landuse_polygon_gen_z6_union WHERE zoom_level = 6 UNION ALL - -- etldoc: osm_landuse_polygon_gen_z7 -> layer_landuse:z7 + -- etldoc: osm_landuse_polygon_gen_z7_union -> layer_landuse:z7 SELECT osm_id, geometry, landuse, @@ -110,10 +293,10 @@ FROM ( place, waterway, man_made - FROM osm_landuse_polygon_gen_z7 + FROM osm_landuse_polygon_gen_z7_union WHERE zoom_level = 7 UNION ALL - -- etldoc: osm_landuse_polygon_gen_z8 -> layer_landuse:z8 + -- etldoc: osm_landuse_polygon_gen_z8_union -> layer_landuse:z8 SELECT osm_id, geometry, landuse, @@ -123,10 +306,10 @@ FROM ( place, waterway, man_made - FROM osm_landuse_polygon_gen_z8 + FROM osm_landuse_polygon_gen_z8_union WHERE zoom_level = 8 UNION ALL - -- etldoc: osm_landuse_polygon_gen_z9 -> layer_landuse:z9 + -- etldoc: osm_landuse_polygon_gen_z9_union -> layer_landuse:z9 SELECT osm_id, geometry, landuse, @@ -136,10 +319,10 @@ FROM ( place, waterway, man_made - FROM osm_landuse_polygon_gen_z9 + FROM osm_landuse_polygon_gen_z9_union WHERE zoom_level = 9 UNION ALL - -- etldoc: osm_landuse_polygon_gen_z10 -> layer_landuse:z10 + -- etldoc: osm_landuse_polygon_gen_z10_union -> layer_landuse:z10 SELECT osm_id, geometry, landuse, @@ -149,10 +332,10 @@ FROM ( place, waterway, man_made - FROM osm_landuse_polygon_gen_z10 + FROM osm_landuse_polygon_gen_z10_union WHERE zoom_level = 10 UNION ALL - -- etldoc: osm_landuse_polygon_gen_z11 -> layer_landuse:z11 + -- etldoc: osm_landuse_polygon_gen_z11_union -> layer_landuse:z11 SELECT osm_id, geometry, landuse, @@ -162,10 +345,10 @@ FROM ( place, waterway, man_made - FROM osm_landuse_polygon_gen_z11 + FROM osm_landuse_polygon_gen_z11_union WHERE zoom_level = 11 UNION ALL - -- etldoc: osm_landuse_polygon_gen_z12 -> layer_landuse:z12 + -- etldoc: osm_landuse_polygon_gen_z12_union -> layer_landuse:z12 SELECT osm_id, geometry, landuse, @@ -175,7 +358,7 @@ FROM ( place, waterway, man_made - FROM osm_landuse_polygon_gen_z12 + FROM osm_landuse_polygon_gen_z12_union WHERE zoom_level = 12 UNION ALL -- etldoc: osm_landuse_polygon_gen_z13 -> layer_landuse:z13 diff --git a/layers/landuse/landuse.yaml b/layers/landuse/landuse.yaml index bec0f6c..63182d2 100644 --- a/layers/landuse/landuse.yaml +++ b/layers/landuse/landuse.yaml @@ -1,5 +1,8 @@ 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. @@ -49,10 +52,13 @@ layer: - prison - wastewater_plant - water_works + - 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 diff --git a/layers/landuse/mapping.yaml b/layers/landuse/mapping.yaml index 4811850..56859b7 100644 --- a/layers/landuse/mapping.yaml +++ b/layers/landuse/mapping.yaml @@ -77,6 +77,7 @@ tables: - railway - cemetery - military + - quarry # zoning - residential - commercial @@ -98,6 +99,7 @@ tables: - motorcycle_parking - bicycle_parking - animal_training + - grave_yard leisure: - stadium - pitch diff --git a/layers/landuse/mapping_diagram.png b/layers/landuse/mapping_diagram.png index b4d6405..f9a8245 100644 Binary files a/layers/landuse/mapping_diagram.png 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/etl_diagram.png b/layers/mountain_peak/etl_diagram.png index e9a5bd7..0f5c6fd 100644 Binary files a/layers/mountain_peak/etl_diagram.png and b/layers/mountain_peak/etl_diagram.png differ diff --git a/layers/mountain_peak/mapping.yaml b/layers/mountain_peak/mapping.yaml index 84a0146..21952d1 100644 --- a/layers/mountain_peak/mapping.yaml +++ b/layers/mountain_peak/mapping.yaml @@ -29,3 +29,32 @@ tables: 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 index e0c5ceb..9e8a3e2 100644 Binary files a/layers/mountain_peak/mapping_diagram.png 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 index b6f6752..cf91e01 100644 --- a/layers/mountain_peak/mountain_peak.sql +++ b/layers/mountain_peak/mountain_peak.sql @@ -1,26 +1,46 @@ +-- 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+" ] ; +-- 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, - "rank" int + 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: osm_peak_point -> layer_mountain_peak:z7_ + -- etldoc: peak_point -> layer_mountain_peak:z7_ osm_id, geometry, name, @@ -30,6 +50,7 @@ SELECT tags, ele::int, ele_ft::int, + customary_ft, rank::int FROM ( SELECT osm_id, @@ -40,21 +61,56 @@ FROM ( 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 ( substring(ele FROM E'^(-?\\d+)(\\D|$)')::int + - (CASE WHEN NULLIF(wikipedia, '') IS NOT NULL THEN 10000 ELSE 0 END) + - (CASE WHEN NULLIF(name, '') IS NOT NULL THEN 10000 ELSE 0 END) + (CASE WHEN wikipedia <> '' THEN 10000 ELSE 0 END) + + (CASE WHEN name <> '' THEN 10000 ELSE 0 END) ) DESC )::int AS "rank" - FROM osm_peak_point + FROM peak_point WHERE geometry && bbox AND ele IS NOT NULL AND ele ~ E'^-?\\d{1,4}(\\D|$)' ) 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 diff --git a/layers/mountain_peak/mountain_peak.yaml b/layers/mountain_peak/mountain_peak.yaml index bcb5d74..a7c52db 100644 --- a/layers/mountain_peak/mountain_peak.yaml +++ b/layers/mountain_peak/mountain_peak.yaml @@ -1,5 +1,8 @@ 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 @@ -10,21 +13,32 @@ layer: name_de: German name `name:de` if available, otherwise `name` or `name:en`. class: description: | - Use the **class** to differentiate between mountain peak and volcano. + 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 feets. + 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, rank FROM layer_mountain_peak(!bbox!, z(!scale_denominator!), !pixel_width!)) AS t + 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 diff --git a/layers/mountain_peak/style.json b/layers/mountain_peak/style.json new file mode 100644 index 0000000..f47736d --- /dev/null +++ b/layers/mountain_peak/style.json @@ -0,0 +1,52 @@ +{ + "layers": [ + { + "id": "mountain_peak", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "mountain_peak", + "maxzoom": 16, + "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 + }, + "filter": [ + "all", + [ + "!=", + "class", + "cliff" + ] + ], + "order": 197 + } + ] +} \ 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 index cda77f3..7346538 100644 --- a/layers/mountain_peak/update_peak_point.sql +++ b/layers/mountain_peak/update_peak_point.sql @@ -6,7 +6,7 @@ CREATE SCHEMA IF NOT EXISTS mountain_peak_point; CREATE TABLE IF NOT EXISTS mountain_peak_point.osm_ids ( - osm_id bigint + osm_id bigint PRIMARY KEY ); -- etldoc: osm_peak_point -> osm_peak_point @@ -26,11 +26,7 @@ SELECT update_osm_peak_point(true); CREATE OR REPLACE FUNCTION mountain_peak_point.store() RETURNS trigger AS $$ BEGIN - IF (tg_op = 'DELETE') THEN - INSERT INTO mountain_peak_point.osm_ids VALUES (OLD.osm_id); - ELSE - INSERT INTO mountain_peak_point.osm_ids VALUES (NEW.osm_id); - END IF; + INSERT INTO mountain_peak_point.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; RETURN NULL; END; $$ LANGUAGE plpgsql; @@ -55,6 +51,11 @@ 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; @@ -67,13 +68,13 @@ END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_store - AFTER INSERT OR UPDATE OR DELETE + 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 OR DELETE + AFTER INSERT OR UPDATE ON osm_peak_point FOR EACH STATEMENT EXECUTE PROCEDURE mountain_peak_point.flag(); diff --git a/layers/park/etl_diagram.png b/layers/park/etl_diagram.png index 82e6ccd..3a27352 100644 Binary files a/layers/park/etl_diagram.png and b/layers/park/etl_diagram.png differ diff --git a/layers/park/mapping.yaml b/layers/park/mapping.yaml index 0fa51e2..4a95569 100644 --- a/layers/park/mapping.yaml +++ b/layers/park/mapping.yaml @@ -1,33 +1,45 @@ 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: ZRES8 + 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: ZRES8 + 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: ZRES9 + 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: ZRES10 + 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: ZRES11 + tolerance: ZRES10 # etldoc: osm_park_polygon_gen_z12 -> osm_park_polygon_gen_z11 park_polygon_gen_z11: @@ -89,3 +101,4 @@ tables: boundary: - national_park - protected_area + - aboriginal_lands diff --git a/layers/park/mapping_diagram.png b/layers/park/mapping_diagram.png index 936755c..45c2c19 100644 Binary files a/layers/park/mapping_diagram.png and b/layers/park/mapping_diagram.png differ diff --git a/layers/park/park.sql b/layers/park/park.sql index 91e9193..bf2f07d 100644 --- a/layers/park/park.sql +++ b/layers/park/park.sql @@ -1,5 +1,5 @@ -- etldoc: layer_park[shape=record fillcolor=lightpink, style="rounded,filled", --- etldoc: label="layer_park | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; +-- 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 @@ -18,25 +18,54 @@ $$ SELECT osm_id, geometry, class, - name, - name_en, - name_de, + 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, + CASE WHEN boundary='aboriginal_lands' THEN 'aboriginal_lands' + ELSE COALESCE( + LOWER(REPLACE(NULLIF(protection_title, ''), ' ', '_')), + NULLIF(boundary, ''), + NULLIF(leisure, '') + ) END 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, + CASE WHEN boundary='aboriginal_lands' THEN boundary END 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, @@ -184,6 +213,23 @@ FROM ( 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, diff --git a/layers/park/park.yaml b/layers/park/park.yaml index b1789f1..f617e95 100644 --- a/layers/park/park.yaml +++ b/layers/park/park.yaml @@ -17,6 +17,7 @@ layer: `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. + The class for `boundary=aboriginal_lands` is `aboriginal_lands`. name: The OSM [`name`](http://wiki.openstreetmap.org/wiki/Key:name) value of the park (point features only). name_en: English name `name:en` if available, otherwise `name` (point features only). name_de: German name `name:de` if available, otherwise `name` or `name:en` (point features only). 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 index 3f30b6f..f4d5bde 100644 --- a/layers/park/update_park_polygon.sql +++ b/layers/park/update_park_polygon.sql @@ -16,6 +16,26 @@ 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; @@ -26,6 +46,10 @@ 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 tigger_flag ON osm_park_polygon; +DROP TRIGGER IF EXISTS tigger_refresh ON park_polygon.updates; -- etldoc: osm_park_polygon -> osm_park_polygon -- etldoc: osm_park_polygon_gen_z13 -> osm_park_polygon_gen_z13 @@ -36,6 +60,8 @@ DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z6; -- 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 @@ -75,6 +101,11 @@ BEGIN SET tags = update_tags(tags, geometry), geometry_point = st_centroid(geometry); + UPDATE osm_park_polygon_gen_z5 + SET tags = update_tags(tags, geometry), + geometry_point = st_centroid(geometry); + + REFRESH MATERIALIZED VIEW CONCURRENTLY osm_park_polygon_dissolve_z4; END; $$ LANGUAGE plpgsql; @@ -88,7 +119,45 @@ CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z9_point_geom_idx ON osm_park_po 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 @@ -101,6 +170,16 @@ BEGIN 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 @@ -154,3 +233,28 @@ CREATE TRIGGER update_row 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_gen_z4 + 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/capital.sql b/layers/place/capital.sql index 3375647..1122109 100644 --- a/layers/place/capital.sql +++ b/layers/place/capital.sql @@ -2,8 +2,8 @@ CREATE OR REPLACE FUNCTION normalize_capital_level(capital text) RETURNS int AS $$ SELECT CASE - WHEN capital IN ('yes', '2') THEN 2 - WHEN capital = '4' THEN 4 + WHEN capital = 'yes' THEN 2 + WHEN capital IN ('2', '3', '4', '5', '6') THEN capital::int END; $$ LANGUAGE SQL IMMUTABLE STRICT diff --git a/layers/place/etl_diagram.png b/layers/place/etl_diagram.png index 41b8c26..de2cb08 100644 Binary files a/layers/place/etl_diagram.png and b/layers/place/etl_diagram.png differ diff --git a/layers/place/mapping.yaml b/layers/place/mapping.yaml index efa1788..ff007b1 100644 --- a/layers/place/mapping.yaml +++ b/layers/place/mapping.yaml @@ -124,6 +124,9 @@ tables: - *name_de - name: tags type: hstore_tags + - name: place + key: place + type: string - name: is_in_country key: is_in:country type: string @@ -140,6 +143,7 @@ tables: mapping: place: - state + - province # etldoc: imposm3 -> osm_city_point city_point: @@ -173,6 +177,7 @@ tables: - town - village - hamlet + - borough - suburb - quarter - neighbourhood diff --git a/layers/place/mapping_diagram.png b/layers/place/mapping_diagram.png index 5f21377..48606cf 100644 Binary files a/layers/place/mapping_diagram.png and b/layers/place/mapping_diagram.png differ diff --git a/layers/place/place.sql b/layers/place/place.sql index f394d23..c232124 100644 --- a/layers/place/place.sql +++ b/layers/place/place.sql @@ -70,7 +70,7 @@ FROM ( COALESCE(NULLIF(name_en, ''), name) AS name_en, COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, tags, - 'state' AS class, + place::text AS class, "rank", NULL::int AS capital, NULL::text AS iso_a2 diff --git a/layers/place/place.yaml b/layers/place/place.yaml index b02cfa5..aba01f2 100644 --- a/layers/place/place.yaml +++ b/layers/place/place.yaml @@ -1,8 +1,14 @@ layer: id: "place" + requires: + tables: + - ne_10m_admin_1_states_provinces + - ne_10m_admin_0_countries + - ne_10m_populated_places 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) and [cities](http://wiki.openstreetmap.org/wiki/Key:place). + [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: @@ -14,12 +20,12 @@ layer: 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, 4] + 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 and + 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. @@ -27,14 +33,17 @@ layer: - continent - country - state + - province - city - town - village - hamlet + - borough - suburb - quarter - neighbourhood - isolated_dwelling + - island 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`. @@ -44,7 +53,7 @@ layer: description: | Countries, states and the most important cities all have a **rank** to boost their importance on the map. - The **rank** field for counries and states ranges from + 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 diff --git a/layers/place/style.json b/layers/place/style.json new file mode 100644 index 0000000..c1564dc --- /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": 198 + }, + { + "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": 199 + }, + { + "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": 200 + }, + { + "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": 201 + }, + { + "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": 202 + }, + { + "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": 203 + }, + { + "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": 204 + }, + { + "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": 205 + }, + { + "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": 206 + } + ] +} \ No newline at end of file diff --git a/layers/place/types.sql b/layers/place/types.sql index 9e34ca9..2fac395 100644 --- a/layers/place/types.sql +++ b/layers/place/types.sql @@ -1,9 +1,10 @@ DO $$ BEGIN - IF NOT EXISTS(SELECT 1 FROM pg_type WHERE typname = 'city_place') THEN - CREATE TYPE city_place AS enum ('city', 'town', 'village', 'hamlet', 'suburb', 'quarter', 'neighbourhood', 'isolated_dwelling'); - END IF; + 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 $$; diff --git a/layers/place/update_city_point.sql b/layers/place/update_city_point.sql index 6a7ec2a..5012376 100644 --- a/layers/place/update_city_point.sql +++ b/layers/place/update_city_point.sql @@ -8,7 +8,7 @@ CREATE SCHEMA IF NOT EXISTS place_city; CREATE TABLE IF NOT EXISTS place_city.osm_ids ( - osm_id bigint + osm_id bigint PRIMARY KEY ); CREATE OR REPLACE FUNCTION update_osm_city_point(full_update boolean) RETURNS void AS @@ -23,8 +23,8 @@ $$ 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.gn_ascii), lower(ne.nameascii)) OR - lower(osm.name_en) IN (lower(ne.name), lower(ne.namealt), lower(ne.meganame), lower(ne.gn_ascii), lower(ne.nameascii)) 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') @@ -56,11 +56,7 @@ CREATE INDEX IF NOT EXISTS osm_city_point_rank_idx ON osm_city_point ("rank"); CREATE OR REPLACE FUNCTION place_city.store() RETURNS trigger AS $$ BEGIN - IF (tg_op = 'DELETE') THEN - INSERT INTO place_city.osm_ids VALUES (OLD.osm_id); - ELSE - INSERT INTO place_city.osm_ids VALUES (NEW.osm_id); - END IF; + INSERT INTO place_city.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; RETURN NULL; END; $$ LANGUAGE plpgsql; @@ -85,6 +81,11 @@ 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; @@ -97,13 +98,13 @@ END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_store - AFTER INSERT OR UPDATE OR DELETE + AFTER INSERT OR UPDATE ON osm_city_point FOR EACH ROW EXECUTE PROCEDURE place_city.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE + AFTER INSERT OR UPDATE ON osm_city_point FOR EACH STATEMENT EXECUTE PROCEDURE place_city.flag(); diff --git a/layers/place/update_continent_point.sql b/layers/place/update_continent_point.sql index 5e944a2..3d92063 100644 --- a/layers/place/update_continent_point.sql +++ b/layers/place/update_continent_point.sql @@ -6,7 +6,7 @@ CREATE SCHEMA IF NOT EXISTS place_continent_point; CREATE TABLE IF NOT EXISTS place_continent_point.osm_ids ( - osm_id bigint + osm_id bigint PRIMARY KEY ); -- etldoc: osm_continent_point -> osm_continent_point @@ -26,11 +26,7 @@ SELECT update_osm_continent_point(true); CREATE OR REPLACE FUNCTION place_continent_point.store() RETURNS trigger AS $$ BEGIN - IF (tg_op = 'DELETE') THEN - INSERT INTO place_continent_point.osm_ids VALUES (OLD.osm_id); - ELSE - INSERT INTO place_continent_point.osm_ids VALUES (NEW.osm_id); - END IF; + INSERT INTO place_continent_point.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; RETURN NULL; END; $$ LANGUAGE plpgsql; @@ -55,6 +51,11 @@ 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; @@ -67,13 +68,13 @@ END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_store - AFTER INSERT OR UPDATE OR DELETE + 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 OR DELETE + AFTER INSERT OR UPDATE ON osm_continent_point FOR EACH STATEMENT EXECUTE PROCEDURE place_continent_point.flag(); diff --git a/layers/place/update_country_point.sql b/layers/place/update_country_point.sql index a4cc79d..dc3a7c3 100644 --- a/layers/place/update_country_point.sql +++ b/layers/place/update_country_point.sql @@ -6,7 +6,7 @@ CREATE SCHEMA IF NOT EXISTS place_country; CREATE TABLE IF NOT EXISTS place_country.osm_ids ( - osm_id bigint + osm_id bigint PRIMARY KEY ); -- etldoc: ne_10m_admin_0_countries -> osm_country_point @@ -105,11 +105,7 @@ CREATE INDEX IF NOT EXISTS osm_country_point_rank_idx ON osm_country_point ("ran CREATE OR REPLACE FUNCTION place_country.store() RETURNS trigger AS $$ BEGIN - IF (tg_op = 'DELETE') THEN - INSERT INTO place_country.osm_ids VALUES (OLD.osm_id); - ELSE - INSERT INTO place_country.osm_ids VALUES (NEW.osm_id); - END IF; + INSERT INTO place_country.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; RETURN NULL; END; $$ LANGUAGE plpgsql; @@ -134,6 +130,11 @@ 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; @@ -146,13 +147,13 @@ END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_store - AFTER INSERT OR UPDATE OR DELETE + AFTER INSERT OR UPDATE ON osm_country_point FOR EACH ROW EXECUTE PROCEDURE place_country.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE + AFTER INSERT OR UPDATE ON osm_country_point FOR EACH STATEMENT EXECUTE PROCEDURE place_country.flag(); diff --git a/layers/place/update_island_point.sql b/layers/place/update_island_point.sql index 7f57d32..3503464 100644 --- a/layers/place/update_island_point.sql +++ b/layers/place/update_island_point.sql @@ -6,7 +6,7 @@ CREATE SCHEMA IF NOT EXISTS place_island_point; CREATE TABLE IF NOT EXISTS place_island_point.osm_ids ( - osm_id bigint + osm_id bigint PRIMARY KEY ); -- etldoc: osm_island_point -> osm_island_point @@ -26,11 +26,7 @@ SELECT update_osm_island_point(true); CREATE OR REPLACE FUNCTION place_island_point.store() RETURNS trigger AS $$ BEGIN - IF (tg_op = 'DELETE') THEN - INSERT INTO place_island_point.osm_ids VALUES (OLD.osm_id); - ELSE - INSERT INTO place_island_point.osm_ids VALUES (NEW.osm_id); - END IF; + INSERT INTO place_island_point.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; RETURN NULL; END; $$ LANGUAGE plpgsql; @@ -55,6 +51,11 @@ 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; @@ -67,13 +68,13 @@ END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_store - AFTER INSERT OR UPDATE OR DELETE + 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 OR DELETE + AFTER INSERT OR UPDATE ON osm_island_point FOR EACH STATEMENT EXECUTE PROCEDURE place_island_point.flag(); diff --git a/layers/place/update_island_polygon.sql b/layers/place/update_island_polygon.sql index 2644f35..23e76c7 100644 --- a/layers/place/update_island_polygon.sql +++ b/layers/place/update_island_polygon.sql @@ -6,7 +6,7 @@ CREATE SCHEMA IF NOT EXISTS place_island_polygon; CREATE TABLE IF NOT EXISTS place_island_polygon.osm_ids ( - osm_id bigint + osm_id bigint PRIMARY KEY ); -- etldoc: osm_island_polygon -> osm_island_polygon @@ -33,11 +33,7 @@ SELECT update_osm_island_polygon(true); CREATE OR REPLACE FUNCTION place_island_polygon.store() RETURNS trigger AS $$ BEGIN - IF (tg_op = 'DELETE') THEN - INSERT INTO place_island_polygon.osm_ids VALUES (OLD.osm_id); - ELSE - INSERT INTO place_island_polygon.osm_ids VALUES (NEW.osm_id); - END IF; + INSERT INTO place_island_polygon.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; RETURN NULL; END; $$ LANGUAGE plpgsql; @@ -62,6 +58,11 @@ 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; @@ -74,13 +75,13 @@ END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_store - AFTER INSERT OR UPDATE OR DELETE + 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 OR DELETE + AFTER INSERT OR UPDATE ON osm_island_polygon FOR EACH STATEMENT EXECUTE PROCEDURE place_island_polygon.flag(); diff --git a/layers/place/update_state_point.sql b/layers/place/update_state_point.sql index d1ba1bd..234851e 100644 --- a/layers/place/update_state_point.sql +++ b/layers/place/update_state_point.sql @@ -6,7 +6,7 @@ CREATE SCHEMA IF NOT EXISTS place_state; CREATE TABLE IF NOT EXISTS place_state.osm_ids ( - osm_id bigint + osm_id bigint PRIMARY KEY ); -- etldoc: ne_10m_admin_1_states_provinces -> osm_state_point @@ -29,8 +29,8 @@ $$ -- because name matching is difficult ST_Within(osm.geometry, ne.geometry) -- We leave out leess important states - AND ne.scalerank <= 3 - AND ne.labelrank <= 2 + 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. @@ -67,11 +67,7 @@ CREATE INDEX IF NOT EXISTS osm_state_point_rank_idx ON osm_state_point ("rank"); CREATE OR REPLACE FUNCTION place_state.store() RETURNS trigger AS $$ BEGIN - IF (tg_op = 'DELETE') THEN - INSERT INTO place_state.osm_ids VALUES (OLD.osm_id); - ELSE - INSERT INTO place_state.osm_ids VALUES (NEW.osm_id); - END IF; + INSERT INTO place_state.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; RETURN NULL; END; $$ LANGUAGE plpgsql; @@ -96,6 +92,11 @@ 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; @@ -108,13 +109,13 @@ END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_store - AFTER INSERT OR UPDATE OR DELETE + AFTER INSERT OR UPDATE ON osm_state_point FOR EACH ROW EXECUTE PROCEDURE place_state.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE + AFTER INSERT OR UPDATE ON osm_state_point FOR EACH STATEMENT EXECUTE PROCEDURE place_state.flag(); diff --git a/layers/poi/etl_diagram.png b/layers/poi/etl_diagram.png index bbabf80..db51621 100644 Binary files a/layers/poi/etl_diagram.png and b/layers/poi/etl_diagram.png differ diff --git a/layers/poi/mapping.yaml b/layers/poi/mapping.yaml index f99769a..89b88f8 100644 --- a/layers/poi/mapping.yaml +++ b/layers/poi/mapping.yaml @@ -9,6 +9,7 @@ def_poi_mapping_aerialway: &poi_mapping_aerialway # amenity values , see http://taginfo.openstreetmap.org/keys/amenity#values def_poi_mapping_amenity: &poi_mapping_amenity - arts_centre + - atm - bank - bar - bbq @@ -26,7 +27,6 @@ def_poi_mapping_amenity: &poi_mapping_amenity - dentist - doctors - drinking_water - - embassy - fast_food - ferry_terminal - fire_station @@ -45,6 +45,7 @@ def_poi_mapping_amenity: &poi_mapping_amenity - pharmacy - place_of_worship - police + - parcel_locker - post_box - post_office - prison @@ -121,6 +122,10 @@ def_poi_mapping_leisure: &poi_mapping_leisure - water_park - nature_reserve +# office values , see http://taginfo.openstreetmap.org/keys/office#values +def_poi_mapping_office: &poi_mapping_office + - diplomatic + # railway values , see http://taginfo.openstreetmap.org/keys/railway#values def_poi_mapping_railway: &poi_mapping_railway - halt @@ -184,6 +189,7 @@ def_poi_mapping_shop: &poi_mapping_shop - kiosk - lamps - laundry + - locksmith - mall - massage - mobile_phone @@ -345,6 +351,8 @@ def_poi_fields: &poi_fields type: id - name: geometry type: geometry + - name: area + type: area - name: name key: name type: string @@ -372,6 +380,9 @@ def_poi_fields: &poi_fields - name: uic_ref key: uic_ref type: string + - name: ref + key: ref + type: string - name: religion key: religion type: string @@ -393,6 +404,15 @@ def_poi_fields: &poi_fields - name: source key: "generator:source" type: string + - name: operator + key: operator + type: string + - name: network + key: network + type: string + - name: brand + key: brand + type: string def_poi_mapping: &poi_mapping aerialway: *poi_mapping_aerialway @@ -403,6 +423,7 @@ def_poi_mapping: &poi_mapping 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 diff --git a/layers/poi/mapping_diagram.png b/layers/poi/mapping_diagram.png index 682751c..fcfbf43 100644 Binary files a/layers/poi/mapping_diagram.png and b/layers/poi/mapping_diagram.png differ diff --git a/layers/poi/poi.sql b/layers/poi/poi.sql index 8aa92ca..cc7eeaf 100644 --- a/layers/poi/poi.sql +++ b/layers/poi/poi.sql @@ -94,20 +94,20 @@ FROM ( -- etldoc: osm_poi_polygon -> layer_poi:z12 -- etldoc: osm_poi_polygon -> layer_poi:z13 - 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 zoom_level BETWEEN 12 AND 13 - AND ((subclass = 'station' AND mapping_key = 'railway') - OR subclass IN ('halt', 'ferry_terminal')) - - UNION ALL - +-- OLD +-- 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 zoom_level BETWEEN 12 AND 13 +-- AND ((subclass = 'station' AND mapping_key = 'railway') +-- OR subclass IN ('halt', 'ferry_terminal')) +-- +-- UNION ALL -- etldoc: osm_poi_polygon -> layer_poi:z14_ SELECT *, NULL::integer AS agg_stop, @@ -116,8 +116,21 @@ FROM ( ELSE osm_id * 10 + 1 END AS osm_id_hash FROM osm_poi_polygon - WHERE geometry && bbox - AND zoom_level >= 14 + 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_raw WHERE NOT (mapping_key = 'building' AND (subclass = 'office' OR subclass = 'industrial') AND coalesce(name, name_en, '') = '') ) AS poi_union diff --git a/layers/poi/poi.yaml b/layers/poi/poi.yaml index e4770aa..6837ff8 100644 --- a/layers/poi/poi.yaml +++ b/layers/poi/poi.yaml @@ -18,37 +18,39 @@ layer: values: shop: subclass: ['accessories', 'antiques', 'beauty', 'bed', 'boutique', 'camera', 'carpet', 'charity', 'chemist', - 'coffee', 'computer', 'convenience', 'copyshop', 'cosmetics', - 'erotic', 'fabric', 'frozen_food', 'video_games', 'video', 'general', 'gift', - 'hearing_aids', 'hifi', 'interior_decoration', 'kiosk', 'lamps', 'mall', 'massage','outdoor', - 'perfumery', 'perfume', 'pet', 'photo', 'second_hand', 'sports', 'stationery', 'tailor', 'tattoo', - 'ticket', 'tobacco', 'travel_agency', 'watches', 'weapons', 'wholesale'] + 'coffee', 'computer', 'convenience', 'copyshop', 'cosmetics', 'garden_centre', 'doityourself', + 'erotic', 'electronics', 'fabric', 'florist', 'frozen_food', 'furniture', 'video_games', 'video', + 'general', 'gift', 'hardware', 'hearing_aids', 'hifi', 'ice_cream', 'interior_decoration', + 'jewelry', 'kiosk', 'locksmith', 'lamps', 'mall', 'massage', 'motorcycle', 'mobile_phone', + 'newsagent', 'optician', 'outdoor', 'perfumery', 'perfume', 'pet', 'photo', 'second_hand', 'shoes', 'sports', + 'stationery', 'tailor', 'tattoo', 'ticket', 'tobacco', 'toys', 'travel_agency', + 'watches', ''weapons', 'wholesale'] optician: - subclass: ['optician'] + subclass: [ 'optician' ] toys: - subclass: ['toys'] + subclass: [ 'toys' ] jewelry: - subclass: ['jewelry'] + subclass: [ 'jewelry' ] furniture: - subclass: ['furniture'] + subclass: [ 'furniture' ] newsagent: - subclass: ['newsagent'] + subclass: [ 'newsagent' ] paint: - subclass: ['paint'] + subclass: [ 'paint' ] beverages: - subclass: ['beverages'] + subclass: [ 'beverages' ] electronics: - subclass: ['electronics'] + subclass: [ 'electronics' ] garden_centre: - subclass: ['garden_centre'] + subclass: [ 'garden_centre' ] mobile_phone: - subclass: ['mobile_phone'] + subclass: [ 'mobile_phone' ] shoes: - subclass: ['shoes'] + subclass: [ 'shoes' ] hardware: - subclass: ['hardware', 'doityourself'] + subclass: [ 'hardware', 'doityourself' ] florist: - subclass: ['florist'] + subclass: [ 'florist' ] town_hall: subclass: ['townhall', 'public_building', 'courthouse'] community_centre: @@ -87,7 +89,7 @@ layer: ice_cream: subclass: ['chocolate', 'confectionery', 'ice_cream'] post: - subclass: ['post_box', 'post_office'] + subclass: ['post_box', 'post_office', 'parcel_locker'] cafe: subclass: ['cafe'] school: @@ -126,25 +128,27 @@ layer: subclass: ['swimming_area', 'swimming'] castle: subclass: ['castle', 'ruins'] + atm: + subclass: ['atm'] airport: - subclass: ['aerodrome'] + subclass: [ 'aerodrome' ] heliport: - subclass: ['helipad'] + subclass: [ 'helipad' ] wind_turbine: __AND__: - subclass: ['generator'] - subtype: ['wind'] - mapping_key: ['power'] + subclass: [ 'generator' ] + subtype: [ 'wind' ] + mapping_key: [ 'power' ] communications_tower: - subclass: ['communications_tower'] + subclass: [ 'communications_tower' ] water_tower: - subclass: ['water_tower'] + subclass: [ 'water_tower' ] wind_mill: - subclass: ['wind_mill'] + subclass: [ 'wind_mill' ] power_tower: - subclass: ['tower'] + subclass: [ 'tower' ] industry: - subclass: ['industrial'] + subclass: [ 'industrial' ] subclass: description: | Original value of either the @@ -162,7 +166,8 @@ layer: [`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) + [`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: | diff --git a/layers/poi/poi_stop_agg.sql b/layers/poi/poi_stop_agg.sql index 5d629b0..39fb170 100644 --- a/layers/poi/poi_stop_agg.sql +++ b/layers/poi/poi_stop_agg.sql @@ -1,3 +1,4 @@ +-- 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 ( @@ -5,12 +6,14 @@ SELECT uic_ref, count(*) AS count, CASE WHEN count(*) > 2 THEN ST_Centroid(ST_UNION(geometry)) END AS centroid FROM osm_poi_point -WHERE nullif(uic_ref, '') IS NOT NULL +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 ( diff --git a/layers/poi/public_transport_stop_type.sql b/layers/poi/public_transport_stop_type.sql index fa37541..bb3239d 100644 --- a/layers/poi/public_transport_stop_type.sql +++ b/layers/poi/public_transport_stop_type.sql @@ -1,12 +1,11 @@ DO $$ BEGIN - IF NOT EXISTS(SELECT 1 - FROM pg_type - WHERE typname = 'public_transport_stop_type') THEN + 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 IF; END $$; diff --git a/layers/poi/style.json b/layers/poi/style.json new file mode 100644 index 0000000..3c5fa70 --- /dev/null +++ b/layers/poi/style.json @@ -0,0 +1,1704 @@ +{ + "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": "{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": [ + "match", + [ + "get", + "class" + ], + "ice_cream", + "#C77400", + "#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" + ] + ], + "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": "{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": [ + "match", + [ + "get", + "class" + ], + "ice_cream", + "#C77400", + "#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": "{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", + "town_hall", + "post", + "library", + "police", + "information", + "cinema", + "theatre", + "fire_station" + ], + [ + "!=", + "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", + "music", + "museum", + "castle", + "monument", + "art_gallery" + ], + [ + "!in", + "subclass", + "books", + "musical_instrument", + "art", + "gallery" + ] + ], + "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" + ] + ], + "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 index b692ece..f7c9a12 100644 --- a/layers/poi/update_poi_point.sql +++ b/layers/poi/update_poi_point.sql @@ -1,47 +1,99 @@ 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() RETURNS void AS +CREATE OR REPLACE FUNCTION update_osm_poi_point(full_update bool) RETURNS void AS $$ BEGIN UPDATE osm_poi_point SET subclass = 'subway' - WHERE station = '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 funicular = 'yes' + 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) = ( + CONCAT(COALESCE(tags -> 'brand', tags -> 'operator'), concat(' ', tags -> 'ref')), + tags || hstore('name', 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 = 'parcel_locker' + AND name = '' + AND COALESCE(tags -> 'brand', tags -> 'operator') IS NOT NULL; + UPDATE osm_poi_point SET tags = update_tags(tags, geometry) - WHERE COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL; + 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(); +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 + 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 = ( + SET + agg_stop = ( CASE WHEN p.subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') - AND r.rk IS NULL OR r.rk = 1 + AND (r.rk IS NULL OR r.rk = 1) THEN 1 - END) + END) FROM osm_poi_stop_rank r - WHERE p.osm_id = r.osm_id; + 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; @@ -52,7 +104,13 @@ SELECT update_osm_poi_point_agg(); -- Handle updates -CREATE SCHEMA IF NOT EXISTS poi_point; +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 ( @@ -74,11 +132,18 @@ DECLARE t TIMESTAMP WITH TIME ZONE := clock_timestamp(); BEGIN RAISE LOG 'Refresh poi_point'; - PERFORM update_osm_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); @@ -86,8 +151,14 @@ BEGIN 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 OR DELETE + AFTER INSERT OR UPDATE ON osm_poi_point FOR EACH STATEMENT EXECUTE PROCEDURE poi_point.flag(); diff --git a/layers/poi/update_poi_polygon.sql b/layers/poi/update_poi_polygon.sql index cf6c223..aef9133 100644 --- a/layers/poi/update_poi_polygon.sql +++ b/layers/poi/update_poi_polygon.sql @@ -6,7 +6,7 @@ CREATE SCHEMA IF NOT EXISTS poi_polygon; CREATE TABLE IF NOT EXISTS poi_polygon.osm_ids ( - osm_id bigint + osm_id bigint PRIMARY KEY ); -- etldoc: osm_poi_polygon -> osm_poi_polygon @@ -51,11 +51,7 @@ SELECT update_poi_polygon(true); CREATE OR REPLACE FUNCTION poi_polygon.store() RETURNS trigger AS $$ BEGIN - IF (tg_op = 'DELETE') THEN - INSERT INTO poi_polygon.osm_ids VALUES (OLD.osm_id); - ELSE - INSERT INTO poi_polygon.osm_ids VALUES (NEW.osm_id); - END IF; + INSERT INTO poi_polygon.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; RETURN NULL; END; $$ LANGUAGE plpgsql; @@ -80,6 +76,11 @@ 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; @@ -92,13 +93,13 @@ END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_store - AFTER INSERT OR UPDATE OR DELETE + AFTER INSERT OR UPDATE ON osm_poi_polygon FOR EACH ROW EXECUTE PROCEDURE poi_polygon.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE + AFTER INSERT OR UPDATE ON osm_poi_polygon FOR EACH STATEMENT EXECUTE PROCEDURE poi_polygon.flag(); diff --git a/layers/transportation/class.sql b/layers/transportation/class.sql index 5777a05..00b8c38 100644 --- a/layers/transportation/class.sql +++ b/layers/transportation/class.sql @@ -48,7 +48,7 @@ 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') THEN 'paved' + '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' @@ -56,3 +56,38 @@ SELECT CASE $$ 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 index cea8f26..b21fb86 100644 Binary files a/layers/transportation/etl_diagram.png 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 index d8cfe48..29cd399 100644 --- a/layers/transportation/mapping.yaml +++ b/layers/transportation/mapping.yaml @@ -31,6 +31,48 @@ generalized_tables: 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 @@ -42,22 +84,10 @@ generalized_tables: sql_filter: ST_IsValid(geometry) tolerance: ZRES13 -# etldoc: osm_highway_linestring_gen_z10 -> osm_highway_linestring_gen_z9 - highway_linestring_gen_z9: - source: highway_linestring_gen_z10 - sql_filter: (highway IN ('motorway', 'trunk', 'primary', 'secondary', 'motorway_link', 'trunk_link', 'primary_link', 'secondary_link') OR highway = 'construction' AND construction IN ('motorway', 'trunk', 'primary', 'secondary', 'motorway_link', 'trunk_link', 'primary_link', 'secondary_link')) AND NOT is_area - tolerance: ZRES10 - -# etldoc: osm_highway_linestring_gen_z11 -> osm_highway_linestring_gen_z10 - highway_linestring_gen_z10: - source: highway_linestring_gen_z11 - sql_filter: (highway IN ('motorway', 'trunk', 'primary', 'secondary', 'motorway_link', 'trunk_link', 'primary_link', 'secondary_link') OR highway = 'construction' AND construction IN ('motorway', 'trunk', 'primary', 'secondary', 'motorway_link', 'trunk_link', 'primary_link', 'secondary_link')) AND NOT is_area - tolerance: ZRES11 - # 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') OR highway = 'construction' AND construction IN ('motorway', 'trunk', 'primary', 'secondary', 'tertiary', 'motorway_link', 'trunk_link', 'primary_link', 'secondary_link', 'tertiary_link')) AND NOT is_area AND ST_IsValid(geometry) + 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 @@ -104,6 +134,14 @@ 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 @@ -155,10 +193,18 @@ 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 @@ -176,6 +222,9 @@ tables: - name: construction key: construction type: string + - name: tracktype + key: tracktype + type: string - *ref - *network - *z_order @@ -195,6 +244,8 @@ tables: - *oneway - *area - *service + - *access + - *toll - *usage - *public_transport - *man_made @@ -202,7 +253,9 @@ tables: - *foot - *horse - *mtb_scale + - *sac_scale - *surface + - *expressway mapping: highway: - motorway @@ -229,11 +282,16 @@ tables: - service - track - raceway + - busway + - bus_guideway - construction public_transport: - platform man_made: - pier + service: + - driveway + - parking_aisle # etldoc: imposm3 -> osm_railway_linestring railway_linestring: @@ -308,8 +366,14 @@ tables: - *usage mapping: aerialway: - - cable_car + - chair_lift + - drag_lift + - platter + - t-bar - gondola + - cable_car + - j-bar + - mixed_lift # etldoc: imposm3 -> osm_shipway_linestring shipway_linestring: @@ -364,6 +428,7 @@ tables: type: bool - *public_transport - *man_made + - *service mapping: highway: - path @@ -379,6 +444,33 @@ tables: - 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: @@ -395,6 +487,13 @@ tables: - *ref - *network - *name + - name: osmc_symbol + key: osmc:symbol + type: string + - name: colour + key: colour + type: string mapping: route: - road + - hiking diff --git a/layers/transportation/mapping_diagram.png b/layers/transportation/mapping_diagram.png index 959e43b..c47f739 100644 Binary files a/layers/transportation/mapping_diagram.png and b/layers/transportation/mapping_diagram.png differ diff --git a/layers/transportation_name/network_type.sql b/layers/transportation/network_type.sql similarity index 54% rename from layers/transportation_name/network_type.sql rename to layers/transportation/network_type.sql index 4e3e9a4..f1fa864 100644 --- a/layers/transportation_name/network_type.sql +++ b/layers/transportation/network_type.sql @@ -10,24 +10,25 @@ DROP TRIGGER IF EXISTS trigger_refresh_name ON transportation_name.updates_name; DO $$ BEGIN - IF NOT EXISTS(SELECT 1 FROM pg_type WHERE typname = 'route_network_type') THEN + 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', - 'gb-motorway', 'gb-trunk' + 'ca-transcanada', 'ca-provincial-arterial', 'ca-provincial', + 'gb-motorway', 'gb-trunk', 'gb-primary', + 'ie-motorway', 'ie-national', 'ie-regional' ); - END IF; END $$; -DO +-- Top-level national route networks that should display at the lowest zooms +CREATE OR REPLACE FUNCTION osm_national_network(network text) RETURNS boolean AS $$ - BEGIN - BEGIN - ALTER TABLE osm_route_member - ADD COLUMN network_type route_network_type; - EXCEPTION - WHEN duplicate_column THEN RAISE NOTICE 'column network_type already exists in network_type.'; - END; - END; -$$; + SELECT network <> '' AND network IN ( + -- Canada + 'ca-transcanada', 'ca-provincial-arterial', + -- United States + 'us-interstate'); +$$ LANGUAGE sql IMMUTABLE + PARALLEL SAFE; diff --git a/layers/transportation/style.json b/layers/transportation/style.json new file mode 100644 index 0000000..d0f2357 --- /dev/null +++ b/layers/transportation/style.json @@ -0,0 +1,7837 @@ +{ + "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", + [ + "==", + [ + "geometry-type" + ], + "LineString" + ], + [ + "all", + [ + "match", + [ + "get", + "brunnel" + ], + [ + "bridge", + "tunnel" + ], + false, + true + ], + [ + "match", + [ + "get", + "class" + ], + [ + "minor" + ], + true, + false + ] + ] + ], + "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", + [ + "==", + [ + "geometry-type" + ], + "LineString" + ], + [ + "all", + [ + "match", + [ + "get", + "brunnel" + ], + [ + "bridge", + "tunnel" + ], + false, + true + ], + [ + "match", + [ + "get", + "class" + ], + [ + "minor_construction" + ], + true, + false + ] + ] + ], + "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 index 416f8c8..3ef9791 100644 --- a/layers/transportation/transportation.sql +++ b/layers/transportation/transportation.sql @@ -11,34 +11,38 @@ $$ LANGUAGE SQL IMMUTABLE CREATE OR REPLACE FUNCTION layer_transportation(bbox geometry, zoom_level int) RETURNS TABLE ( - osm_id bigint, - geometry geometry, - class text, - subclass text, - ramp int, - oneway int, - brunnel text, - service text, - layer int, - level int, - indoor int, - bicycle text, - foot text, - horse text, - mtb_scale text, - surface text + 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 NULLIF(highway, '') IS NOT NULL OR NULLIF(public_transport, '') IS NOT NULL + WHEN highway <> '' OR public_transport <> '' THEN highway_class(highway, public_transport, construction) - WHEN NULLIF(railway, '') IS NOT NULL THEN railway_class(railway) - WHEN NULLIF(aerialway, '') IS NOT NULL THEN 'aerialway' - WHEN NULLIF(shipway, '') IS NOT NULL THEN shipway - WHEN NULLIF(man_made, '') IS NOT NULL THEN man_made + 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 @@ -47,14 +51,18 @@ SELECT osm_id, 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 highway = 'steps' - THEN 1 - ELSE is_ramp::int END AS ramp, - is_oneway::int AS oneway, + 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 expressway = TRUE THEN 1 END AS expressway, NULLIF(layer, 0) AS layer, "level", CASE WHEN indoor = TRUE THEN 1 END AS indoor, @@ -69,14 +77,18 @@ FROM ( 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, @@ -98,14 +110,18 @@ FROM ( 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, @@ -127,14 +143,18 @@ FROM ( 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, @@ -156,14 +176,18 @@ FROM ( 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, @@ -185,14 +209,18 @@ FROM ( 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, @@ -209,19 +237,23 @@ FROM ( WHERE zoom_level = 8 UNION ALL - -- etldoc: osm_highway_linestring_gen_z9 -> layer_transportation:z9 + -- 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, @@ -234,24 +266,27 @@ FROM ( mtb_scale, NULL AS surface, z_order - FROM osm_highway_linestring_gen_z9 + FROM osm_transportation_merge_linestring_gen_z9 WHERE zoom_level = 9 - AND ST_Length(geometry) > ZRes(11) UNION ALL - -- etldoc: osm_highway_linestring_gen_z10 -> layer_transportation:z10 + -- 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, @@ -264,24 +299,27 @@ FROM ( mtb_scale, NULL AS surface, z_order - FROM osm_highway_linestring_gen_z10 + FROM osm_transportation_merge_linestring_gen_z10 WHERE zoom_level = 10 - AND ST_Length(geometry) > ZRes(11) UNION ALL - -- etldoc: osm_highway_linestring_gen_z11 -> layer_transportation:z11 + -- 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, @@ -294,59 +332,65 @@ FROM ( mtb_scale, NULL AS surface, z_order - FROM osm_highway_linestring_gen_z11 + FROM osm_transportation_merge_linestring_gen_z11 WHERE zoom_level = 11 - AND ST_Length(geometry) > ZRes(12) UNION ALL -- etldoc: osm_highway_linestring -> layer_transportation:z12 -- etldoc: osm_highway_linestring -> layer_transportation:z13 -- etldoc: osm_highway_linestring -> layer_transportation:z14_ - SELECT osm_id, - geometry, - highway, + -- 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, - layer, - CASE WHEN highway IN ('footway', 'steps') THEN "level" END AS "level", - CASE WHEN highway IN ('footway', 'steps') THEN indoor END AS indoor, + 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(surface) AS "surface", - z_order - FROM osm_highway_linestring + 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 ( - zoom_level = 12 AND ( - highway_class(highway, public_transport, construction) NOT IN ('track', 'path', 'minor') - OR highway IN ('unclassified', 'residential') - ) AND man_made <> 'pier' - OR zoom_level = 13 - AND ( - highway_class(highway, public_transport, construction) NOT IN ('track', 'path') AND - man_made <> 'pier' - OR - man_made = 'pier' AND NOT ST_IsClosed(geometry) - ) - OR zoom_level >= 14 - AND ( - man_made <> 'pier' - OR - NOT ST_IsClosed(geometry) - ) - ) + 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 @@ -354,14 +398,18 @@ FROM ( 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, @@ -386,14 +434,18 @@ FROM ( 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, @@ -418,14 +470,18 @@ FROM ( 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, is_oneway, NULL AS man_made, @@ -449,14 +505,18 @@ FROM ( 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, is_oneway, NULL AS man_made, @@ -480,14 +540,18 @@ FROM ( 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, is_oneway, NULL AS man_made, @@ -512,14 +576,18 @@ FROM ( 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, is_oneway, NULL AS man_made, @@ -544,14 +612,18 @@ FROM ( 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, is_oneway, NULL AS man_made, @@ -574,14 +646,18 @@ FROM ( 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, is_oneway, NULL AS man_made, @@ -598,19 +674,254 @@ FROM ( WHERE zoom_level >= 13 UNION ALL - -- etldoc: osm_shipway_linestring_gen_z11 -> layer_transportation:z11 + -- 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, is_oneway, NULL AS man_made, @@ -632,14 +943,18 @@ FROM ( 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, is_oneway, NULL AS man_made, @@ -662,14 +977,18 @@ FROM ( 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, is_oneway, NULL AS man_made, @@ -696,17 +1015,21 @@ FROM ( 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, diff --git a/layers/transportation/transportation.yaml b/layers/transportation/transportation.yaml index 1ed5e03..4e0caf8 100644 --- a/layers/transportation/transportation.yaml +++ b/layers/transportation/transportation.yaml @@ -1,5 +1,8 @@ layer: id: "transportation" + requires: + tables: + - ne_10m_admin_0_countries description: | **transportation** contains roads, railways, aerial ways, and shipping lines. @@ -21,8 +24,9 @@ layer: [`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), or - [`man_made`](http://wiki.openstreetmap.org/wiki/Key:route). + 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'] @@ -45,6 +49,12 @@ layer: highway: track raceway: highway: raceway + busway: + highway: busway + bus_guideway: + highway: bus_guideway + ferry: + highway: shipway motorway_construction: __AND__: highway: construction @@ -111,6 +121,14 @@ layer: - 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. @@ -123,12 +141,12 @@ layer: 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: [0, 1, -1] + values: [1, -1] ramp: description: | Mark with `1` whether way is a ramp (link or steps) or not with `0`. - values: [0, 1] + values: [1] service: description: | Original value of the [`service`](http://wiki.openstreetmap.org/wiki/Key:service) tag. @@ -140,6 +158,21 @@ layer: - 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. @@ -174,9 +207,12 @@ layer: datasource: geometry_field: geometry srid: 900913 - query: (SELECT geometry, class, subclass, oneway, ramp, brunnel, service, layer, level, indoor, bicycle, foot, horse, mtb_scale, surface FROM layer_transportation(!bbox!, z(!scale_denominator!))) AS t + 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: diff --git a/layers/transportation/update_route_member.sql b/layers/transportation/update_route_member.sql new file mode 100644 index 0000000..77d7773 --- /dev/null +++ b/layers/transportation/update_route_member.sql @@ -0,0 +1,214 @@ +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 + 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-arterial'::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, + 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; + + -- Create GBR/IRE relations (so we can use it in the same way as other relations) + -- etldoc: gbr_route_members_view -> transportation_route_member_coalesced + INSERT INTO transportation_route_member_coalesced (member, network, ref, network_type, concurrency_index, osm_id) + SELECT member, network, ref, osm_route_member_network_type(network, ref) AS network_type, + 1 AS concurrency_index, 0 AS osm_id + FROM gbr_route_members_view + WHERE full_update OR EXISTS( + SELECT NULL + FROM transportation_name.network_changes c + WHERE c.is_old IS FALSE AND c.osm_id = gbr_route_members_view.member + ) + GROUP BY member, network, ref + ON CONFLICT (member, network, ref) DO NOTHING; + + -- etldoc: ire_route_members_view -> transportation_route_member_coalesced + INSERT INTO transportation_route_member_coalesced (member, network, ref, network_type, concurrency_index, osm_id) + SELECT member, network, ref, osm_route_member_network_type(network, ref) AS network_type, + 1 AS concurrency_index, 0 AS osm_id + FROM ire_route_members_view + WHERE full_update OR EXISTS( + SELECT NULL + FROM transportation_name.network_changes c + WHERE c.is_old IS FALSE AND c.osm_id = ire_route_members_view.member + ) + GROUP BY member, network, ref + ON CONFLICT (member, network, ref) DO NOTHING; + + -- 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 + -- see http://wiki.openstreetmap.org/wiki/Relation:route#Road_routes + SELECT DISTINCT ON (member, network, ref) + member, + network, + ref, + osm_id, + role, + type, + name, + osmc_symbol, + colour + FROM osm_route_member + WHERE full_update OR EXISTS( + SELECT NULL + FROM transportation_name.network_changes c + WHERE c.is_old IS FALSE AND c.osm_id = osm_route_member.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, + 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, network, 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 index 94ba324..7bacd55 100644 --- a/layers/transportation/update_transportation_merge.sql +++ b/layers/transportation/update_transportation_merge.sql @@ -1,191 +1,1413 @@ -DROP TRIGGER IF EXISTS trigger_flag_transportation ON osm_highway_linestring; -DROP TRIGGER IF EXISTS trigger_refresh ON transportation.updates; +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 LEAKPROOF + 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 LEAKPROOF + 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 - --- Improve performance of the sql in transportation_name/network_type.sql -CREATE INDEX IF NOT EXISTS osm_highway_linestring_highway_partial_idx - ON osm_highway_linestring (highway) - WHERE highway IN ('motorway', 'trunk', 'primary', 'construction'); - --- etldoc: osm_highway_linestring -> osm_transportation_merge_linestring -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_merge_linestring CASCADE; -CREATE MATERIALIZED VIEW osm_transportation_merge_linestring AS -( -SELECT (ST_Dump(geometry)).geom AS geometry, - NULL::bigint AS osm_id, - highway, - construction, - is_bridge, - is_tunnel, - is_ford, - z_order +-- etldoc: osm_highway_linestring -> osm_transportation_name_network +-- etldoc: transportation_route_member_coalesced -> osm_transportation_name_network +CREATE TABLE IF NOT EXISTS 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 ST_LineMerge(ST_Collect(geometry)) AS geometry, - highway, - construction, - is_bridge, - is_tunnel, - is_ford, - min(z_order) AS z_order - FROM osm_highway_linestring - WHERE (highway IN ('motorway', 'trunk', 'primary') OR - highway = 'construction' AND construction IN ('motorway', 'trunk', 'primary')) - AND ST_IsValid(geometry) - GROUP BY highway, construction, is_bridge, is_tunnel, is_ford - ) AS highway_union - ) /* DELAY_MATERIALIZED_VIEW_CREATION */; -CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_geometry_idx - ON osm_transportation_merge_linestring USING gist (geometry); + 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, + NULLIF(rm1.network, '') || '=' || COALESCE(rm1.ref, '') AS route_1, + NULLIF(rm2.network, '') || '=' || COALESCE(rm2.ref, '') AS route_2, + NULLIF(rm3.network, '') || '=' || COALESCE(rm3.ref, '') AS route_3, + NULLIF(rm4.network, '') || '=' || COALESCE(rm4.ref, '') AS route_4, + NULLIF(rm5.network, '') || '=' || COALESCE(rm5.ref, '') AS route_5, + NULLIF(rm6.network, '') || '=' || COALESCE(rm6.ref, '') 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; --- etldoc: osm_transportation_merge_linestring -> osm_transportation_merge_linestring_gen_z8 -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_merge_linestring_gen_z8 CASCADE; -CREATE MATERIALIZED VIEW osm_transportation_merge_linestring_gen_z8 AS -( -SELECT ST_Simplify(geometry, ZRes(10)) AS geometry, - osm_id, +-- 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 +); + +-- 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, - z_order -FROM osm_transportation_merge_linestring -WHERE highway IN ('motorway', 'trunk', 'primary') - OR highway = 'construction' AND construction IN ('motorway', 'trunk', 'primary') - ) /* DELAY_MATERIALIZED_VIEW_CREATION */; + 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('LineString'), + 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 +); + +-- 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); --- etldoc: osm_transportation_merge_linestring_gen_z8 -> osm_transportation_merge_linestring_gen_z7 -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_merge_linestring_gen_z7 CASCADE; -CREATE MATERIALIZED VIEW osm_transportation_merge_linestring_gen_z7 AS +-- 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 ( -SELECT ST_Simplify(geometry, ZRes(9)) AS geometry, - osm_id, - highway, - construction, - is_bridge, - is_tunnel, - is_ford, - z_order -FROM osm_transportation_merge_linestring_gen_z8 -WHERE (highway IN ('motorway', 'trunk', 'primary') OR - highway = 'construction' AND construction IN ('motorway', 'trunk', 'primary')) - AND ST_Length(geometry) > 50 - ) /* DELAY_MATERIALIZED_VIEW_CREATION */; + 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 + SELECT ST_Simplify(geometry, ZRes(9)) 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 osm_transportation_merge_linestring_gen_z8 + -- 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_z8.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 + osm_national_network(network) 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); - --- etldoc: osm_transportation_merge_linestring_gen_z7 -> osm_transportation_merge_linestring_gen_z6 -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_merge_linestring_gen_z6 CASCADE; -CREATE MATERIALIZED VIEW osm_transportation_merge_linestring_gen_z6 AS -( -SELECT ST_Simplify(geometry, ZRes(8)) AS geometry, - osm_id, - highway, - construction, - is_bridge, - is_tunnel, - is_ford, - z_order -FROM osm_transportation_merge_linestring_gen_z7 -WHERE (highway IN ('motorway', 'trunk') OR highway = 'construction' AND construction IN ('motorway', 'trunk')) - AND ST_Length(geometry) > 100 - ) /* DELAY_MATERIALIZED_VIEW_CREATION */; CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z6_geometry_idx ON osm_transportation_merge_linestring_gen_z6 USING gist (geometry); - --- etldoc: osm_transportation_merge_linestring_gen_z6 -> osm_transportation_merge_linestring_gen_z5 -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_merge_linestring_gen_z5 CASCADE; -CREATE MATERIALIZED VIEW osm_transportation_merge_linestring_gen_z5 AS -( -SELECT ST_Simplify(geometry, ZRes(7)) AS geometry, - osm_id, - highway, - construction, - is_bridge, - is_tunnel, - is_ford, - z_order -FROM osm_transportation_merge_linestring_gen_z6 -WHERE (highway IN ('motorway', 'trunk') OR highway = 'construction' AND construction IN ('motorway', 'trunk')) - AND ST_Length(geometry) > 500 - ) /* DELAY_MATERIALIZED_VIEW_CREATION */; CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z5_geometry_idx ON osm_transportation_merge_linestring_gen_z5 USING gist (geometry); - --- etldoc: osm_transportation_merge_linestring_gen_z5 -> osm_transportation_merge_linestring_gen_z4 -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_merge_linestring_gen_z4 CASCADE; -CREATE MATERIALIZED VIEW osm_transportation_merge_linestring_gen_z4 AS -( -SELECT ST_Simplify(geometry, ZRes(6)) AS geometry, - osm_id, - highway, - construction, - is_bridge, - is_tunnel, - is_ford, - z_order -FROM osm_transportation_merge_linestring_gen_z5 -WHERE (highway = 'motorway' OR highway = 'construction' AND construction = 'motorway') - AND ST_Length(geometry) > 1000 - ) /* DELAY_MATERIALIZED_VIEW_CREATION */; 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 +-- 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 TABLE IF NOT EXISTS transportation.changes_z11 +( + is_old boolean NULL, + osm_id bigint, + PRIMARY KEY (is_old, osm_id) +); -CREATE SCHEMA IF NOT EXISTS transportation; +-- 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; -CREATE TABLE IF NOT EXISTS transportation.updates +-- 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() RETURNS trigger AS +CREATE OR REPLACE FUNCTION transportation.flag_z11() RETURNS trigger AS $$ BEGIN - INSERT INTO transportation.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + INSERT INTO transportation.updates_z11(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; RETURN NULL; END; $$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION transportation.refresh() RETURNS trigger AS +CREATE OR REPLACE FUNCTION transportation.refresh_z11() RETURNS trigger AS $$ DECLARE t TIMESTAMP WITH TIME ZONE := clock_timestamp(); BEGIN - RAISE LOG 'Refresh transportation'; - REFRESH MATERIALIZED VIEW osm_transportation_merge_linestring; - REFRESH MATERIALIZED VIEW osm_transportation_merge_linestring_gen_z8; - REFRESH MATERIALIZED VIEW osm_transportation_merge_linestring_gen_z7; - REFRESH MATERIALIZED VIEW osm_transportation_merge_linestring_gen_z6; - REFRESH MATERIALIZED VIEW osm_transportation_merge_linestring_gen_z5; - REFRESH MATERIALIZED VIEW osm_transportation_merge_linestring_gen_z4; - -- noinspection SqlWithoutWhere - DELETE FROM transportation.updates; + 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, 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; + + -- Create index on geometry column and analyze the created table to speed up subsequent queries + CREATE INDEX ON linestrings_to_merge USING GIST (geometry); + ANALYZE linestrings_to_merge; + + -- Add all Merged-LineStrings intersecting with Source-LineStrings affected by this update + INSERT INTO linestrings_to_merge + SELECT s.source_id AS osm_id, m.id, + 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, access, toll, + visible_layer(geometry, layer, 11) AS layer, z_order + FROM osm_transportation_merge_linestring_gen_z11 m + JOIN osm_transportation_merge_linestring_gen_z11_source_ids s ON (m.id = s.id) + WHERE EXISTS(SELECT NULL FROM linestrings_to_merge WHERE 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 column to store relations before they have been + -- intersected + ALTER TABLE osm_transportation_merge_linestring_gen_z11 ADD COLUMN IF NOT EXISTS source_ids bigint[]; + + WITH inserted_linestrings AS ( + -- Merge LineStrings of each cluster and insert them + 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, + 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, 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, unnest(source_ids) AS source_id, geometry + FROM inserted_linestrings + ) 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 column + ALTER TABLE osm_transportation_merge_linestring_gen_z11 DROP COLUMN IF EXISTS 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); - RAISE LOG 'Refresh transportation done in %', age(clock_timestamp(), t); RETURN NULL; END; $$ LANGUAGE plpgsql; -CREATE TRIGGER trigger_flag_transportation +CREATE TRIGGER trigger_store_transportation_highway_linestring_gen_z11 AFTER INSERT OR UPDATE OR DELETE - ON osm_highway_linestring - FOR EACH STATEMENT -EXECUTE PROCEDURE transportation.flag(); + ON osm_highway_linestring_gen_z11 + FOR EACH ROW +EXECUTE PROCEDURE transportation.store_gen_z11(); -CREATE CONSTRAINT TRIGGER trigger_refresh +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 + ON transportation.updates_z11 INITIALLY DEFERRED FOR EACH ROW -EXECUTE PROCEDURE transportation.refresh(); \ No newline at end of file +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, 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 AND + ( + 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; + + -- Create index on geometry column and analyze the created table to speed up subsequent queries + CREATE INDEX ON linestrings_to_merge USING GIST (geometry); + ANALYZE linestrings_to_merge; + + -- Add all Merged-LineStrings intersecting with Source-LineStrings affected by this update + INSERT INTO linestrings_to_merge + SELECT s.source_id, m.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_z8 m + JOIN osm_transportation_merge_linestring_gen_z8_source_ids s ON (m.id = s.id) + WHERE EXISTS(SELECT NULL FROM linestrings_to_merge WHERE 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 column to store relations before they have been + -- intersected + ALTER TABLE osm_transportation_merge_linestring_gen_z8 ADD COLUMN IF NOT EXISTS source_ids bigint[]; + + WITH inserted_linestrings AS ( + -- Merge LineStrings of each cluster and insert them + 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(source_id) as 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, 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, unnest(source_ids) AS source_id, geometry + FROM inserted_linestrings + ) 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 column + ALTER TABLE osm_transportation_merge_linestring_gen_z8 DROP COLUMN IF EXISTS 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/etl_diagram.png b/layers/transportation_name/etl_diagram.png index 7ed7a04..c4ee4e0 100644 Binary files a/layers/transportation_name/etl_diagram.png 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..b9f8e07 --- /dev/null +++ b/layers/transportation_name/highway_classification.sql @@ -0,0 +1,57 @@ +CREATE OR REPLACE FUNCTION highway_to_val(hwy_class varchar) +RETURNS int +IMMUTABLE +LANGUAGE plpgsql +AS $$ +BEGIN + CASE hwy_class + WHEN 'motorway' THEN RETURN 6; + WHEN 'trunk' THEN RETURN 5; + WHEN 'primary' THEN RETURN 4; + WHEN 'secondary' THEN RETURN 3; + WHEN 'tertiary' THEN RETURN 2; + WHEN 'unclassified' THEN RETURN 1; + else RETURN 0; + END CASE; +END; +$$; + +CREATE OR REPLACE FUNCTION val_to_highway(hwy_val int) +RETURNS varchar +IMMUTABLE +LANGUAGE plpgsql +AS $$ +BEGIN + CASE hwy_val + WHEN 6 THEN RETURN 'motorway'; + WHEN 5 THEN RETURN 'trunk'; + WHEN 4 THEN RETURN 'primary'; + WHEN 3 THEN RETURN 'secondary'; + WHEN 2 THEN RETURN 'tertiary'; + WHEN 1 THEN RETURN 'unclassified'; + else RETURN null; + END CASE; +END; +$$; + +CREATE OR REPLACE FUNCTION highest_hwy_sfunc(agg_state varchar, hwy_class varchar) +RETURNS varchar +IMMUTABLE +LANGUAGE plpgsql +AS $$ +BEGIN + RETURN val_to_highway( + GREATEST( + highway_to_val(agg_state), + highway_to_val(hwy_class) + ) + ); +END; +$$; + +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 index 959e43b..c47f739 100644 Binary files a/layers/transportation_name/mapping_diagram.png 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 index 9a28281..9db6d01 100644 --- a/layers/transportation_name/transportation_name.sql +++ b/layers/transportation_name/transportation_name.sql @@ -4,7 +4,6 @@ CREATE OR REPLACE FUNCTION layer_transportation_name(bbox geometry, zoom_level integer) RETURNS TABLE ( - osm_id bigint, geometry geometry, name text, name_en text, @@ -13,6 +12,12 @@ CREATE OR REPLACE FUNCTION layer_transportation_name(bbox geometry, zoom_level i ref text, ref_length int, network text, + route_1 text, + route_2 text, + route_3 text, + route_4 text, + route_5 text, + route_6 text, class text, subclass text, brunnel text, @@ -22,25 +27,25 @@ CREATE OR REPLACE FUNCTION layer_transportation_name(bbox geometry, zoom_level i ) AS $$ -SELECT osm_id, - geometry, - name, - COALESCE(name_en, name) AS name_en, - COALESCE(name_de, name, name_en) AS name_de, +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, - --TODO: The road network of the road is not yet implemented CASE WHEN network IS NOT NULL THEN network::text WHEN length(coalesce(ref, '')) > 0 THEN 'road' END AS network, - highway_class(highway, '', construction) AS class, + route_1, route_2, route_3, route_4, route_5, route_6, + highway_class(highway, '', subclass) AS class, CASE - WHEN highway IS NOT NULL AND highway_class(highway, '', construction) = 'path' + WHEN highway IS NOT NULL AND highway_class(highway, '', subclass) = 'path' THEN highway + ELSE subclass END AS subclass, brunnel, NULLIF(layer, 0) AS layer, @@ -49,7 +54,20 @@ SELECT osm_id, FROM ( -- etldoc: osm_transportation_name_linestring_gen4 -> layer_transportation_name:z6 - SELECT *, + 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 @@ -58,7 +76,20 @@ FROM ( UNION ALL -- etldoc: osm_transportation_name_linestring_gen3 -> layer_transportation_name:z7 - SELECT *, + 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 @@ -67,7 +98,20 @@ FROM ( UNION ALL -- etldoc: osm_transportation_name_linestring_gen2 -> layer_transportation_name:z8 - SELECT *, + 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 @@ -78,7 +122,20 @@ FROM ( -- 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 *, + 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 @@ -88,67 +145,98 @@ FROM ( -- etldoc: osm_transportation_name_linestring -> layer_transportation_name:z12 SELECT geometry, - osm_id, - name, - name_en, - name_de, "tags", ref, highway, - construction, + 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(name, ref), geometry) - AND highway_class(highway, '', construction) NOT IN ('minor', 'track', 'path') + AND LineLabel(zoom_level, COALESCE(tags->'name', ref), 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, - osm_id, - name, - name_en, - name_de, "tags", ref, highway, - construction, + 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(name, ref), geometry) - AND highway_class(highway, '', construction) NOT IN ('track', 'path') + AND LineLabel(zoom_level, COALESCE(tags->'name', ref), 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, - osm_id, - name, - name_en, - name_de, "tags", ref, highway, - construction, + 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::text AS route_1, + NULL::text AS route_2, + NULL::text AS route_3, + NULL::text AS route_4, + NULL::text AS route_5, + NULL::text 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; diff --git a/layers/transportation_name/transportation_name.yaml b/layers/transportation_name/transportation_name.yaml index 331929e..a5e9266 100644 --- a/layers/transportation_name/transportation_name.yaml +++ b/layers/transportation_name/transportation_name.yaml @@ -1,7 +1,9 @@ layer: id: "transportation_name" # transportation_name relies on the function highway_class() defined in transportation layer - requires: "transportation" + 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 @@ -26,8 +28,14 @@ layer: - 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: | @@ -55,11 +63,13 @@ layer: - 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). + [`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 @@ -69,6 +79,7 @@ layer: - bridleway - corridor - platform + - junction brunnel: description: | Mark whether way is a bridge, a tunnel or a ford. @@ -90,13 +101,18 @@ layer: value of [`indoor`](http://wiki.openstreetmap.org/wiki/Key:indoor) tag. values: - 1 + route_1: 1st route concurrency. + route_2: 2nd route concurrency. + route_3: 3rd route concurrency. + route_4: 4th route concurrency. + route_5: 5th route concurrency. + route_6: 6th route concurrency. 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 FROM layer_transportation_name(!bbox!, z(!scale_denominator!))) AS t + query: (SELECT geometry, name, name_en, name_de, {name_languages}, ref, ref_length, network::text, class::text, subclass, brunnel, layer, level, indoor, route_1, route_2, route_3, route_4, route_5, route_6 FROM layer_transportation_name(!bbox!, z(!scale_denominator!))) AS t schema: - - ./network_type.sql - - ./update_route_member.sql + - ./highway_classification.sql - ./update_transportation_name.sql - ./transportation_name.sql datasources: diff --git a/layers/transportation_name/update_route_member.sql b/layers/transportation_name/update_route_member.sql deleted file mode 100644 index bc1c945..0000000 --- a/layers/transportation_name/update_route_member.sql +++ /dev/null @@ -1,93 +0,0 @@ -CREATE TABLE IF NOT EXISTS ne_10m_admin_0_bg_buffer AS -SELECT ST_Buffer(geometry, 10000) -FROM ne_10m_admin_0_countries -WHERE iso_a2 = 'GB'; - -CREATE OR REPLACE VIEW gbr_route_members_view AS -SELECT 0, - osm_id, - substring(ref FROM E'^[AM][0-9AM()]+'), - CASE WHEN highway = 'motorway' THEN 'omt-gb-motorway' ELSE 'omt-gb-trunk' END -FROM osm_highway_linestring -WHERE length(ref) > 0 - AND ST_Intersects(geometry, (SELECT * FROM ne_10m_admin_0_bg_buffer)) - AND highway IN ('motorway', 'trunk') -; --- Create GBR relations (so we can use it in the same way as other relations) -DELETE -FROM osm_route_member -WHERE network IN ('omt-gb-motorway', 'omt-gb-trunk'); --- etldoc: osm_highway_linestring -> osm_route_member -INSERT INTO osm_route_member (osm_id, member, ref, network) -SELECT * -FROM gbr_route_members_view; - -CREATE OR REPLACE FUNCTION osm_route_member_network_type(network text, name text, ref text) RETURNS route_network_type AS -$$ -SELECT CASE - 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 - -- TODO: improve hierarchical queries using - -- http://www.openstreetmap.org/relation/1307243 - -- however the relation does not cover the whole Trans-Canada_Highway - WHEN - (network = 'CA:transcanada') OR - (network = 'CA:BC:primary' AND ref IN ('16')) OR - (name = 'Yellowhead Highway (AB)' AND ref IN ('16')) OR - (network = 'CA:SK:primary' AND ref IN ('16')) OR - (network = 'CA:ON:primary' AND ref IN ('17', '417')) OR - (name = 'Route Transcanadienne') OR - (network = 'CA:NB:primary' AND ref IN ('2', '16')) OR - (network = 'CA:PE' AND ref IN ('1')) OR - (network = 'CA:NS' AND ref IN ('104', '105')) OR - (network = 'CA:NL:R' AND ref IN ('1')) OR - (name = 'Trans-Canada Highway') - THEN 'ca-transcanada'::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 - END; -$$ LANGUAGE sql IMMUTABLE - PARALLEL SAFE; - --- etldoc: osm_route_member -> osm_route_member --- see http://wiki.openstreetmap.org/wiki/Relation:route#Road_routes -UPDATE osm_route_member -SET network_type = osm_route_member_network_type(network, name, ref) -WHERE network != '' - AND network_type IS DISTINCT FROM osm_route_member_network_type(network, name, ref) -; - -CREATE OR REPLACE FUNCTION update_osm_route_member() RETURNS void AS -$$ -BEGIN - DELETE - FROM osm_route_member AS r - USING - transportation_name.network_changes AS c - WHERE network IN ('omt-gb-motorway', 'omt-gb-trunk') - AND r.osm_id = c.osm_id; - - INSERT INTO osm_route_member (osm_id, member, ref, network) - SELECT r.* - FROM gbr_route_members_view AS r - JOIN transportation_name.network_changes AS c ON - r.osm_id = c.osm_id; - - UPDATE - osm_route_member AS r - SET network_type = osm_route_member_network_type(network, name, ref) - FROM transportation_name.network_changes AS c - WHERE network != '' - AND network_type IS DISTINCT FROM osm_route_member_network_type(network, name, ref) - AND r.member = c.osm_id; -END; -$$ LANGUAGE plpgsql; - -CREATE INDEX IF NOT EXISTS osm_route_member_network_idx ON osm_route_member ("network"); -CREATE INDEX IF NOT EXISTS osm_route_member_member_idx ON osm_route_member ("member"); -CREATE INDEX IF NOT EXISTS osm_route_member_name_idx ON osm_route_member ("name"); -CREATE INDEX IF NOT EXISTS osm_route_member_ref_idx ON osm_route_member ("ref"); - -CREATE INDEX IF NOT EXISTS osm_route_member_network_type_idx ON osm_route_member ("network_type"); diff --git a/layers/transportation_name/update_transportation_name.sql b/layers/transportation_name/update_transportation_name.sql index d50cc56..ffe7bba 100644 --- a/layers/transportation_name/update_transportation_name.sql +++ b/layers/transportation_name/update_transportation_name.sql @@ -1,244 +1,634 @@ +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); --- etldoc: osm_highway_linestring -> osm_transportation_name_network --- etldoc: osm_route_member -> osm_transportation_name_network -CREATE TABLE IF NOT EXISTS osm_transportation_name_network AS -SELECT - geometry, - osm_id, - name, - name_en, - name_de, - tags, - ref, - highway, - construction, - brunnel, - "level", - layer, - indoor, - network_type, - z_order -FROM ( - SELECT hl.geometry, - hl.osm_id, - CASE WHEN length(hl.name) > 15 THEN osml10n_street_abbrev_all(hl.name) ELSE NULLIF(hl.name, '') END AS "name", - CASE WHEN length(hl.name_en) > 15 THEN osml10n_street_abbrev_en(hl.name_en) ELSE NULLIF(hl.name_en, '') END AS "name_en", - CASE WHEN length(hl.name_de) > 15 THEN osml10n_street_abbrev_de(hl.name_de) ELSE NULLIF(hl.name_de, '') END AS "name_de", - slice_language_tags(hl.tags) AS tags, - rm.network_type, - CASE - WHEN rm.network_type IS NOT NULL AND nullif(rm.ref::text, '') IS NOT NULL - THEN rm.ref::text - ELSE NULLIF(hl.ref, '') - END AS ref, - hl.highway, - hl.construction, - brunnel(hl.is_bridge, hl.is_tunnel, hl.is_ford) AS brunnel, - 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, - ROW_NUMBER() OVER (PARTITION BY hl.osm_id - ORDER BY rm.network_type) AS "rank", - hl.z_order - FROM osm_highway_linestring hl - LEFT JOIN osm_route_member rm ON - rm.member = hl.osm_id - WHERE (hl.name <> '' OR hl.ref <> '') - AND NULLIF(hl.highway, '') IS NOT NULL -) AS t -WHERE ("rank" = 1 OR "rank" IS NULL); -CREATE INDEX IF NOT EXISTS osm_transportation_name_network_osm_id_idx ON osm_transportation_name_network (osm_id); -CREATE INDEX IF NOT EXISTS osm_transportation_name_network_name_ref_idx ON osm_transportation_name_network (coalesce(name, ''), coalesce(ref, '')); -CREATE INDEX IF NOT EXISTS osm_transportation_name_network_geometry_idx ON osm_transportation_name_network USING gist (geometry); - +-- 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 -CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring AS -SELECT (ST_Dump(geometry)).geom AS geometry, - NULL::bigint AS osm_id, - name, - name_en, - name_de, - tags || get_basic_names(tags, geometry) AS "tags", +-- 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 text, + route_2 text, + route_3 text, + route_4 text, + route_5 text, + route_6 text, + z_order integer, + route_rank integer +); + +-- 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, - construction, + subclass, brunnel, + sac_scale, "level", layer, indoor, network_type AS network, - z_order + route_1, route_2, route_3, route_4, route_5, route_6, + z_order, + route_rank FROM ( - SELECT ST_LineMerge(ST_Collect(geometry)) AS geometry, - name, - name_en, - name_de, - tags || hstore( -- store results of osml10n_street_abbrev_* above - ARRAY ['name', name, 'name:en', name_en, 'name:de', name_de]) AS tags, + -- 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, - construction, + subclass, brunnel, - "level", + sac_scale, + level, layer, indoor, network_type, - min(z_order) AS z_order - FROM osm_transportation_name_network - GROUP BY name, name_en, name_de, tags, ref, highway, construction, brunnel, "level", layer, indoor, network_type - ) AS highway_union -; -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_name_ref_idx ON osm_transportation_name_linestring (coalesce(name, ''), coalesce(ref, '')); -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_geometry_idx ON osm_transportation_name_linestring USING gist (geometry); + 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 -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_highway_partial_idx - ON osm_transportation_name_linestring (highway, construction) - WHERE highway IN ('motorway', 'trunk', 'construction'); + -- 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 AS route_1, + NULL AS route_2, + NULL AS route_3, + NULL AS route_4, + NULL AS route_5, + NULL 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 --- etldoc: osm_transportation_name_linestring -> osm_transportation_name_linestring_gen1 -CREATE OR REPLACE VIEW osm_transportation_name_linestring_gen1_view AS -SELECT ST_Simplify(geometry, 50) AS geometry, - osm_id, - name, - name_en, - name_de, - tags, - ref, - highway, - construction, - brunnel, - network, - z_order -FROM osm_transportation_name_linestring -WHERE (highway IN ('motorway', 'trunk') OR highway = 'construction' AND construction IN ('motorway', 'trunk')) - AND ST_Length(geometry) > 8000 -; -CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen1 AS -SELECT * -FROM osm_transportation_name_linestring_gen1_view; -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen1_name_ref_idx ON osm_transportation_name_linestring_gen1((coalesce(name, ref))); -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen1_geometry_idx ON osm_transportation_name_linestring_gen1 USING gist (geometry); + -- 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 AS route_1, + NULL AS route_2, + NULL AS route_3, + NULL AS route_4, + NULL AS route_5, + NULL 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; -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen1_highway_partial_idx - ON osm_transportation_name_linestring_gen1 (highway, construction) - WHERE highway IN ('motorway', 'trunk', 'construction'); +-- Geometry Index +CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_geometry_idx + ON osm_transportation_name_linestring USING gist (geometry); --- etldoc: osm_transportation_name_linestring_gen1 -> osm_transportation_name_linestring_gen2 -CREATE OR REPLACE VIEW osm_transportation_name_linestring_gen2_view AS -SELECT ST_Simplify(geometry, 120) AS geometry, - osm_id, - name, - name_en, - name_de, - tags, - ref, - highway, - construction, - brunnel, - network, - z_order -FROM osm_transportation_name_linestring_gen1 -WHERE (highway IN ('motorway', 'trunk') OR highway = 'construction' AND construction IN ('motorway', 'trunk')) - AND ST_Length(geometry) > 14000 -; -CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen2 AS -SELECT * -FROM osm_transportation_name_linestring_gen2_view; -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen2_name_ref_idx ON osm_transportation_name_linestring_gen2((coalesce(name, ref))); -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen2_geometry_idx ON osm_transportation_name_linestring_gen2 USING gist (geometry); +-- Create table for simplified LineStrings +CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen1 ( + id integer, + geometry geometry('LineString'), + tags hstore, + ref text, + highway varchar, + subclass text, + brunnel text, + network route_network_type, + route_1 text, + route_2 text, + route_3 text, + route_4 text, + route_5 text, + route_6 text, + z_order integer +); -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen2_highway_partial_idx - ON osm_transportation_name_linestring_gen2 (highway, construction) - WHERE highway IN ('motorway', 'trunk', 'construction'); +-- 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); --- etldoc: osm_transportation_name_linestring_gen2 -> osm_transportation_name_linestring_gen3 -CREATE OR REPLACE VIEW osm_transportation_name_linestring_gen3_view AS -SELECT ST_Simplify(geometry, 200) AS geometry, - osm_id, - name, - name_en, - name_de, - tags, - ref, - highway, - construction, - brunnel, - network, - z_order -FROM osm_transportation_name_linestring_gen2 -WHERE (highway = 'motorway' OR highway = 'construction' AND construction = 'motorway') - AND ST_Length(geometry) > 20000 -; -CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen3 AS -SELECT * -FROM osm_transportation_name_linestring_gen3_view; -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen3_name_ref_idx ON osm_transportation_name_linestring_gen3((coalesce(name, ref))); -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen3_geometry_idx ON osm_transportation_name_linestring_gen3 USING gist (geometry); +-- 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 INDEX IF NOT EXISTS osm_transportation_name_linestring_gen3_highway_partial_idx - ON osm_transportation_name_linestring_gen3 (highway, construction) - WHERE highway IN ('motorway', 'construction'); +-- 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); --- etldoc: osm_transportation_name_linestring_gen3 -> osm_transportation_name_linestring_gen4 -CREATE OR REPLACE VIEW osm_transportation_name_linestring_gen4_view AS -SELECT ST_Simplify(geometry, 500) AS geometry, - osm_id, - name, - name_en, - name_de, - tags, - ref, - highway, - construction, - brunnel, - network, - z_order -FROM osm_transportation_name_linestring_gen3 -WHERE (highway = 'motorway' OR highway = 'construction' AND construction = 'motorway') - AND ST_Length(geometry) > 20000 -; -CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen4 AS -SELECT * -FROM osm_transportation_name_linestring_gen4_view; -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen4_name_ref_idx ON osm_transportation_name_linestring_gen4((coalesce(name, ref))); -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen4_geometry_idx ON osm_transportation_name_linestring_gen4 USING gist (geometry); +-- 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; --- Handle updates + 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, z_order) + SELECT id, ST_Simplify(geometry, 50) AS geometry, tags, ref, highway, subclass, brunnel, network, route_1, route_2, + route_3, route_4, route_5, route_6, z_order + FROM osm_transportation_name_linestring + 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.id + ) + ) AND ( + (highway IN ('motorway', 'trunk') OR highway = 'construction' AND subclass IN ('motorway', 'trunk')) AND + ST_Length(geometry) > 8000 + ) 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, z_order = excluded.z_order; + + -- 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, z_order) + SELECT id, ST_Simplify(geometry, 120) AS geometry, tags, ref, highway, subclass, brunnel, network, route_1, route_2, + route_3, route_4, route_5, route_6, z_order + FROM osm_transportation_name_linestring_gen1 + 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.id + ) + ) AND ( + (highway IN ('motorway', 'trunk') OR highway = 'construction' AND subclass IN ('motorway', 'trunk')) AND + ST_Length(geometry) > 14000 + ) 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, z_order = excluded.z_order; + + -- 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, z_order) + SELECT id, ST_Simplify(geometry, 200) AS geometry, tags, ref, highway, subclass, brunnel, network, route_1, route_2, + route_3, route_4, route_5, route_6, z_order + FROM osm_transportation_name_linestring_gen2 + 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.id + ) + ) AND ( + (highway = 'motorway' OR highway = 'construction' AND subclass = 'motorway') AND + ST_Length(geometry) > 20000 + ) 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, z_order = excluded.z_order; + + -- 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, z_order) + SELECT id, ST_Simplify(geometry, 500) AS geometry, tags, ref, highway, subclass, brunnel, network, route_1, route_2, + route_3, route_4, route_5, route_6, z_order + FROM osm_transportation_name_linestring_gen3 + 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.id + ) + ) AND ( + (highway = 'motorway' OR highway = 'construction' AND subclass = 'motorway') AND + ST_Length(geometry) > 20000 + ) 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, z_order = excluded.z_order; + + -- 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, - UNIQUE (osm_id) + 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 - INSERT INTO transportation_name.network_changes(osm_id) - VALUES (CASE WHEN tg_op IN ('DELETE', 'UPDATE') THEN old.member ELSE new.member END) - ON CONFLICT(osm_id) DO NOTHING; - + 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 - INSERT INTO transportation_name.network_changes(osm_id) - VALUES (CASE WHEN tg_op IN ('DELETE', 'UPDATE') THEN old.osm_id ELSE new.osm_id END) - ON CONFLICT(osm_id) DO NOTHING; - + 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; @@ -263,63 +653,94 @@ DECLARE t TIMESTAMP WITH TIME ZONE := clock_timestamp(); BEGIN RAISE LOG 'Refresh transportation_name_network'; - PERFORM update_osm_route_member(); + + -- 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 AS n - USING - transportation_name.network_changes AS c - WHERE n.osm_id = c.osm_id; + 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, - name, - name_en, - name_de, - tags, + tags || get_basic_names(tags, geometry) AS tags, ref, highway, - construction, + subclass, brunnel, level, + sac_scale, layer, indoor, network_type, - z_order + route_1, route_2, route_3, route_4, route_5, route_6, + z_order, + route_rank FROM ( SELECT hl.geometry, hl.osm_id, - CASE WHEN length(hl.name) > 15 THEN osml10n_street_abbrev_all(hl.name) ELSE NULLIF(hl.name, '') END AS name, - CASE WHEN length(hl.name_en) > 15 THEN osml10n_street_abbrev_en(hl.name_en) ELSE NULLIF(hl.name_en, '') END AS name_en, - CASE WHEN length(hl.name_de) > 15 THEN osml10n_street_abbrev_de(hl.name_de) ELSE NULLIF(hl.name_de, '') END AS name_de, - slice_language_tags(hl.tags) AS tags, - rm.network_type, + transportation_name_tags(hl.geometry, hl.tags, hl.name, hl.name_en, hl.name_de) AS tags, + rm1.network_type, CASE - WHEN rm.network_type IS NOT NULL AND NULLIF(rm.ref::text, '') IS NOT NULL - THEN rm.ref::text + WHEN rm1.network_type IS NOT NULL AND rm1.ref::text <> '' + THEN rm1.ref::text ELSE NULLIF(hl.ref, '') END AS ref, hl.highway, - hl.construction, + 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, - ROW_NUMBER() OVER (PARTITION BY hl.osm_id - ORDER BY rm.network_type) AS "rank", - hl.z_order + NULLIF(rm1.network, '') || '=' || COALESCE(rm1.ref, '') AS route_1, + NULLIF(rm2.network, '') || '=' || COALESCE(rm2.ref, '') AS route_2, + NULLIF(rm3.network, '') || '=' || COALESCE(rm3.ref, '') AS route_3, + NULLIF(rm4.network, '') || '=' || COALESCE(rm4.ref, '') AS route_4, + NULLIF(rm5.network, '') || '=' || COALESCE(rm5.ref, '') AS route_5, + NULLIF(rm6.network, '') || '=' || COALESCE(rm6.ref, '') 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 - hl.osm_id = c.osm_id - LEFT JOIN osm_route_member rm ON - rm.member = hl.osm_id - WHERE (hl.name <> '' OR hl.ref <> '') - AND NULLIF(hl.highway, '') IS NOT NULL + 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 - WHERE ("rank" = 1 OR "rank" IS NULL); + ON CONFLICT DO NOTHING; -- noinspection SqlWithoutWhere DELETE FROM transportation_name.network_changes; @@ -357,42 +778,109 @@ CREATE CONSTRAINT TRIGGER trigger_refresh_network FOR EACH ROW EXECUTE PROCEDURE transportation_name.refresh_network(); --- Trigger to update "osm_transportation_name_linestring" from "osm_transportation_name_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 TABLE IF NOT EXISTS transportation_name.name_changes ( - id serial PRIMARY KEY, is_old boolean, osm_id bigint, - name character varying, - name_en character varying, - name_de character varying, - ref character varying, - highway character varying, - construction character varying, - brunnel character varying, - level integer, - layer integer, - indoor boolean, - network_type route_network_type + 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, name, name_en, name_de, ref, highway, construction, - brunnel, level, layer, indoor, network_type) - VALUES (TRUE, old.osm_id, old.name, old.name_en, old.name_de, old.ref, old.highway, old.construction, - old.brunnel, old.level, old.layer, old.indoor, old.network_type); + 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, name, name_en, name_de, ref, highway, construction, - brunnel, level, layer, indoor, network_type) - VALUES (FALSE, new.osm_id, new.name, new.name_en, new.name_de, new.ref, new.highway, new.construction, - new.brunnel, new.level, new.layer, new.indoor, new.network_type); + 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; @@ -404,6 +892,18 @@ CREATE TABLE IF NOT EXISTS transportation_name.updates_name 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 @@ -412,6 +912,22 @@ BEGIN 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 @@ -419,225 +935,517 @@ DECLARE BEGIN RAISE LOG 'Refresh transportation_name'; - -- REFRESH osm_transportation_name_linestring + -- REFRESH osm_transportation_name_linestring from osm_transportation_name_network - -- Compact the change history to keep only the first and last version, and then uniq version of row - CREATE TEMP TABLE name_changes_compact AS - SELECT DISTINCT ON (name, name_en, name_de, ref, highway, construction, brunnel, level, layer, indoor, network_type) - name, - name_en, - name_de, - ref, - highway, - construction, - brunnel, - level, - layer, - indoor, - network_type, - coalesce(name, ref) AS name_ref - FROM (( - SELECT DISTINCT ON (osm_id) * - FROM transportation_name.name_changes - WHERE is_old - ORDER BY osm_id, - id ASC - ) - UNION ALL - ( - SELECT DISTINCT ON (osm_id) * - FROM transportation_name.name_changes - WHERE NOT is_old - ORDER BY osm_id, - id DESC - )) AS t; + -- 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 AS n - USING name_changes_compact AS c - WHERE coalesce(n.name, '') = coalesce(c.name, '') - AND coalesce(n.ref, '') = coalesce(c.ref, '') - AND n.name_en IS NOT DISTINCT FROM c.name_en - AND n.name_de IS NOT DISTINCT FROM c.name_de - AND n.highway IS NOT DISTINCT FROM c.highway - AND n.construction IS NOT DISTINCT FROM c.construction - AND n.brunnel IS NOT DISTINCT FROM c.brunnel - AND n.level IS NOT DISTINCT FROM c.level - AND n.layer IS NOT DISTINCT FROM c.layer - AND n.indoor IS NOT DISTINCT FROM c.indoor - AND n.network IS NOT DISTINCT FROM c.network_type; + 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; - INSERT INTO osm_transportation_name_linestring - SELECT (ST_Dump(geometry)).geom AS geometry, - NULL::bigint AS osm_id, - name, - name_en, - name_de, - tags || get_basic_names(tags, geometry) AS tags, - ref, - highway, - construction, - brunnel, - level, - layer, - indoor, - network_type AS network, - z_order + -- 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, 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 ( - SELECT ST_LineMerge(ST_Collect(n.geometry)) AS geometry, - n.name, - n.name_en, - n.name_de, - hstore(string_agg(nullif(slice_language_tags(tags || - hstore(ARRAY ['name', n.name, 'name:en', n.name_en, 'name:de', n.name_de]))::text, - ''), ',')) AS tags, - n.ref, - n.highway, - n.construction, - n.brunnel, - n.level, - n.layer, - n.indoor, - n.network_type, - min(n.z_order) AS z_order - FROM osm_transportation_name_network AS n - JOIN name_changes_compact AS c ON - coalesce(n.name, '') = coalesce(c.name, '') - AND coalesce(n.ref, '') = coalesce(c.ref, '') - AND n.name_en IS NOT DISTINCT FROM c.name_en - AND n.name_de IS NOT DISTINCT FROM c.name_de - AND n.highway IS NOT DISTINCT FROM c.highway - AND n.construction IS NOT DISTINCT FROM c.construction - AND n.brunnel IS NOT DISTINCT FROM c.brunnel - AND n.level IS NOT DISTINCT FROM c.level - AND n.layer IS NOT DISTINCT FROM c.layer - AND n.indoor IS NOT DISTINCT FROM c.indoor - AND n.network_type IS NOT DISTINCT FROM c.network_type - GROUP BY n.name, n.name_en, n.name_de, n.ref, n.highway, n.construction, n.brunnel, n.level, n.layer, n.indoor, n.network_type - ) AS highway_union; + -- 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 AND + coalesce(tags->'name', '') <> '' OR coalesce(ref, '') <> '' + ); - -- REFRESH osm_transportation_name_linestring_gen1 - DELETE FROM osm_transportation_name_linestring_gen1 AS n - USING name_changes_compact AS c - WHERE - coalesce(n.name, n.ref) = c.name_ref - AND n.name IS NOT DISTINCT FROM c.name - AND n.name_en IS NOT DISTINCT FROM c.name_en - AND n.name_de IS NOT DISTINCT FROM c.name_de - AND n.ref IS NOT DISTINCT FROM c.ref - AND n.highway IS NOT DISTINCT FROM c.highway - AND n.construction IS NOT DISTINCT FROM c.construction - AND n.brunnel IS NOT DISTINCT FROM c.brunnel - AND n.network IS NOT DISTINCT FROM c.network_type; + -- Drop temporary tables early to save resources + DROP TABLE affected_merged_linestrings; - INSERT INTO osm_transportation_name_linestring_gen1 - SELECT n.* - FROM osm_transportation_name_linestring_gen1_view AS n - JOIN name_changes_compact AS c ON - coalesce(n.name, n.ref) = c.name_ref - AND n.name IS NOT DISTINCT FROM c.name - AND n.name_en IS NOT DISTINCT FROM c.name_en - AND n.name_de IS NOT DISTINCT FROM c.name_de - AND n.ref IS NOT DISTINCT FROM c.ref - AND n.highway IS NOT DISTINCT FROM c.highway - AND n.construction IS NOT DISTINCT FROM c.construction - AND n.brunnel IS NOT DISTINCT FROM c.brunnel - AND n.network IS NOT DISTINCT FROM c.network_type; + -- Create index on geometry column and analyze the created table to speed up subsequent queries + CREATE INDEX ON linestrings_to_merge USING GIST (geometry); + ANALYZE linestrings_to_merge; - -- REFRESH osm_transportation_name_linestring_gen2 - DELETE FROM osm_transportation_name_linestring_gen2 AS n - USING name_changes_compact AS c - WHERE - coalesce(n.name, n.ref) = c.name_ref - AND n.name IS NOT DISTINCT FROM c.name - AND n.name_en IS NOT DISTINCT FROM c.name_en - AND n.name_de IS NOT DISTINCT FROM c.name_de - AND n.ref IS NOT DISTINCT FROM c.ref - AND n.highway IS NOT DISTINCT FROM c.highway - AND n.construction IS NOT DISTINCT FROM c.construction - AND n.brunnel IS NOT DISTINCT FROM c.brunnel - AND n.network IS NOT DISTINCT FROM c.network_type; + -- Add all Merged-LineStrings intersecting with Source-LineStrings affected by this update + INSERT INTO linestrings_to_merge + SELECT s.source_id AS osm_id, m.id, geometry, tags, ref, highway, subclass, brunnel, sac_scale, level, + layer, indoor, network AS network_type, route_1, route_2, route_3, route_4, route_5, route_6, z_order, + route_rank + FROM osm_transportation_name_linestring m + JOIN osm_transportation_name_linestring_source_ids s ON (s.source = 0 AND m.id = s.id) + WHERE EXISTS( + SELECT NULL FROM linestrings_to_merge WHERE ST_Intersects(linestrings_to_merge.geometry, m.geometry) + ); - INSERT INTO osm_transportation_name_linestring_gen2 - SELECT n.* - FROM osm_transportation_name_linestring_gen2_view AS n - JOIN name_changes_compact AS c ON - coalesce(n.name, n.ref) = c.name_ref - AND n.name IS NOT DISTINCT FROM c.name - AND n.name_en IS NOT DISTINCT FROM c.name_en - AND n.name_de IS NOT DISTINCT FROM c.name_de - AND n.ref IS NOT DISTINCT FROM c.ref - AND n.highway IS NOT DISTINCT FROM c.highway - AND n.construction IS NOT DISTINCT FROM c.construction - AND n.brunnel IS NOT DISTINCT FROM c.brunnel - AND n.network IS NOT DISTINCT FROM c.network_type; + -- Analyze the created table to speed up subsequent queries + ANALYZE linestrings_to_merge; - -- REFRESH osm_transportation_name_linestring_gen3 - DELETE FROM osm_transportation_name_linestring_gen3 AS n - USING name_changes_compact AS c - WHERE - coalesce(n.name, n.ref) = c.name_ref - AND n.name IS NOT DISTINCT FROM c.name - AND n.name_en IS NOT DISTINCT FROM c.name_en - AND n.name_de IS NOT DISTINCT FROM c.name_de - AND n.ref IS NOT DISTINCT FROM c.ref - AND n.highway IS NOT DISTINCT FROM c.highway - AND n.construction IS NOT DISTINCT FROM c.construction - AND n.brunnel IS NOT DISTINCT FROM c.brunnel - AND n.network IS NOT DISTINCT FROM c.network_type; + -- 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; - INSERT INTO osm_transportation_name_linestring_gen3 - SELECT n.* - FROM osm_transportation_name_linestring_gen3_view AS n - JOIN name_changes_compact AS c ON - coalesce(n.name, n.ref) = c.name_ref - AND n.name IS NOT DISTINCT FROM c.name - AND n.name_en IS NOT DISTINCT FROM c.name_en - AND n.name_de IS NOT DISTINCT FROM c.name_de - AND n.ref IS NOT DISTINCT FROM c.ref - AND n.highway IS NOT DISTINCT FROM c.highway - AND n.construction IS NOT DISTINCT FROM c.construction - AND n.brunnel IS NOT DISTINCT FROM c.brunnel - AND n.network IS NOT DISTINCT FROM c.network_type; + -- 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; - -- REFRESH osm_transportation_name_linestring_gen4 - DELETE FROM osm_transportation_name_linestring_gen4 AS n - USING name_changes_compact AS c - WHERE - coalesce(n.name, n.ref) = c.name_ref - AND n.name IS NOT DISTINCT FROM c.name - AND n.name_en IS NOT DISTINCT FROM c.name_en - AND n.name_de IS NOT DISTINCT FROM c.name_de - AND n.ref IS NOT DISTINCT FROM c.ref - AND n.highway IS NOT DISTINCT FROM c.highway - AND n.construction IS NOT DISTINCT FROM c.construction - AND n.brunnel IS NOT DISTINCT FROM c.brunnel - AND n.network IS NOT DISTINCT FROM c.network_type; + -- Drop temporary tables early to save resources + DROP TABLE linestrings_to_merge; - INSERT INTO osm_transportation_name_linestring_gen4 - SELECT n.* - FROM osm_transportation_name_linestring_gen4_view AS n - JOIN name_changes_compact AS c ON - coalesce(n.name, n.ref) = c.name_ref - AND n.name IS NOT DISTINCT FROM c.name - AND n.name_en IS NOT DISTINCT FROM c.name_en - AND n.name_de IS NOT DISTINCT FROM c.name_de - AND n.ref IS NOT DISTINCT FROM c.ref - AND n.highway IS NOT DISTINCT FROM c.highway - AND n.construction IS NOT DISTINCT FROM c.construction - AND n.brunnel IS NOT DISTINCT FROM c.brunnel - AND n.network IS NOT DISTINCT FROM c.network_type; + -- 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; - DROP TABLE name_changes_compact; + -- Create temporary Merged-LineString to Source-LineStrings-ID column to store relations before they have been + -- intersected + ALTER TABLE osm_transportation_name_linestring ADD COLUMN IF NOT EXISTS source_ids bigint[]; + + + WITH inserted_linestrings AS ( + -- Merge LineStrings of each cluster and insert them + 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 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 + array_agg(osm_id) AS 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, 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, unnest(source_ids) AS source_id, geometry + FROM inserted_linestrings + ) 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 column + ALTER TABLE osm_transportation_name_linestring DROP COLUMN IF EXISTS 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; +$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.name_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, 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 AND + name <> '' + ); + + -- Drop temporary tables early to save resources + DROP TABLE affected_merged_linestrings; + + -- Create index on geometry column and analyze the created table to speed up subsequent queries + CREATE INDEX ON linestrings_to_merge USING GIST (geometry); + ANALYZE linestrings_to_merge; + + -- Add all Merged-LineStrings intersecting with Source-LineStrings affected by this update + INSERT INTO linestrings_to_merge + SELECT s.source_id AS osm_id, m.id, geometry, tags, subclass, layer, z_order + FROM osm_transportation_name_linestring m + JOIN osm_transportation_name_linestring_source_ids s ON (s.source = 1 AND m.id = s.id) + WHERE EXISTS( + SELECT NULL FROM linestrings_to_merge WHERE 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_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 column to store relations before they have been + -- intersected + ALTER TABLE osm_transportation_name_linestring ADD COLUMN IF NOT EXISTS source_ids bigint[]; + + WITH inserted_linestrings AS ( + -- Merge LineStrings of each cluster and insert them + INSERT INTO osm_transportation_name_linestring(source, geometry, 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 + array_agg(osm_id) AS 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, 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, unnest(source_ids) AS source_id, geometry + FROM inserted_linestrings + ) 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 column + ALTER TABLE osm_transportation_name_linestring DROP COLUMN IF EXISTS 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.name_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, 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 AND + name <> '' + ); + + -- Drop temporary tables early to save resources + DROP TABLE affected_merged_linestrings; + + -- Create index on geometry column and analyze the created table to speed up subsequent queries + CREATE INDEX ON linestrings_to_merge USING GIST (geometry); + ANALYZE linestrings_to_merge; + + -- Add all Merged-LineStrings intersecting with Source-LineStrings affected by this update + INSERT INTO linestrings_to_merge + SELECT s.source_id AS osm_id, m.id, geometry, tags, subclass, layer, z_order + FROM osm_transportation_name_linestring m + JOIN osm_transportation_name_linestring_source_ids s ON (s.source = 2 AND m.id = s.id) + WHERE EXISTS( + SELECT NULL FROM linestrings_to_merge WHERE 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_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 column to store relations before they have been + -- intersected + ALTER TABLE osm_transportation_name_linestring ADD COLUMN IF NOT EXISTS source_ids bigint[]; + + WITH inserted_linestrings AS ( + -- Merge LineStrings of each cluster and insert them + INSERT INTO osm_transportation_name_linestring(source, geometry, 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 + array_agg(osm_id) AS 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, 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, unnest(source_ids) AS source_id, geometry + FROM inserted_linestrings + ) 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 column + ALTER TABLE osm_transportation_name_linestring DROP COLUMN IF EXISTS 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 @@ -645,15 +1453,59 @@ CREATE TRIGGER trigger_store_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/etl_diagram.png b/layers/water/etl_diagram.png index 998f535..6efbbb8 100644 Binary files a/layers/water/etl_diagram.png and b/layers/water/etl_diagram.png differ diff --git a/layers/water/mapping.yaml b/layers/water/mapping.yaml index 0e3b865..8491d67 100644 --- a/layers/water/mapping.yaml +++ b/layers/water/mapping.yaml @@ -66,6 +66,9 @@ tables: type: string - name: tags type: hstore_tags + - name: place + key: place + type: string - name: natural key: natural type: string @@ -75,6 +78,12 @@ tables: - 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 @@ -93,12 +102,16 @@ tables: natural: - water - bay + - spring waterway: + - dock + water: - river - - riverbank - stream - canal - - drain - ditch - - dock + - drain + - pond + - basin + - wastewater type: polygon diff --git a/layers/water/mapping_diagram.png b/layers/water/mapping_diagram.png index 05133be..7a6f2ef 100644 Binary files a/layers/water/mapping_diagram.png 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/water.sql b/layers/water/water.sql index 6b8144e..9e94817 100644 --- a/layers/water/water.sql +++ b/layers/water/water.sql @@ -1,8 +1,9 @@ -CREATE OR REPLACE FUNCTION water_class(waterway text) RETURNS text AS +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 'river' + ELSE 'lake' END; $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; @@ -18,12 +19,59 @@ $$ 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 ST_Simplify(geometry, ZRes(7)) AS geometry, +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, @@ -37,13 +85,16 @@ CREATE INDEX IF NOT EXISTS ne_10m_ocean_gen_z5_idx ON ne_10m_ocean_gen_z5 USING DROP MATERIALIZED VIEW IF EXISTS ne_10m_lakes_gen_z5 CASCADE; CREATE MATERIALIZED VIEW ne_10m_lakes_gen_z5 AS ( -SELECT ogc_fid, - ST_MakeValid(ST_Simplify(geometry, ZRes(7))) AS geometry, +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); @@ -51,8 +102,8 @@ CREATE INDEX IF NOT EXISTS ne_10m_lakes_gen_z5_idx ON ne_10m_lakes_gen_z5 USING DROP MATERIALIZED VIEW IF EXISTS ne_10m_lakes_gen_z4 CASCADE; CREATE MATERIALIZED VIEW ne_10m_lakes_gen_z4 AS ( -SELECT ogc_fid, - ST_MakeValid(ST_Simplify(geometry, ZRes(6))) AS geometry, +SELECT id, + (ST_Dump(ST_MakeValid(ST_Simplify(geometry, ZRes(6))))).geom AS geometry, class, is_intermittent, is_bridge, @@ -66,7 +117,8 @@ CREATE INDEX IF NOT EXISTS ne_10m_lakes_gen_z4_idx ON ne_10m_lakes_gen_z4 USING DROP MATERIALIZED VIEW IF EXISTS ne_50m_ocean_gen_z4 CASCADE; CREATE MATERIALIZED VIEW ne_50m_ocean_gen_z4 AS ( -SELECT ST_Simplify(geometry, ZRes(6)) AS geometry, +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, @@ -79,7 +131,8 @@ CREATE INDEX IF NOT EXISTS ne_50m_ocean_gen_z4_idx ON ne_50m_ocean_gen_z4 USING DROP MATERIALIZED VIEW IF EXISTS ne_50m_ocean_gen_z3 CASCADE; CREATE MATERIALIZED VIEW ne_50m_ocean_gen_z3 AS ( -SELECT ST_Simplify(geometry, ZRes(5)) AS geometry, +SELECT id, + ST_Simplify(geometry, ZRes(5)) AS geometry, class, is_intermittent, is_bridge, @@ -92,7 +145,8 @@ CREATE INDEX IF NOT EXISTS ne_50m_ocean_gen_z3_idx ON ne_50m_ocean_gen_z3 USING DROP MATERIALIZED VIEW IF EXISTS ne_50m_ocean_gen_z2 CASCADE; CREATE MATERIALIZED VIEW ne_50m_ocean_gen_z2 AS ( -SELECT ST_Simplify(geometry, ZRes(4)) AS geometry, +SELECT id, + ST_Simplify(geometry, ZRes(4)) AS geometry, class, is_intermittent, is_bridge, @@ -106,13 +160,14 @@ CREATE INDEX IF NOT EXISTS ne_50m_ocean_gen_z2_idx ON ne_50m_ocean_gen_z2 USING DROP MATERIALIZED VIEW IF EXISTS ne_50m_lakes_gen_z3 CASCADE; CREATE MATERIALIZED VIEW ne_50m_lakes_gen_z3 AS ( -SELECT ogc_fid, - ST_MakeValid(ST_Simplify(geometry, ZRes(5))) AS geometry, - 'lakes'::text AS class, +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); @@ -120,8 +175,8 @@ CREATE INDEX IF NOT EXISTS ne_50m_lakes_gen_z3_idx ON ne_50m_lakes_gen_z3 USING DROP MATERIALIZED VIEW IF EXISTS ne_50m_lakes_gen_z2 CASCADE; CREATE MATERIALIZED VIEW ne_50m_lakes_gen_z2 AS ( -SELECT ogc_fid, - ST_MakeValid(ST_Simplify(geometry, ZRes(4))) AS geometry, +SELECT id, + (ST_Dump(ST_MakeValid(ST_Simplify(geometry, ZRes(4))))).geom AS geometry, class, is_intermittent, is_bridge, @@ -135,7 +190,8 @@ CREATE INDEX IF NOT EXISTS ne_50m_lakes_gen_z2_idx ON ne_50m_lakes_gen_z2 USING DROP MATERIALIZED VIEW IF EXISTS ne_110m_ocean_gen_z1 CASCADE; CREATE MATERIALIZED VIEW ne_110m_ocean_gen_z1 AS ( -SELECT ST_Simplify(geometry, ZRes(3)) AS geometry, +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, @@ -148,7 +204,8 @@ CREATE INDEX IF NOT EXISTS ne_110m_ocean_gen_z1_idx ON ne_110m_ocean_gen_z1 USIN DROP MATERIALIZED VIEW IF EXISTS ne_110m_ocean_gen_z0 CASCADE; CREATE MATERIALIZED VIEW ne_110m_ocean_gen_z0 AS ( -SELECT ST_Simplify(geometry, ZRes(2)) AS geometry, +SELECT id, + ST_Simplify(geometry, ZRes(2)) AS geometry, class, is_intermittent, is_bridge, @@ -163,13 +220,14 @@ CREATE INDEX IF NOT EXISTS ne_110m_ocean_gen_z0_idx ON ne_110m_ocean_gen_z0 USIN DROP MATERIALIZED VIEW IF EXISTS ne_110m_lakes_gen_z1 CASCADE; CREATE MATERIALIZED VIEW ne_110m_lakes_gen_z1 AS ( -SELECT ogc_fid, - ST_Simplify(geometry, ZRes(3)) AS geometry, - 'lakes'::text AS class, +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); @@ -177,8 +235,8 @@ CREATE INDEX IF NOT EXISTS ne_110m_lakes_gen_z1_idx ON ne_110m_lakes_gen_z1 USIN DROP MATERIALIZED VIEW IF EXISTS ne_110m_lakes_gen_z0 CASCADE; CREATE MATERIALIZED VIEW ne_110m_lakes_gen_z0 AS ( -SELECT ogc_fid, - ST_Simplify(geometry, ZRes(2)) AS geometry, +SELECT id, + (ST_Dump(ST_Simplify(geometry, ZRes(2)))).geom AS geometry, class, is_intermittent, is_bridge, @@ -187,11 +245,19 @@ 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 geometry, +SELECT id, + geometry, class, is_intermittent, is_bridge, @@ -199,7 +265,8 @@ SELECT geometry, FROM ne_110m_ocean_gen_z0 UNION ALL -- etldoc: ne_110m_lakes_gen_z0 -> water_z0 -SELECT geometry, +SELECT id, + geometry, class, is_intermittent, is_bridge, @@ -210,7 +277,8 @@ FROM ne_110m_lakes_gen_z0 CREATE OR REPLACE VIEW water_z1 AS ( -- etldoc: ne_110m_ocean_gen_z1 -> water_z1 -SELECT geometry, +SELECT id, + geometry, class, is_intermittent, is_bridge, @@ -218,7 +286,8 @@ SELECT geometry, FROM ne_110m_ocean_gen_z1 UNION ALL -- etldoc: ne_110m_lakes_gen_z1 -> water_z1 -SELECT geometry, +SELECT id, + geometry, class, is_intermittent, is_bridge, @@ -229,7 +298,8 @@ FROM ne_110m_lakes_gen_z1 CREATE OR REPLACE VIEW water_z2 AS ( -- etldoc: ne_50m_ocean_gen_z2 -> water_z2 -SELECT geometry, +SELECT id, + geometry, class, is_intermittent, is_bridge, @@ -237,7 +307,8 @@ SELECT geometry, FROM ne_50m_ocean_gen_z2 UNION ALL -- etldoc: ne_50m_lakes_gen_z2 -> water_z2 -SELECT geometry, +SELECT id, + geometry, class, is_intermittent, is_bridge, @@ -248,7 +319,8 @@ FROM ne_50m_lakes_gen_z2 CREATE OR REPLACE VIEW water_z3 AS ( -- etldoc: ne_50m_ocean_gen_z3 -> water_z3 -SELECT geometry, +SELECT id, + geometry, class, is_intermittent, is_bridge, @@ -256,7 +328,8 @@ SELECT geometry, FROM ne_50m_ocean_gen_z3 UNION ALL -- etldoc: ne_50m_lakes_gen_z3 -> water_z3 -SELECT geometry, +SELECT id, + geometry, class, is_intermittent, is_bridge, @@ -267,7 +340,8 @@ FROM ne_50m_lakes_gen_z3 CREATE OR REPLACE VIEW water_z4 AS ( -- etldoc: ne_50m_ocean_gen_z4 -> water_z4 -SELECT geometry, +SELECT id, + geometry, class, is_intermittent, is_bridge, @@ -275,7 +349,8 @@ SELECT geometry, FROM ne_50m_ocean_gen_z4 UNION ALL -- etldoc: ne_10m_lakes_gen_z4 -> water_z4 -SELECT geometry, +SELECT id, + geometry, class, is_intermittent, is_bridge, @@ -283,10 +358,12 @@ SELECT geometry, FROM ne_10m_lakes_gen_z4 ); + CREATE OR REPLACE VIEW water_z5 AS ( -- etldoc: ne_10m_ocean_gen_z5 -> water_z5 -SELECT geometry, +SELECT id, + geometry, class, is_intermittent, is_bridge, @@ -294,7 +371,8 @@ SELECT geometry, FROM ne_10m_ocean_gen_z5 UNION ALL -- etldoc: ne_10m_lakes_gen_z5 -> water_z5 -SELECT geometry, +SELECT id, + geometry, class, is_intermittent, is_bridge, @@ -302,10 +380,11 @@ SELECT geometry, FROM ne_10m_lakes_gen_z5 ); -CREATE OR REPLACE VIEW water_z6 AS +CREATE MATERIALIZED VIEW water_z6 AS ( -- etldoc: osm_ocean_polygon_gen_z6 -> water_z6 -SELECT geometry, +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, @@ -313,19 +392,22 @@ SELECT geometry, FROM osm_ocean_polygon_gen_z6 UNION ALL -- etldoc: osm_water_polygon_gen_z6 -> water_z6 -SELECT geometry, - water_class(waterway) AS class, +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 OR REPLACE VIEW water_z7 AS +CREATE MATERIALIZED VIEW water_z7 AS ( -- etldoc: osm_ocean_polygon_gen_z7 -> water_z7 -SELECT geometry, +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, @@ -333,19 +415,22 @@ SELECT geometry, FROM osm_ocean_polygon_gen_z7 UNION ALL -- etldoc: osm_water_polygon_gen_z7 -> water_z7 -SELECT geometry, - water_class(waterway) AS class, +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 OR REPLACE VIEW water_z8 AS +CREATE MATERIALIZED VIEW water_z8 AS ( -- etldoc: osm_ocean_polygon_gen_z8 -> water_z8 -SELECT geometry, +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, @@ -353,19 +438,22 @@ SELECT geometry, FROM osm_ocean_polygon_gen_z8 UNION ALL -- etldoc: osm_water_polygon_gen_z8 -> water_z8 -SELECT geometry, - water_class(waterway) AS class, +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 OR REPLACE VIEW water_z9 AS +CREATE MATERIALIZED VIEW water_z9 AS ( -- etldoc: osm_ocean_polygon_gen_z9 -> water_z9 -SELECT geometry, +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, @@ -373,19 +461,22 @@ SELECT geometry, FROM osm_ocean_polygon_gen_z9 UNION ALL -- etldoc: osm_water_polygon_gen_z9 -> water_z9 -SELECT geometry, - water_class(waterway) AS class, +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 OR REPLACE VIEW water_z10 AS +CREATE MATERIALIZED VIEW water_z10 AS ( -- etldoc: osm_ocean_polygon_gen_z10 -> water_z10 -SELECT geometry, +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, @@ -393,19 +484,22 @@ SELECT geometry, FROM osm_ocean_polygon_gen_z10 UNION ALL -- etldoc: osm_water_polygon_gen_z10 -> water_z10 -SELECT geometry, - water_class(waterway) AS class, +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 OR REPLACE VIEW water_z11 AS +CREATE MATERIALIZED VIEW water_z11 AS ( -- etldoc: osm_ocean_polygon_gen_z11 -> water_z11 -SELECT geometry, +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, @@ -413,19 +507,22 @@ SELECT geometry, FROM osm_ocean_polygon_gen_z11 UNION ALL -- etldoc: osm_water_polygon_gen_z11 -> water_z11 -SELECT geometry, - water_class(waterway) AS class, +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 OR REPLACE VIEW water_z12 AS +CREATE MATERIALIZED VIEW water_z12 AS ( -- etldoc: osm_ocean_polygon_union -> water_z12 -SELECT geometry, +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, @@ -433,14 +530,16 @@ SELECT geometry, FROM osm_ocean_polygon_union UNION ALL -- etldoc: osm_water_polygon -> water_z12 -SELECT geometry, - water_class(waterway) AS class, +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+" ] ; @@ -448,6 +547,7 @@ WHERE "natural" != 'bay' CREATE OR REPLACE FUNCTION layer_water(bbox geometry, zoom_level int) RETURNS TABLE ( + id bigint, geometry geometry, class text, brunnel text, @@ -455,7 +555,8 @@ CREATE OR REPLACE FUNCTION layer_water(bbox geometry, zoom_level int) ) AS $$ -SELECT geometry, +SELECT id, + geometry, class::text, waterway_brunnel(is_bridge, is_tunnel) AS brunnel, is_intermittent::int AS intermittent diff --git a/layers/water/water.yaml b/layers/water/water.yaml index cbd0a07..8a71bb2 100644 --- a/layers/water/water.yaml +++ b/layers/water/water.yaml @@ -1,5 +1,14 @@ 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 @@ -9,17 +18,33 @@ layer: 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`. - Water bodies are classified as `lake` or `river` for water bodies with the [`waterway`](http://wiki.openstreetmap.org/wiki/Key:waterway) tag. + 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: - lake: - waterway: ['', 'lake'] dock: waterway: 'dock' river: + water: ['river', 'stream', 'canal', 'ditch', 'drain'] + pond: + water: ['pond', 'basin', 'wastewater'] + 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. @@ -32,7 +57,7 @@ layer: - tunnel buffer_size: 4 datasource: - query: (SELECT geometry, class, intermittent, brunnel FROM layer_water(!bbox!, z(!scale_denominator!))) AS t + query: (SELECT id, geometry, class, intermittent, brunnel FROM layer_water(!bbox!, z(!scale_denominator!))) AS t schema: - ./update_water.sql - ./water.sql diff --git a/layers/water_name/etl_diagram.png b/layers/water_name/etl_diagram.png index 5792d42..89e1440 100644 Binary files a/layers/water_name/etl_diagram.png and b/layers/water_name/etl_diagram.png differ diff --git a/layers/water_name/mapping.yaml b/layers/water_name/mapping.yaml index 558ad61..8c5365d 100644 --- a/layers/water_name/mapping.yaml +++ b/layers/water_name/mapping.yaml @@ -21,6 +21,9 @@ tables: - name: place key: place type: string + - name: natural + key: natural + type: string - name: rank key: rank type: integer @@ -34,3 +37,6 @@ tables: place: - ocean - sea + natural: + - bay + - strait diff --git a/layers/water_name/mapping_diagram.png b/layers/water_name/mapping_diagram.png index 166e748..7a880ad 100644 Binary files a/layers/water_name/mapping_diagram.png 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 index 5fdbc7a..95dfd96 100644 --- a/layers/water_name/update_marine_point.sql +++ b/layers/water_name/update_marine_point.sql @@ -6,7 +6,7 @@ CREATE SCHEMA IF NOT EXISTS water_name_marine; CREATE TABLE IF NOT EXISTS water_name_marine.osm_ids ( - osm_id bigint + osm_id bigint PRIMARY KEY ); CREATE OR REPLACE FUNCTION update_osm_marine_point(full_update boolean) RETURNS void AS @@ -45,11 +45,7 @@ CREATE INDEX IF NOT EXISTS osm_marine_point_rank_idx ON osm_marine_point ("rank" CREATE OR REPLACE FUNCTION water_name_marine.store() RETURNS trigger AS $$ BEGIN - IF (tg_op = 'DELETE') THEN - INSERT INTO water_name_marine.osm_ids VALUES (OLD.osm_id); - ELSE - INSERT INTO water_name_marine.osm_ids VALUES (NEW.osm_id); - END IF; + INSERT INTO water_name_marine.osm_ids VALUES (NEW.osm_id) ON CONFLICT (osm_id) DO NOTHING; RETURN NULL; END; $$ LANGUAGE plpgsql; @@ -74,6 +70,11 @@ 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; @@ -86,13 +87,13 @@ END; $$ LANGUAGE plpgsql; CREATE TRIGGER trigger_store - AFTER INSERT OR UPDATE OR DELETE + 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 OR DELETE + AFTER INSERT OR UPDATE ON osm_marine_point FOR EACH STATEMENT EXECUTE PROCEDURE water_name_marine.flag(); diff --git a/layers/water_name/update_water_lakeline.sql b/layers/water_name/update_water_lakeline.sql deleted file mode 100644 index fccc317..0000000 --- a/layers/water_name/update_water_lakeline.sql +++ /dev/null @@ -1,94 +0,0 @@ -DROP TRIGGER IF EXISTS trigger_delete_line ON osm_water_polygon; -DROP TRIGGER IF EXISTS trigger_update_line ON osm_water_polygon; -DROP TRIGGER IF EXISTS trigger_insert_line ON osm_water_polygon; - -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); - --- Handle updates - -CREATE SCHEMA IF NOT EXISTS water_lakeline; - -CREATE OR REPLACE FUNCTION water_lakeline.delete() RETURNS trigger AS -$$ -BEGIN - DELETE - FROM osm_water_lakeline - WHERE osm_water_lakeline.osm_id = OLD.osm_id; - - RETURN NULL; -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION water_lakeline.update() RETURNS trigger AS -$$ -BEGIN - UPDATE osm_water_lakeline - SET (osm_id, geometry, name, name_en, name_de, tags, area, is_intermittent) = - (SELECT * FROM osm_water_lakeline_view WHERE osm_water_lakeline_view.osm_id = NEW.osm_id) - WHERE osm_water_lakeline.osm_id = NEW.osm_id; - - RETURN NULL; -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION water_lakeline.insert() RETURNS trigger AS -$$ -BEGIN - INSERT INTO osm_water_lakeline - SELECT * - FROM osm_water_lakeline_view - WHERE osm_water_lakeline_view.osm_id = NEW.osm_id - -- May happen in case we replay update - ON CONFLICT ON CONSTRAINT osm_water_lakeline_pk - DO NOTHING; - - RETURN NULL; -END; -$$ LANGUAGE plpgsql; - -CREATE TRIGGER trigger_delete_line - AFTER DELETE - ON osm_water_polygon - FOR EACH ROW -EXECUTE PROCEDURE water_lakeline.delete(); - -CREATE TRIGGER trigger_update_line - AFTER UPDATE - ON osm_water_polygon - FOR EACH ROW -EXECUTE PROCEDURE water_lakeline.update(); - -CREATE TRIGGER trigger_insert_line - AFTER INSERT - ON osm_water_polygon - FOR EACH ROW -EXECUTE PROCEDURE water_lakeline.insert(); 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/update_water_point.sql b/layers/water_name/update_water_point.sql deleted file mode 100644 index 5accc80..0000000 --- a/layers/water_name/update_water_point.sql +++ /dev/null @@ -1,95 +0,0 @@ -DROP TRIGGER IF EXISTS trigger_delete_point ON osm_water_polygon; -DROP TRIGGER IF EXISTS trigger_update_point ON osm_water_polygon; -DROP TRIGGER IF EXISTS trigger_insert_point ON osm_water_polygon; - -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, - update_tags(wp.tags, ST_PointOnSurface(wp.geometry)) AS tags, - 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_polygon -> osm_water_point --- etldoc: lake_centerline -> osm_water_point -CREATE TABLE IF NOT EXISTS osm_water_point AS -SELECT * -FROM osm_water_point_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_point; - -CREATE OR REPLACE FUNCTION water_point.delete() RETURNS trigger AS -$$ -BEGIN - DELETE - FROM osm_water_point - WHERE osm_water_point.osm_id = OLD.osm_id; - - RETURN NULL; -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION water_point.update() RETURNS trigger AS -$$ -BEGIN - UPDATE osm_water_point - SET (osm_id, geometry, name, name_en, name_de, tags, area, is_intermittent) = - (SELECT * FROM osm_water_point_view WHERE osm_water_point_view.osm_id = NEW.osm_id) - WHERE osm_water_point.osm_id = NEW.osm_id; - - RETURN NULL; -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION water_point.insert() RETURNS trigger AS -$$ -BEGIN - INSERT INTO osm_water_point - SELECT * - FROM osm_water_point_view - WHERE osm_water_point_view.osm_id = NEW.osm_id - -- May happen in case we replay update - ON CONFLICT ON CONSTRAINT osm_water_point_pk - DO NOTHING; - - RETURN NULL; -END; -$$ LANGUAGE plpgsql; - -CREATE TRIGGER trigger_delete_point - AFTER DELETE - ON osm_water_polygon - FOR EACH ROW -EXECUTE PROCEDURE water_point.delete(); - -CREATE TRIGGER trigger_update_point - AFTER UPDATE - ON osm_water_polygon - FOR EACH ROW -EXECUTE PROCEDURE water_point.update(); - -CREATE TRIGGER trigger_insert_point - AFTER INSERT - ON osm_water_polygon - FOR EACH ROW -EXECUTE PROCEDURE water_point.insert(); diff --git a/layers/water_name/water_name.sql b/layers/water_name/water_name.sql index 41ed543..748b390 100644 --- a/layers/water_name/water_name.sql +++ b/layers/water_name/water_name.sql @@ -31,7 +31,7 @@ SELECT is_intermittent::int AS intermittent FROM osm_water_lakeline WHERE geometry && bbox - AND ((zoom_level BETWEEN 9 AND 13 AND LineLabel(zoom_level, NULLIF(name, ''), geometry)) + AND ((zoom_level BETWEEN 3 AND 13 AND LineLabel(zoom_level, NULLIF(name, ''), geometry)) OR (zoom_level >= 14)) UNION ALL SELECT @@ -46,12 +46,14 @@ SELECT COALESCE(NULLIF(name_en, ''), name) AS name_en, COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, tags, - 'lake'::text AS class, + class, is_intermittent::int AS intermittent FROM osm_water_point WHERE geometry && bbox AND ( - (zoom_level BETWEEN 9 AND 13 AND area > 70000 * 2 ^ (20 - zoom_level)) + -- 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 @@ -65,15 +67,15 @@ SELECT COALESCE(NULLIF(name_en, ''), name) AS name_en, COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, tags, - place::text AS class, + COALESCE(NULLIF("natural",''), "place") AS class, is_intermittent::int AS intermittent FROM osm_marine_point WHERE geometry && bbox - AND ( - place = 'ocean' - OR (zoom_level >= "rank" AND "rank" IS NOT NULL) - OR (zoom_level >= 8) - ); + 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 index 82c4cff..89b6098 100644 --- a/layers/water_name/water_name.yaml +++ b/layers/water_name/water_name.yaml @@ -1,8 +1,12 @@ 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/lukasmartinelli/osm-lakelines) project + 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. @@ -10,9 +14,13 @@ layer: name_de: German name `name:de` if available, otherwise `name` or `name:en`. class: description: | - At the moment only `lake` since no ocean parts are labelled. *Reserved for future use*. + 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. @@ -27,8 +35,7 @@ layer: 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_lakeline.sql - - ./update_water_point.sql + - ./update_water_name.sql - ./water_name.sql datasources: - type: imposm3 diff --git a/layers/waterway/etl_diagram.png b/layers/waterway/etl_diagram.png index 7619531..e88b513 100644 Binary files a/layers/waterway/etl_diagram.png and b/layers/waterway/etl_diagram.png differ diff --git a/layers/waterway/mapping.yaml b/layers/waterway/mapping.yaml index 2a34a41..cecd114 100644 --- a/layers/waterway/mapping.yaml +++ b/layers/waterway/mapping.yaml @@ -42,3 +42,26 @@ tables: - 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 index cd6c0b3..45b61fe 100644 Binary files a/layers/waterway/mapping_diagram.png 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 index ae044cd..65cc39f 100644 --- a/layers/waterway/update_important_waterway.sql +++ b/layers/waterway/update_important_waterway.sql @@ -1,3 +1,4 @@ +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; @@ -6,110 +7,342 @@ DROP TRIGGER IF EXISTS trigger_refresh ON waterway_important.updates; -- 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 -CREATE INDEX IF NOT EXISTS osm_waterway_linestring_waterway_partial_idx - ON osm_waterway_linestring (waterway) - WHERE waterway = 'river'; +-- 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); -CREATE INDEX IF NOT EXISTS osm_waterway_linestring_name_partial_idx - ON osm_waterway_linestring (name) - WHERE name <> ''; +-- 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 +); + +-- 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 -CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring AS -SELECT (ST_Dump(geometry)).geom AS geometry, +-- 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, - tags + slice_language_tags(tags) AS tags FROM ( - SELECT ST_LineMerge(ST_Union(geometry)) AS geometry, - name, - name_en, - name_de, - slice_language_tags(tags) AS tags - FROM osm_waterway_linestring - WHERE name <> '' - AND waterway = 'river' - AND ST_IsValid(geometry) - GROUP BY name, name_en, name_de, slice_language_tags(tags) - ) AS waterway_union; -CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_names ON osm_important_waterway_linestring (name); -CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_geometry_idx ON osm_important_waterway_linestring USING gist (geometry); + 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); --- etldoc: osm_important_waterway_linestring -> osm_important_waterway_linestring_gen_z11 -DROP MATERIALIZED VIEW IF EXISTS osm_important_waterway_linestring_gen_z11 CASCADE; -CREATE MATERIALIZED VIEW osm_important_waterway_linestring_gen_z11 AS -( -SELECT ST_Simplify(geometry, ZRes(12)) AS geometry, - name, - name_en, - name_de, - tags -FROM osm_important_waterway_linestring -WHERE ST_Length(geometry) > 1000 - ); -CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen_z11_name_idx ON osm_important_waterway_linestring_gen_z11 (name); -CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen_z11_geometry_idx ON osm_important_waterway_linestring_gen_z11 USING gist (geometry); +-- Geometry Index +CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_geometry_idx + ON osm_important_waterway_linestring USING gist (geometry); --- etldoc: osm_important_waterway_linestring_gen_z11 -> osm_important_waterway_linestring_gen_z10 -DROP MATERIALIZED VIEW IF EXISTS osm_important_waterway_linestring_gen_z10 CASCADE; -CREATE MATERIALIZED VIEW osm_important_waterway_linestring_gen_z10 AS -( -SELECT ST_Simplify(geometry, ZRes(11)) AS geometry, - name, - name_en, - name_de, - tags -FROM osm_important_waterway_linestring_gen_z11 -WHERE ST_Length(geometry) > 4000 - ); -CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen_z10_name_idx ON osm_important_waterway_linestring_gen_z10 (name); -CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen_z10_geometry_idx ON osm_important_waterway_linestring_gen_z10 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; --- etldoc: osm_important_waterway_linestring_gen_z10 -> osm_important_waterway_linestring_gen_z9 -DROP MATERIALIZED VIEW IF EXISTS osm_important_waterway_linestring_gen_z9 CASCADE; -CREATE MATERIALIZED VIEW osm_important_waterway_linestring_gen_z9 AS -( -SELECT ST_Simplify(geometry, ZRes(10)) AS geometry, - name, - name_en, - name_de, - tags -FROM osm_important_waterway_linestring_gen_z10 -WHERE ST_Length(geometry) > 8000 - ); -CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen_z9_name_idx ON osm_important_waterway_linestring_gen_z9 (name); -CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen_z9_geometry_idx ON osm_important_waterway_linestring_gen_z9 USING gist (geometry); + 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; --- Handle updates + 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 TABLE IF NOT EXISTS waterway_important.changes ( - id serial PRIMARY KEY, osm_id bigint, is_old boolean, - name character varying, - name_en character varying, - name_de character varying, - tags hstore + 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, name, name_en, name_de, tags) - VALUES (TRUE, OLD.name, OLD.name_en, OLD.name_de, slice_language_tags(OLD.tags)); + 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, name, name_en, name_de, tags) - VALUES (FALSE, NEW.name, NEW.name_en, NEW.name_de, slice_language_tags(NEW.tags)); + 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, @@ -133,80 +366,170 @@ BEGIN -- REFRESH osm_important_waterway_linestring - -- Compact the change history to keep only the first and last version, and then uniq version of row - CREATE TEMP TABLE changes_compact AS - SELECT DISTINCT ON (name, name_en, name_de, tags) - name, - name_en, - name_de, - tags - FROM (( - SELECT DISTINCT ON (osm_id) * - FROM waterway_important.changes - WHERE is_old - ORDER BY osm_id, - id ASC - ) - UNION ALL - ( - SELECT DISTINCT ON (osm_id) * - FROM waterway_important.changes - WHERE NOT is_old - ORDER BY osm_id, - id DESC - )) AS t; + -- 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 AS w - USING changes_compact AS c - WHERE w.name = c.name - AND w.name_en IS NOT DISTINCT FROM c.name_en - AND w.name_de IS NOT DISTINCT FROM c.name_de - AND w.tags IS NOT DISTINCT FROM c.tags; + 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; - INSERT INTO osm_important_waterway_linestring - SELECT (ST_Dump(geometry)).geom AS geometry, - name, - name_en, - name_de, - tags + -- 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, 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 ( - SELECT ST_LineMerge(ST_Union(geometry)) AS geometry, - w.name, - w.name_en, - w.name_de, - slice_language_tags(w.tags) AS tags - FROM osm_waterway_linestring AS w - JOIN changes_compact AS c ON - w.name = c.name AND w.name_en IS NOT DISTINCT FROM c.name_en AND - w.name_de IS NOT DISTINCT FROM c.name_de AND - slice_language_tags(w.tags) IS NOT DISTINCT FROM c.tags - WHERE w.name <> '' - AND w.waterway = 'river' - AND ST_IsValid(geometry) - GROUP BY w.name, w.name_en, w.name_de, slice_language_tags(w.tags) - ) AS waterway_union; + -- 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 AND + name <> '' AND waterway = 'river' AND ST_IsValid(geometry) + ); - -- REFRESH osm_important_waterway_linestring_gen_z11 - REFRESH MATERIALIZED VIEW osm_important_waterway_linestring_gen_z11; + -- Drop temporary tables early to save resources + DROP TABLE affected_merged_linestrings; - -- REFRESH osm_important_waterway_linestring_gen_z10 - REFRESH MATERIALIZED VIEW osm_important_waterway_linestring_gen_z10; + -- Create index on geometry column and analyze the created table to speed up subsequent queries + CREATE INDEX ON linestrings_to_merge USING GIST (geometry); + ANALYZE linestrings_to_merge; - -- REFRESH osm_important_waterway_linestring_gen_z9 - REFRESH MATERIALIZED VIEW osm_important_waterway_linestring_gen_z9; + -- Add all Merged-LineStrings intersecting with Source-LineStrings affected by this update + INSERT INTO linestrings_to_merge + SELECT s.source_id AS osm_id, m.id, geometry, name, name_en, name_de, tags + FROM osm_important_waterway_linestring m + JOIN osm_important_waterway_linestring_source_ids s ON (m.id = s.id) + WHERE EXISTS(SELECT NULL FROM linestrings_to_merge WHERE 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 column to store relations before they have been + -- intersected + ALTER TABLE osm_important_waterway_linestring ADD COLUMN IF NOT EXISTS source_ids bigint[]; + + WITH inserted_linestrings AS ( + -- Merge LineStrings of each cluster and insert them + 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, + tags + FROM clustered_linestrings_to_merge + GROUP BY cluster_group, cluster, name, name_en, name_de, tags + RETURNING id, 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, unnest(source_ids) AS source_id, geometry + FROM inserted_linestrings + ) 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 column + ALTER TABLE osm_important_waterway_linestring DROP COLUMN IF EXISTS source_ids; - DROP TABLE changes_compact; -- 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 diff --git a/layers/waterway/waterway.sql b/layers/waterway/waterway.sql index 22ce2a7..6b2f7f3 100644 --- a/layers/waterway/waterway.sql +++ b/layers/waterway/waterway.sql @@ -62,30 +62,43 @@ 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); --- ne_10m_rivers_lake_centerlines --- etldoc: ne_10m_rivers_lake_centerlines -> ne_10m_rivers_lake_centerlines_gen_z8 -DROP MATERIALIZED VIEW IF EXISTS ne_10m_rivers_lake_centerlines_gen_z8 CASCADE; -CREATE MATERIALIZED VIEW ne_10m_rivers_lake_centerlines_gen_z8 AS -( -SELECT ST_Simplify(geometry, ZRes(10)) as 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, - NULL::text AS name, + name, NULL::text AS name_en, NULL::text AS name_de, - NULL::hstore AS tags, + tags, NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, NULL::boolean AS is_intermittent -FROM ne_10m_rivers_lake_centerlines -WHERE featurecla = 'River' - ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; -CREATE INDEX IF NOT EXISTS ne_10m_rivers_lake_centerlines_gen_z8_idx ON ne_10m_rivers_lake_centerlines_gen_z8 USING gist (geometry); + 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: ne_10m_rivers_lake_centerlines_gen_z8 -> ne_10m_rivers_lake_centerlines_gen_z7 -DROP MATERIALIZED VIEW IF EXISTS ne_10m_rivers_lake_centerlines_gen_z7 CASCADE; -CREATE MATERIALIZED VIEW ne_10m_rivers_lake_centerlines_gen_z7 AS -( -SELECT ST_Simplify(geometry, ZRes(9)) as 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, @@ -94,15 +107,15 @@ SELECT ST_Simplify(geometry, ZRes(9)) as geometry, is_bridge, is_tunnel, is_intermittent -FROM ne_10m_rivers_lake_centerlines_gen_z8 - ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; -CREATE INDEX IF NOT EXISTS ne_10m_rivers_lake_centerlines_gen_z7_idx ON ne_10m_rivers_lake_centerlines_gen_z7 USING gist (geometry); + 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: ne_10m_rivers_lake_centerlines_gen_z7 -> ne_10m_rivers_lake_centerlines_gen_z6 -DROP MATERIALIZED VIEW IF EXISTS ne_10m_rivers_lake_centerlines_gen_z6 CASCADE; -CREATE MATERIALIZED VIEW ne_10m_rivers_lake_centerlines_gen_z6 AS -( -SELECT ST_Simplify(geometry, ZRes(8)) as 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, @@ -111,9 +124,10 @@ SELECT ST_Simplify(geometry, ZRes(8)) as geometry, is_bridge, is_tunnel, is_intermittent -FROM ne_10m_rivers_lake_centerlines_gen_z7 - ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; -CREATE INDEX IF NOT EXISTS ne_10m_rivers_lake_centerlines_gen_z6_idx ON ne_10m_rivers_lake_centerlines_gen_z6 USING gist (geometry); + 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 @@ -161,7 +175,7 @@ SELECT geometry, FROM ne_50m_rivers_lake_centerlines_gen_z5 ); --- etldoc: ne_10m_rivers_lake_centerlines_gen_z6 -> waterway_z6 +-- etldoc: waterway_relation_gen_z6 -> waterway_z6 CREATE OR REPLACE VIEW waterway_z6 AS ( SELECT geometry, @@ -173,10 +187,10 @@ SELECT geometry, is_bridge, is_tunnel, is_intermittent -FROM ne_10m_rivers_lake_centerlines_gen_z6 +FROM waterway_relation_gen_z6 ); --- etldoc: ne_10m_rivers_lake_centerlines_gen_z7 -> waterway_z7 +-- etldoc: waterway_relation_gen_z7 -> waterway_z7 CREATE OR REPLACE VIEW waterway_z7 AS ( SELECT geometry, @@ -188,10 +202,10 @@ SELECT geometry, is_bridge, is_tunnel, is_intermittent -FROM ne_10m_rivers_lake_centerlines_gen_z7 +FROM waterway_relation_gen_z7 ); - -- etldoc: ne_10m_rivers_lake_centerlines_gen_z8 -> waterway_z8 +-- etldoc: waterway_relation_gen_z8 -> waterway_z8 CREATE OR REPLACE VIEW waterway_z8 AS ( SELECT geometry, @@ -203,7 +217,7 @@ SELECT geometry, is_bridge, is_tunnel, is_intermittent -FROM ne_10m_rivers_lake_centerlines_gen_z8 +FROM waterway_relation_gen_z8 ); -- etldoc: osm_important_waterway_linestring_gen_z9 -> waterway_z9 @@ -318,8 +332,8 @@ $$ SELECT geometry, class, NULLIF(name, '') AS name, - COALESCE(NULLIF(name_en, ''), name) AS name_en, - COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + 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 diff --git a/layers/waterway/waterway.yaml b/layers/waterway/waterway.yaml index b07b986..53ec3bc 100644 --- a/layers/waterway/waterway.yaml +++ b/layers/waterway/waterway.yaml @@ -1,7 +1,12 @@ layer: id: "waterway" # waterway relies on the function waterway_brunnel() defined in water layer - requires: "water" + 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). diff --git a/openmaptiles.yaml b/openmaptiles.yaml index 26b59c4..e3e8e4b 100644 --- a/openmaptiles.yaml +++ b/openmaptiles.yaml @@ -19,7 +19,7 @@ tileset: - layers/landmarks/landmark.yaml - layers/aerodrome_label/aerodrome_label.yaml name: OpenMapTiles - version: 3.12.1 + version: 3.14.0 id: openmaptiles description: "A tileset showcasing all layers in OpenMapTiles. https://openmaptiles.org" attribution: '© OpenMapTiles © OpenStreetMap contributors' @@ -34,6 +34,7 @@ tileset: - az # Azerbaijani, Latin - be # Belorussian - bg # Bulgarian + - bn # Bengali - br # Breton, Latin - bs # Bosnian, Latin - ca # Catalan, Latin @@ -48,6 +49,7 @@ tileset: - es # Spanish, Latin - et # Estonian, Latin - eu # Basque, Latin + - fa # Persian - fi # Finnish, Latin - fr # French, Latin - fy # Western Frisian, Latin @@ -82,6 +84,8 @@ tileset: - nl # Dutch, Latin - "no" # Norwegian, Latin - oc # Occitan (post 1500), Latin + - pa # Punjabi + - pnb # Western Punjabi - pl # Polish, Latin - pt # Portuguese, Latin - rm # Romansh, Latin @@ -98,7 +102,11 @@ tileset: - th # Thai - tr # Turkish, Latin - uk # Ukrainian + - ur # Urdu + - vi # Vietnamese, Latin - zh # Chinese + - zh-Hant # Traditional Chinese + - zh-Hans # Simplified Chinese defaults: 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: diff --git a/quickstart.sh b/quickstart.sh index 192cac2..79df14f 100755 --- a/quickstart.sh +++ b/quickstart.sh @@ -70,18 +70,33 @@ echo "========================================================================== echo " Docker check & Download images " echo "-------------------------------------------------------------------------------------" echo "====> : Please check the Docker and docker-compose version!" -echo " : We are using docker-compose v2 file format! see more at https://docs.docker.com/" +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 --version +docker_compose_command --version # based on: http://stackoverflow.com/questions/16989598/bash-comparing-version-numbers -function version { echo "$@" | tr -cs '0-9.' '.' | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'; } +function version { echo "$@" | tr -d 'v' | tr -cs '0-9.' '.' | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'; } -COMPOSE_VER=$(docker-compose version --short) +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 @@ -116,7 +131,7 @@ echo " : Started : $STARTDATE " echo " : Your bash version: $BASH_VERSION" echo " : Your OS : $OSTYPE" docker --version -docker-compose --version +docker_compose_command --version if [[ "$OSTYPE" == "linux-gnu" ]]; then echo " " @@ -130,9 +145,15 @@ if [[ "$OSTYPE" == "linux-gnu" ]]; then echo "ERR: Sorry this is working only on x86_64!" exit 1 fi + echo " : --- Memory, CPU info ---- " - mem=$( grep MemTotal /proc/meminfo | awk '{print $2}' | xargs -I {} echo "scale=4; {}/1024^2" | bc ) - echo "System memory (GB): ${mem}" + 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 @@ -187,7 +208,7 @@ if [[ "$USE_PRELOADED_IMAGE" == true ]]; then 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/lukasmartinelli/osm-lakelines" + 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" @@ -208,7 +229,7 @@ else 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/lukasmartinelli/osm-lakelines" + 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" @@ -223,19 +244,11 @@ 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/tree/master/docker/import-osm " +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 border ${area} data into PostgreSQL using osmborder" -echo " : Source code: https://github.com/pnorman/osmborder" -echo " : Data license: http://www.openstreetmap.org/copyright" -echo " : Thank you Paul Norman" -make import-borders - echo " " echo "-------------------------------------------------------------------------------------" echo "====> : Start importing Wikidata: Wikidata Query Service -> PostgreSQL" @@ -278,17 +291,10 @@ fi echo " " echo "-------------------------------------------------------------------------------------" -echo "====> : Start generating MBTiles (containing gzipped MVT PBF) from a TM2Source project. " -echo " : TM2Source project definitions : ./build/openmaptiles.tm2source/data.yml " +echo "====> : Start generating MBTiles (containing gzipped MVT PBF) using PostGIS. " echo " : Output MBTiles: ./data/${area}.mbtiles " -echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/generate-vectortiles " -echo " : We are using a lot of Mapbox Open Source tools! : https://github.com/mapbox " -echo " : Thank you https://www.mapbox.com !" -echo " : See other MVT tools : https://github.com/mapbox/awesome-vector-tiles " -echo " : " -echo " : You will see a lot of deprecated warning in the log! This is normal! " -echo " : like : Mapnik LOG> ... is deprecated and will be removed in Mapnik 4.x ... " -make generate-tiles +echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/blob/master/bin/generate-tiles " +make generate-tiles-pg echo " " 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/advertising_column.svg b/style/icons/advertising_column.svg new file mode 100644 index 0000000..506588e --- /dev/null +++ b/style/icons/advertising_column.svg @@ -0,0 +1,5 @@ + + + + + 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..787b22d --- /dev/null +++ b/style/icons/bar.svg @@ -0,0 +1,3 @@ + + + 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..1da8f39 --- /dev/null +++ b/style/icons/bench.svg @@ -0,0 +1,3 @@ + + + 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/chocolate.svg b/style/icons/chocolate.svg new file mode 100644 index 0000000..ab55f15 --- /dev/null +++ b/style/icons/chocolate.svg @@ -0,0 +1,60 @@ + + + + + + image/svg+xml + + + + + + + + + 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/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/frozen_food.svg b/style/icons/frozen_food.svg new file mode 100644 index 0000000..707fcdc --- /dev/null +++ b/style/icons/frozen_food.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + 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/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/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..1472463 --- /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..00f363e --- /dev/null +++ b/style/icons/restaurant.svg @@ -0,0 +1,3 @@ + + + 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/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/tattoo.svg b/style/icons/tattoo.svg new file mode 100644 index 0000000..d29e518 --- /dev/null +++ b/style/icons/tattoo.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + 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/waste_basket.svg b/style/icons/waste_basket.svg new file mode 100644 index 0000000..f5784d7 --- /dev/null +++ b/style/icons/waste_basket.svg @@ -0,0 +1,3 @@ + + + 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/watches.svg b/style/icons/watches.svg new file mode 100644 index 0000000..df654b4 --- /dev/null +++ b/style/icons/watches.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + 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..93de4f6 --- /dev/null +++ b/tests/import/600_import-poi.osm @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..373c557 --- /dev/null +++ b/tests/test-post-import.sql @@ -0,0 +1,215 @@ +-- 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; + +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..9695310 --- /dev/null +++ b/tests/test-post-update.sql @@ -0,0 +1,126 @@ +-- 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 ATM" 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; + +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..e03e31f --- /dev/null +++ b/tests/update/600_update-poi.osc @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + +