diff --git a/.env b/.env index 61fca597d..d664449c7 100644 --- a/.env +++ b/.env @@ -1,13 +1,54 @@ -POSTGRES_DB=openmaptiles -POSTGRES_USER=openmaptiles -POSTGRES_PASSWORD=openmaptiles -POSTGRES_HOST=postgres -POSTGRES_PORT=5432 -QUICKSTART_MIN_ZOOM=0 -QUICKSTART_MAX_ZOOM=7 -DIFF_MODE=false -TOOLS_VERSION=3.1.0 +# This file defines default environment variables for all images + +# Layers definition and meta data +TILESET_FILE=openmaptiles.yaml + +# 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 +PGUSER=openmaptiles +PGPASSWORD=openmaptiles +PGHOST=postgres +PGPORT=5432 +# BBOX may get overwritten by the computed bbox of the specific area: +# make generate-bbox-file +# By default, the Makefile will use the content of data/$(area).bbox file if it exists. BBOX=-180.0,-85.0511,180.0,85.0511 + +# Which zooms to generate with make generate-tiles-pg MIN_ZOOM=0 -MAX_ZOOM=14 +MAX_ZOOM=7 + +# `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 + +# 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 + +# The current setup assumes this file is placed inside the data/ dir +MBTILES_FILE=tiles.mbtiles +# This is the current repl_config.json location, pre-configured in the tools Dockerfile +# Makefile and quickstart replace it with the dynamically generated one, but we keep it here in case some other method is used to run. +IMPOSM_CONFIG_FILE=/usr/src/app/config/repl_config.json + +# import-borders temp files - set them here to defaults, and override in the Makefile based on the area +BORDERS_CLEANUP_FILE=data/borders/cleanup.pbf +BORDERS_PBF_FILE=data/borders/filtered.pbf +BORDERS_CSV_FILE=data/borders/lines.csv + +# Number of parallel processes to use when importing sql files +MAX_PARALLEL_PSQL=5 + +# Number of parallel threads to use when generating vector map tiles +COPY_CONCURRENCY=10 + +# Variables for generate tiles using tilelive-pgquery +PGHOSTS_LIST= diff --git a/.github/workflows/build-test-data.sh b/.github/workflows/build-test-data.sh new file mode 100755 index 000000000..168e20468 --- /dev/null +++ b/.github/workflows/build-test-data.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +set -euo pipefail + +# +# Download several areas, combine them into a single file, and print --bbox params needed to run test-perf +# + +# List of Geofabrik areas +TEST_AREAS=(equatorial-guinea liechtenstein district-of-columbia greater-london) + +: "${DATA_DIR:=/tileset/data/test}" +: "${DATA_FILE_SUFFIX:=-latest.osm.pbf}" +: "${RESULT_FILE:=test${DATA_FILE_SUFFIX}}" + +mkdir -p "$DATA_DIR" +cd "$DATA_DIR" + + +echo -e $"\n=========== downloading areas" "${TEST_AREAS[@]}" "===========================" +for area in "${TEST_AREAS[@]}"; do + file="${area}${DATA_FILE_SUFFIX}" + if [ -f "$file" ]; then + echo "File $file already exists, skipping download" + else + download-osm geofabrik "${area}" -- -d "$DATA_DIR" + if [ ! -f "$file" ]; then + echo "Unexpected error while downloading $file, aborting" + exit 1 + fi + fi +done + + +echo -e $"\n=========== Merging" "${TEST_AREAS[@]}" "into ${RESULT_FILE} =====" +rm -f "${RESULT_FILE}" +OSMOSIS_ARG="--read-pbf ${TEST_AREAS[0]}${DATA_FILE_SUFFIX} $(printf " --read-pbf %s${DATA_FILE_SUFFIX} --merge" "${TEST_AREAS[@]:1}")" +# shellcheck disable=SC2086 +( set -x; osmosis ${OSMOSIS_ARG} --write-pbf "${RESULT_FILE}" ) + + +echo -e $"\n=========== Computing test BBOXes =======================" +echo -e $"\n File ${RESULT_FILE} ($(du -b "$RESULT_FILE" | cut -f1)) has been generated with these test areas:\n" +for area in "${TEST_AREAS[@]}"; do + file="${area}${DATA_FILE_SUFFIX}" + STATS=$(osmconvert --out-statistics "$file" ) + LON_MIN=$( echo "$STATS" | grep "lon min:" | cut -d":" -f 2 | awk '{gsub(/^ +| +$/,"")} {print $0}' ) + LON_MAX=$( echo "$STATS" | grep "lon max:" | cut -d":" -f 2 | awk '{gsub(/^ +| +$/,"")} {print $0}' ) + LAT_MIN=$( echo "$STATS" | grep "lat min:" | cut -d":" -f 2 | awk '{gsub(/^ +| +$/,"")} {print $0}' ) + LAT_MAX=$( echo "$STATS" | grep "lat max:" | cut -d":" -f 2 | awk '{gsub(/^ +| +$/,"")} {print $0}' ) + BBOX="${LON_MIN},${LAT_MIN},${LON_MAX},${LAT_MAX}" + FILE_SIZE="$(du -b "$file" | cut -f1)" + + cat <> .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/performance.yml b/.github/workflows/performance.yml new file mode 100644 index 000000000..3266dbda5 --- /dev/null +++ b/.github/workflows/performance.yml @@ -0,0 +1,300 @@ +# Workflow to run performance tests OMT`s new Pull Requests and commits pushed into OMT repo + +name: OpenMapTiles Performance CI + +on: + push: + branches: [ master, master-tools ] + pull_request: + +jobs: + performance: + name: Evaluate performance + runs-on: self-hosted + env: + ## Smaller tests (runs everything in about 30 minutes) + ## Two test areas: equatorial-guinea and liechtenstein + #TEST_DATA_URL: "https://drive.google.com/uc?export=download&id=12vw07f9W0MiAHIqMztRiIMwahJfqTi21" + #TEST_PERF_PARAMS: "--minzoom 0 --maxzoom 14 --bbox 5.4172943,-1.6732196,12.3733400,4.3475256 --bbox 9.0900979,46.9688169,9.6717077,47.5258072" + + # Large test, size is 79,472,850 + # --bbox 5.4172943,-1.6732196,12.3733400,4.3475256 `# equatorial-guinea 87,768 tiles at z14, 2.1MB, 24.9 bytes/tile [8438/7993]x[8755/8268]` \ + # --bbox 9.0900979,46.9688169,9.6717077,47.5258072 `# liechtenstein 1,064 tiles at z14, 2.2MB, 2,217.0 bytes/tile [8605/5727]x[8632/5764]` \ + # --bbox -78.7749754,38.7820235,-76.8957735,39.6985009 `# district-of-columbia 4,785 tiles at z14, 16.0MB, 3,508.9 bytes/tile [4606/6220]x[4692/6274]` \ + # --bbox -0.6124681,51.2268449,0.3996690,51.7873570 `# greater-london 1,974 tiles at z14, 55.5MB, 29,458.3 bytes/tile [8164/5427]x[8210/5468]` \ + TEST_DATA_URL: "https://drive.google.com/uc?export=download&id=18nP3f06aBBiEKhUNmAkqq30gqQnU2_VJ" + TEST_PERF_PARAMS: >- + --minzoom 0 --maxzoom 14 + --bbox 5.4172943,-1.6732196,12.3733400,4.3475256 + --bbox 9.0900979,46.9688169,9.6717077,47.5258072 + --bbox -78.7749754,38.7820235,-76.8957735,39.6985009 + --bbox -0.6124681,51.2268449,0.3996690,51.7873570 + + ## Large test data -- we should switch to it after everything is working ok + # 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@v2 + with: + path: ci_cache + key: "v2-${{ env.TEST_DATA_URL }}" + + - name: Download test data on cache miss + if: steps.cache-testdata.outputs.cache-hit != 'true' + run: | + echo "Data file does not exist, downloading $TEST_DATA_URL" + mkdir -p ci_cache + curl --silent --show-error --location --output ci_cache/perf-test-areas-latest.osm.pbf "$TEST_DATA_URL" + + - name: Get code + uses: actions/checkout@v2 + with: + # Fetch the last two commits in case this is a PR, + # and we need to profile the base branch first + fetch-depth: 2 + path: code + + - name: Compute git revision hash to cache + id: calc + run: | + # If this is a pull request, we should cache the parent (base) revision + # Otherwise cache the current one + cd code + REV_HASH=$(git log -1 --format="%H") + if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then + # Take the first parent of the grafted commit (cannot use HEAD^1 with shallow clones) + REV_HASH=$(git cat-file -p $REV_HASH | awk 'NR > 1 {if(/^parent/){print $2; exit}}') + fi + echo "::set-output name=hash::$REV_HASH" + + - name: Set up caching for the performance results + uses: actions/cache@v2 + with: + path: perf_cache + # If profiling result cache has incompatible format, increase this "v" number + key: "v13-${{ steps.calc.outputs.hash }}-${{ env.TEST_DATA_URL }}" + + - name: Load test data into DB and run performance test + id: main + env: + CACHE_SHA: "${{ steps.calc.outputs.hash }}" + run: | + profile() { + TIME_FORMAT="# %C + P\t%P\tPercentage of the CPU that this job got. This is just user + system times divided by the total running time. It also prints a percentage sign. + x\t%x\tExit status of the command. + -\t-----\t-- Time -------------------------------------------------------------------------------------------------- + e\t%e\tElapsed real (wall clock) time used by the process, in seconds. + E\t%E\tElapsed real (wall clock) time used by the process, in [hours:]minutes:seconds. + U\t%U\tTotal number of CPU-seconds that the process used directly (in user mode), in seconds. + S\t%S\tTotal number of CPU-seconds used by the system on behalf of the process (in kernel mode), in seconds. + -\t-----\t-- Context Switches -------------------------------------------------------------------------------------- + c\t%c\tNumber of times the process was context-switched involuntarily (because the time slice expired). + w\t%w\tNumber of times that the program was context-switched voluntarily, for instance while waiting for an I/O operation to complete. + -\t-----\t-- Page faults ------------------------------------------------------------------------------------------- + F\t%F\tNumber of major, or I/O-requiring, page faults that occurred while the process was running. These are faults where the page has actually migrated out of primary memory. + R\t%R\tNumber of minor, or recoverable, page faults. These are pages that are not valid (so they fault) but which have not yet been claimed by other virtual pages. Thus the data in the page is still valid but the system tables must be updated. + W\t%W\tNumber of times the process was swapped out of main memory. + -\t-----\t-- Memory ------------------------------------------------------------------------------------------------ + K\t%K\tAverage total (data+stack+text) memory use of the process, in Kilobytes. + t\t%t\tAverage resident set size of the process, in Kilobytes. + p\t%p\tAverage unshared stack size of the process, in Kilobytes. + M\t%M\tMaximum resident set size of the process during its lifetime, in Kilobytes. + D\t%D\tAverage size of the process's unshared data area, in Kilobytes. + X\t%X\tAverage amount of shared text in the process, in Kilobytes. + Z\t%Z\tSystem's page size, in bytes. This is a per-system constant, but varies between systems. + -\t-----\t-- Other ------------------------------------------------------------------------------------------------- + I\t%I\tNumber of file system inputs by the process. + O\t%O\tNumber of file system outputs by the process. + k\t%k\tNumber of signals delivered to the process. + r\t%r\tNumber of socket messages received by the process. + s\t%s\tNumber of socket messages sent by the process. + " + + # reset system for a more predictable results (hopefully) + sudo -- bash -c " + set -euo pipefail + echo 'Hardware reset $1 ...' + # Run the TRIM command (for SSDs) + /sbin/fstrim --all + # Run sync to minimize the number of dirty objects on the system + /bin/sync + # Give sync a little bit of time to finish. See https://linux.die.net/man/8/sync + sleep 5 + # Free slab objects and pagecache + echo 3 > /proc/sys/vm/drop_caches + # Request compaction to reduce memory fragmentation + echo 1 > /proc/sys/vm/compact_memory + echo 'Hardware reset $1 done' + " + + # Must use full path to get the full-featured version of time + # profile-*.tsv filenames are parsed using ${file:8:-4} below + /usr/bin/time --format "$TIME_FORMAT" --output "${PROFILE_DIR}/profile-${1}.tsv" "${@:2}" + } + + create_db() { + make clean + make init-dirs + cp ../ci_cache/perf-test-areas-latest.osm.pbf data/perf-test-areas.osm.pbf + make destroy-db + make all + make start-db + profile 1_data make import-data + profile 2_osm make import-osm + + if [ -f ../ci_cache/wikidata-cache.json ]; then + cp ../ci_cache/wikidata-cache.json cache/wikidata-cache.json + fi + 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 + 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 + cd code + + CURRENT_SHA=$(git log -1 --format="%H") + + if [ ! -f ../perf_cache/results.json ]; then + echo "We do not have cached performance results, create them..." + if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then + git reset --hard ${CURRENT_SHA}^1 + fi + + docker-compose pull + PROFILE_DIR=../perf_cache + create_db + if [ ! -f ../ci_cache/wikidata-cache.json ]; then + cp cache/wikidata-cache.json ../ci_cache/wikidata-cache.json + fi + + (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 + # For pull requests, restore to the PR version before continuing + git reset --hard ${CURRENT_SHA} + fi + else + 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. + for file in *.tsv; do + cp "$file" "../artifacts/base-$file" + done + else + cp results.json ../artifacts/results.json + # Copy all tsv files, not just the ones with "profile-" prefix. + cp *.tsv ../artifacts + fi + popd + + if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then + echo "Comparing pull request results with the base..." + + 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 \ + --compare /tileset/results.json --record /tileset/pr-results.json)" + rm results.json + mv pr-results.json ../artifacts/ + + # Split into two parts -- before and after the ===== SUMMARY ===== + OUT_SUMMARY="${OUTPUT##*========}" + OUT_DETAILS="${OUTPUT%%========*}" + + pushd ../artifacts + PROFILING_MSG="step | total time | change | user time | change + --- | --- | --- | --- | --- + " + for file in profile*.tsv; do + new_total_time_fmt=$(grep -E '^E' "$file" | cut -d$'\t' -f 2) + old_total_time=$(grep -E '^e' "base-$file" | cut -d$'\t' -f 2) + new_total_time=$(grep -E '^e' "$file" | cut -d$'\t' -f 2) + old_user_time=$(grep -E '^U' "base-$file" | cut -d$'\t' -f 2) + new_user_time=$(grep -E '^U' "$file" | cut -d$'\t' -f 2) + + PROFILING_MSG="${PROFILING_MSG}${file:8:-4} \ + | ${new_total_time_fmt} | $( echo "$old_total_time $new_total_time" | awk '{ if ($1 == 0) print "-"; else printf "%.1f%%", ($2-$1)*100/$1 }' ) \ + | $( echo "$old_user_time $new_user_time" | awk '{ if ($1 == 0) print "%s | -", $2 ; else printf "%s | %.1f%%", $2, ($2-$1)*100/$1 }' ) \ + "$'\n' + done + + DB_SIZE_CHANGE=$( echo "$(cat base-db_size.tsv) $(cat db_size.tsv)" | \ + awk '{ printf "PostgreSQL DB size in MB: %s ⇒ %s", $1, $2; if ($1 != 0) printf " (%.1f%% change)", ($2-$1)*100/$1 }' ) + + popd + + cat > ../pr_message/message.md < + expand for details... + + \`\`\` + $OUT_DETAILS + \`\`\` + + + EOF + + fi + + - name: Save performance artifacts + 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@v2 + with: + name: pr_message + path: pr_message diff --git a/.github/workflows/pr-updater.yml b/.github/workflows/pr-updater.yml new file mode 100644 index 000000000..c116400a3 --- /dev/null +++ b/.github/workflows/pr-updater.yml @@ -0,0 +1,246 @@ +name: Update PR comments + +on: + 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 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. + # Set to 10-20 minutes higher than cron's job frequency set above. + IGNORE_RUNS_OLDER_THAN: 80 + # How far back to look for updated pull requests, in minutes. + # Should be bigger than IGNORE_RUNS_OLDER_THAN by the maximum time a pull request jobs may take + IGNORE_PRS_OLDER_THAN: 80 + run: | + # + # Strategy: + # * get all recently updated pull requests + # * get all recent workflow runs + # * match pull requests and their current SHA with the last workflow run for the same SHA + # * for each found match of and : + # * download artifact from the workflow run -- expects a single file with markdown content + # * look through existing PR comments to see if we have posted a comment before + # (uses a hidden magical header to identify our comment) + # * either create or update the comment with the new text (if changed) + # + + export GITHUB_API="https://api.github.com/repos/$GITHUB_REPOSITORY" + export COMMENT_MAGIC_HEADER='" + + # A useful wrapper around CURL + crl() { + curl --silent --show-error --location --retry 1 "${@:2}" \ + -H "Accept: application/vnd.github.antiope-preview+json, application/vnd.github.v3+json" \ + "$1" + } + + auth_crl() { + crl "$1" -H "authorization: Bearer $GITHUB_TOKEN" "${@:2}" + } + + # + # Parse current pull requests + # + + # Get all pull requests, most recently updated first + # (this way we don't need to page through all of them) + # Filter out PRs that are older than $IGNORE_PRS_OLDER_THAN minutes + # Result is an object, mapping a "key" to the pull request number: + # { + # "nyurik/openmaptiles/nyurik-patch-1/4953dd2370b9988a7832d090b5e47b3cd867f594": 6, + # ... + # } + PULL_REQUESTS_RAW="$( crl "$GITHUB_API/pulls?sort=updated&direction=desc" )" + if ! PULL_REQUESTS="$(jq --arg IGNORE_PRS_OLDER_THAN "$IGNORE_PRS_OLDER_THAN" ' + map( + # Only select unlocked pull requests updated within last $IGNORE_PRS_OLDER_THAN minutes + select(.locked==false + and (now - (.updated_at|fromdate)) / 60 < ($IGNORE_PRS_OLDER_THAN | tonumber)) + # Prepare for "from_entries" by creating a key/value object + # The key is a combination of repository name, branch name, and latest SHA + | { key: (.head.repo.full_name + "/" + .head.ref + "/" + .head.sha), value: .number } + ) + | from_entries + ' <( echo "$PULL_REQUESTS_RAW" ) )"; then + + echo "Error parsing pull requests" + echo "$PULL_REQUESTS_RAW" + exit 1 + fi + + # Count how many pull requests we should process, and exit early if there are none + PR_COUNT="$(jq 'length' <( echo "$PULL_REQUESTS" ) )" + if [ "$PR_COUNT" -eq 0 ]; then + echo "There are no pull requests updated in the last $IGNORE_PRS_OLDER_THAN minutes. Exiting." + exit + else + echo "$PR_COUNT pull requests have been updated in the last $IGNORE_PRS_OLDER_THAN minutes" + echo "$PULL_REQUESTS" | jq -r 'keys|.[]|" * " + .' + fi + + + + # + # Resolve workflow name into workflow ID + # + WORKFLOW_ID="$(crl "$GITHUB_API/actions/workflows" \ + | jq --arg WORKFLOW_NAME "$WORKFLOW_NAME" ' + .workflows[] | select(.name == $WORKFLOW_NAME) | .id + ')" + if [ -z "$WORKFLOW_ID" ]; then + echo "Unable to find workflow '$WORKFLOW_NAME' in $GITHUB_REPOSITORY" + exit 1 + else + echo "Resolved workflow '$WORKFLOW_NAME' to ID $WORKFLOW_ID" + fi + + + + # + # Match pull requests with the workflow runs + # + + # Get all workflow runs that were triggered by pull requests + WORKFLOW_PR_RUNS="$(crl "$GITHUB_API/actions/workflows/${WORKFLOW_ID}/runs?event=pull_request")" + + # For each workflow run, match it with the pull request to get the PR number + # A match is based on "source repository + branch + SHA" key + # In rare cases (e.g. force push to an older revision), there could be more than one match + # for a given PR number, so just use the most recent one. + # Result is a table (list of lists) - each row with PR number, JOB ID, and the above key + PR_JOB_MAP="$(jq --arg IGNORE_RUNS_OLDER_THAN "$IGNORE_RUNS_OLDER_THAN" ' + # second input is the pull request map - use it to lookup PR numbers + input as $PULL_REQUESTS + | .workflow_runs + | map( + # Create a new object with the relevant values + { + id, + updated_at, + # create lookup key based on source repository + branch + SHA + key: (.head_repository.full_name + "/" + .head_branch + "/" + .head_sha), + # was this a successful run? + # do not include .conclusion=="success" because errors could also post messages + success: (.status=="completed") + } + # lookup PR number from $PULL_REQUESTS using the above key + | . += { pr_number: $PULL_REQUESTS[.key] } + # Remove runs that were not in the list of the PRs + | select(.pr_number) + ) + # Keep just the most recent run per pull request + | group_by(.pr_number) + | map( + sort_by(.updated_at) + | last + # If the most recent run did not succeed, or if the run is too old, ignore it + | select(.success and (now - (.updated_at|fromdate)) / 60 < ($IGNORE_RUNS_OLDER_THAN | tonumber)) + # Keep just the pull request number mapping to run ID + | { pr_number, id, key } + ) + ' <( echo "$WORKFLOW_PR_RUNS" ) <( echo "$PULL_REQUESTS" ) )" + + + # Count how many jobs we should process, and exit early if there are none + JOBS_COUNT="$(jq 'length' <( echo "$PR_JOB_MAP" ) )" + if [ "$JOBS_COUNT" -eq 0 ]; then + echo "There are no recent workflow job runs in the last $IGNORE_RUNS_OLDER_THAN minutes. Exiting." + exit + else + echo "$JOBS_COUNT '$WORKFLOW_NAME' jobs have been updated in the last $IGNORE_RUNS_OLDER_THAN minutes" + echo "$PR_JOB_MAP" | jq -r '.[] | " * PR #\(.pr_number) Job #\(.id) -- \(.key) "' + fi + + # + # Iterate over the found pairs of PR number + run ID, and update them all + # + echo "$PR_JOB_MAP" | jq -r '.[] | [ .pr_number, .id, .key ] | @sh' | \ + while read -r PR_NUMBER RUN_ID RUN_KEY; do + + echo "Processing '$WORKFLOW_NAME' run #$RUN_ID for pull request #$PR_NUMBER $RUN_KEY..." + ARTIFACTS="$(crl "$GITHUB_API/actions/runs/$RUN_ID/artifacts")" + + # Find the artifact download URL for the artifact with the expected name + ARTIFACT_URL="$(jq -r --arg MSG_ARTIFACT_NAME "$MSG_ARTIFACT_NAME" ' + .artifacts + | map(select(.name == $MSG_ARTIFACT_NAME and .expired == false)) + | first + | .archive_download_url + | select(.!=null) + ' <( echo "$ARTIFACTS" ) )" + + if [ -z "$ARTIFACT_URL" ]; then + echo "Unable to find an artifact named '$MSG_ARTIFACT_NAME' in workflow $RUN_ID (PR #$PR_NUMBER), skipping..." + continue + fi + + echo "Downloading artifact $ARTIFACT_URL (assuming single text file per artifact)..." + if ! MESSAGE="$(auth_crl "$ARTIFACT_URL" | gunzip)"; then + echo "Unable to download or parse message from artifact '$MSG_ARTIFACT_NAME' in workflow $RUN_ID (PR #$PR_NUMBER), skipping..." + continue + fi + if [ -z "$MESSAGE" ]; then + echo "Empty message in artifact '$MSG_ARTIFACT_NAME' in workflow $RUN_ID (PR #$PR_NUMBER), skipping..." + continue + fi + + # Create a message body by appending a magic header + # and stripping any starting and ending whitespace from the original message + MESSAGE_BODY="$(jq -n \ + --arg COMMENT_MAGIC_HEADER "$COMMENT_MAGIC_HEADER" \ + --arg MESSAGE "$MESSAGE" \ + '{ body: ($COMMENT_MAGIC_HEADER + "\n" + ($MESSAGE | sub( "^[\\s\\p{Cc}]+"; "" ) | sub( "[\\s\\p{Cc}]+$"; "" ))) }' \ + )" + + EXISTING_PR_COMMENTS="$(crl "$GITHUB_API/issues/$PR_NUMBER/comments")" + + # Get the comment URL for the first comment that begins with the magic header, or empty string + OLD_COMMENT="$(jq --arg COMMENT_MAGIC_HEADER "$COMMENT_MAGIC_HEADER" ' + map(select(.body | startswith($COMMENT_MAGIC_HEADER))) + | first + | select(.!=null) + ' <( echo "$EXISTING_PR_COMMENTS" ) )" + + if [ -z "$OLD_COMMENT" ]; then + COMMENT_HTML_URL="$(auth_crl "$GITHUB_API/issues/$PR_NUMBER/comments" \ + -X POST \ + -H "Content-Type: application/json" \ + --data "$MESSAGE_BODY" \ + | jq -r '.html_url' )" + COMMENT_INFO="New comment $COMMENT_HTML_URL was created" + else + # Make sure the content of the message has changed + COMMENT_URL="$(jq -r ' + (input | .body) as $body + | select(.body | . != $body) + | .url + ' <( echo "$OLD_COMMENT" ) <( echo "$MESSAGE_BODY" ) )" + + if [ -z "$COMMENT_URL" ]; then + echo "The message has already been posted from artifact '$MSG_ARTIFACT_NAME' in workflow $RUN_ID (PR #$PR_NUMBER), skipping..." + continue + fi + + COMMENT_HTML_URL="$(auth_crl "$COMMENT_URL" \ + -X PATCH \ + -H "Content-Type: application/json" \ + --data "$MESSAGE_BODY" \ + | jq -r '.html_url' )" + + COMMENT_INFO="Existing comment $COMMENT_HTML_URL was updated" + fi + + echo "$COMMENT_INFO from workflow $WORKFLOW_NAME #$RUN_ID" + done diff --git a/.github/workflows/sql-tests.yml b/.github/workflows/sql-tests.yml new file mode 100644 index 000000000..b301f055c --- /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/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..9eb8a73f2 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,316 @@ +# Workflow to validate OMT`s new Pull Requests and commits pushed into OMT repo + +name: OpenMapTiles 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 + 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 + env: + ## Smaller tests (runs everything in about 30 minutes) + ## Two test areas: equatorial-guinea and liechtenstein + #TEST_DATA_URL: "https://drive.google.com/uc?export=download&id=12vw07f9W0MiAHIqMztRiIMwahJfqTi21" + #TEST_PERF_PARAMS: "--minzoom 0 --maxzoom 14 --bbox 5.4172943,-1.6732196,12.3733400,4.3475256 --bbox 9.0900979,46.9688169,9.6717077,47.5258072" + + # Large test, size is 79,472,850 + # --bbox 5.4172943,-1.6732196,12.3733400,4.3475256 `# equatorial-guinea 87,768 tiles at z14, 2.1MB, 24.9 bytes/tile [8438/7993]x[8755/8268]` \ + # --bbox 9.0900979,46.9688169,9.6717077,47.5258072 `# liechtenstein 1,064 tiles at z14, 2.2MB, 2,217.0 bytes/tile [8605/5727]x[8632/5764]` \ + # --bbox -78.7749754,38.7820235,-76.8957735,39.6985009 `# district-of-columbia 4,785 tiles at z14, 16.0MB, 3,508.9 bytes/tile [4606/6220]x[4692/6274]` \ + # --bbox -0.6124681,51.2268449,0.3996690,51.7873570 `# greater-london 1,974 tiles at z14, 55.5MB, 29,458.3 bytes/tile [8164/5427]x[8210/5468]` \ + TEST_DATA_URL: "https://drive.google.com/uc?export=download&id=18nP3f06aBBiEKhUNmAkqq30gqQnU2_VJ" + TEST_PERF_PARAMS: >- + --minzoom 0 --maxzoom 14 + --bbox 5.4172943,-1.6732196,12.3733400,4.3475256 + --bbox 9.0900979,46.9688169,9.6717077,47.5258072 + --bbox -78.7749754,38.7820235,-76.8957735,39.6985009 + --bbox -0.6124681,51.2268449,0.3996690,51.7873570 + + ## Large test data -- we should switch to it after everything is working ok + # 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: Cache test data download + id: cache-testdata + uses: actions/cache@v1 + with: + path: ci_cache + key: "v2-${{ env.TEST_DATA_URL }}" + + - name: Download test data on cache miss + if: steps.cache-testdata.outputs.cache-hit != 'true' + run: | + echo "Data file does not exist, downloading $TEST_DATA_URL" + mkdir -p ci_cache + curl --silent --show-error --location --output ci_cache/perf-test-areas-latest.osm.pbf "$TEST_DATA_URL" + + - name: Get code + uses: actions/checkout@v2 + with: + # Fetch the last two commits in case this is a PR, + # and we need to profile the base branch first + fetch-depth: 2 + path: code + + - name: Compute git revision hash to cache + id: calc + run: | + # If this is a pull request, we should cache the parent (base) revision + # Otherwise cache the current one + cd code + REV_HASH=$(git log -1 --format="%H") + if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then + # Take the first parent of the grafted commit (cannot use HEAD^1 with shallow clones) + REV_HASH=$(git cat-file -p $REV_HASH | awk 'NR > 1 {if(/^parent/){print $2; exit}}') + fi + echo "::set-output name=hash::$REV_HASH" + + - name: Set up caching for the performance results + uses: actions/cache@v1 + 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 }}" + + - name: Load test data into DB and run performance test + id: main + env: + CACHE_SHA: "${{ steps.calc.outputs.hash }}" + run: | + profile() { + TIME_FORMAT="# %C + P\t%P\tPercentage of the CPU that this job got. This is just user + system times divided by the total running time. It also prints a percentage sign. + x\t%x\tExit status of the command. + -\t-----\t-- Time -------------------------------------------------------------------------------------------------- + e\t%e\tElapsed real (wall clock) time used by the process, in seconds. + E\t%E\tElapsed real (wall clock) time used by the process, in [hours:]minutes:seconds. + U\t%U\tTotal number of CPU-seconds that the process used directly (in user mode), in seconds. + S\t%S\tTotal number of CPU-seconds used by the system on behalf of the process (in kernel mode), in seconds. + -\t-----\t-- Context Switches -------------------------------------------------------------------------------------- + c\t%c\tNumber of times the process was context-switched involuntarily (because the time slice expired). + w\t%w\tNumber of times that the program was context-switched voluntarily, for instance while waiting for an I/O operation to complete. + -\t-----\t-- Page faults ------------------------------------------------------------------------------------------- + F\t%F\tNumber of major, or I/O-requiring, page faults that occurred while the process was running. These are faults where the page has actually migrated out of primary memory. + R\t%R\tNumber of minor, or recoverable, page faults. These are pages that are not valid (so they fault) but which have not yet been claimed by other virtual pages. Thus the data in the page is still valid but the system tables must be updated. + W\t%W\tNumber of times the process was swapped out of main memory. + -\t-----\t-- Memory ------------------------------------------------------------------------------------------------ + K\t%K\tAverage total (data+stack+text) memory use of the process, in Kilobytes. + t\t%t\tAverage resident set size of the process, in Kilobytes. + p\t%p\tAverage unshared stack size of the process, in Kilobytes. + M\t%M\tMaximum resident set size of the process during its lifetime, in Kilobytes. + D\t%D\tAverage size of the process's unshared data area, in Kilobytes. + X\t%X\tAverage amount of shared text in the process, in Kilobytes. + Z\t%Z\tSystem's page size, in bytes. This is a per-system constant, but varies between systems. + -\t-----\t-- Other ------------------------------------------------------------------------------------------------- + I\t%I\tNumber of file system inputs by the process. + O\t%O\tNumber of file system outputs by the process. + k\t%k\tNumber of signals delivered to the process. + r\t%r\tNumber of socket messages received by the process. + s\t%s\tNumber of socket messages sent by the process. + " + + # reset system for a more predictable results (hopefully) + sudo -- bash -c " + set -euo pipefail + echo 'Hardware reset $1 ...' + # Run the TRIM command (for SSDs) + /sbin/fstrim --all + # Run sync to minimize the number of dirty objects on the system + /bin/sync + # Give sync a little bit of time to finish. See https://linux.die.net/man/8/sync + sleep 5 + # Free slab objects and pagecache + echo 3 > /proc/sys/vm/drop_caches + # Request compaction to reduce memory fragmentation + echo 1 > /proc/sys/vm/compact_memory + echo 'Hardware reset $1 done' + " + + # Must use full path to get the full-featured version of time + # profile-*.tsv filenames are parsed using ${file:8:-4} below + /usr/bin/time --format "$TIME_FORMAT" --output "${PROFILE_DIR}/profile-${1}.tsv" "${@:2}" + } + + create_db() { + make clean + make init-dirs + cp ../ci_cache/perf-test-areas-latest.osm.pbf data/perf-test-areas.osm.pbf + make destroy-db + make all + 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 + + # 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 + 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" + } + + mkdir -p perf_cache + mkdir -p artifacts + mkdir -p pr_message + cd code + + CURRENT_SHA=$(git log -1 --format="%H") + + if [ ! -f ../perf_cache/results.json ]; then + echo "We do not have cached performance results, create them..." + if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then + git reset --hard ${CURRENT_SHA}^1 + fi + + PROFILE_DIR=../perf_cache + create_db + if [ ! -f ../ci_cache/wikidata-cache.json ]; then + cp cache/wikidata-cache.json ../ci_cache/wikidata-cache.json + fi + + (set -x; profile test-perf docker-compose run --rm -T openmaptiles-tools \ + test-perf openmaptiles.yaml $TEST_PERF_PARAMS \ + --record /tileset/results.json) + mv results.json ../perf_cache + + if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then + # For pull requests, restore to the PR version before continuing + git reset --hard ${CURRENT_SHA} + fi + else + echo "Found cached performance results" + fi + + pushd ../perf_cache + 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. + for file in *.tsv; do + cp "$file" "../artifacts/base-$file" + done + else + cp results.json ../artifacts/results.json + # Copy all tsv files, not just the ones with "profile-" prefix. + cp *.tsv ../artifacts + fi + popd + + if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then + echo "Comparing pull request results with the base..." + + PROFILE_DIR=../artifacts + create_db + + 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 \ + --compare /tileset/results.json --record /tileset/pr-results.json)" + rm results.json + mv pr-results.json ../artifacts/ + + # Split into two parts -- before and after the ===== SUMMARY ===== + OUT_SUMMARY="${OUTPUT##*========}" + OUT_DETAILS="${OUTPUT%%========*}" + + pushd ../artifacts + PROFILING_MSG="step | total time | change | user time | change + --- | --- | --- | --- | --- + " + for file in profile*.tsv; do + new_total_time_fmt=$(grep -E '^E' "$file" | cut -d$'\t' -f 2) + old_total_time=$(grep -E '^e' "base-$file" | cut -d$'\t' -f 2) + new_total_time=$(grep -E '^e' "$file" | cut -d$'\t' -f 2) + old_user_time=$(grep -E '^U' "base-$file" | cut -d$'\t' -f 2) + new_user_time=$(grep -E '^U' "$file" | cut -d$'\t' -f 2) + + PROFILING_MSG="${PROFILING_MSG}${file:8:-4} \ + | ${new_total_time_fmt} | $( echo "$old_total_time $new_total_time" | awk '{ if ($1 == 0) print "-"; else printf "%.1f%%", ($2-$1)*100/$1 }' ) \ + | $( echo "$old_user_time $new_user_time" | awk '{ if ($1 == 0) print "%s | -", $2 ; else printf "%s | %.1f%%", $2, ($2-$1)*100/$1 }' ) \ + "$'\n' + done + + DB_SIZE_CHANGE=$( echo "$(cat base-db_size.tsv) $(cat db_size.tsv)" | \ + awk '{ printf "PostgreSQL DB size in MB: %s ⇒ %s", $1, $2; if ($1 != 0) printf " (%.1f%% change)", ($2-$1)*100/$1 }' ) + + popd + + cat > ../pr_message/message.md < + expand for details... + + \`\`\` + $OUT_DETAILS + \`\`\` + + + EOF + + fi + + - name: Save performance artifacts + uses: actions/upload-artifact@v1 + with: + name: performance_results + path: artifacts + + - name: Save PR message artifact + if: github.event_name == 'pull_request' + uses: actions/upload-artifact@v1 + with: + name: pr_message + path: pr_message diff --git a/.gitignore b/.gitignore index 9696f5613..70f146272 100644 --- a/.gitignore +++ b/.gitignore @@ -11,12 +11,12 @@ quickstart.log # imput / output data data/* -# input wikidata -wikidata/* - # generated source files build/* +# Import cache +cache/* + # any IDE files .idea/ .vscode/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 789527d2e..000000000 --- a/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -sudo: required - -language: bash - -services: - - docker - -script: - - set -e - - TEST_MODE=yes make generate-devdoc - - ./quickstart.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9e6149280..3c4b31f42 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,16 +32,57 @@ Your pull request must: * Use clear commit messages. * Be possible to merge automatically. -When you modify rules of importing data in `mapping.yaml` or `*.sql`, please update also: +When you modify import data rules in `mapping.yaml` or `*.sql`, please update: 1. field description in `[layer].yaml` 2. comments starting with `#etldoc` -3. if needed, generate new `mapping_diagram.png` or `etl_diagram.png` using commands below: -``` -make mapping-graph-[layer] -make etl-graph-[layer] -``` +3. regenerate documentation graphs with `make generate-devdoc` 4. update layer description on https://openmaptiles.org/schema/ (https://github.com/openmaptiles/www.openmaptiles.org/tree/master/layers) 5. check if OMT styles are affected by the PR and if there is a need for style updates When you are making PR that adds new spatial features to OpenMapTiles schema, please make also PR for at least one of our GL styles to show it on the map. Visual check is crucial. + +# SQL unit testing + +It is recommended that you create a [unit test](TESTING.md) when modifying the behavior of the SQL layer. This will ensure that your changes are working as expected when importing or updating OSM data into an OpenMapTiles database. + +# Verifying that updates still work + +When testing a PR, you should also verify that the update process completes without an error. Please modify, if necessary, and run the script below. + +**Note:** + +The verification requires the script to append temporary changes to the `.env` file. Please restore the original version from git using `git checkout .env` or remove these changes before submitting a PR. + +``` +( +set -e + +cat >> .env << EOM + +# temporary changes for verifying that updates still work +# Ensure DIFF_MODE is active +DIFF_MODE=true +# Ensure all zoom levels are tested +MAX_ZOOM=14 +EOM + +# Set the test area to the appropriate geofabrik extract +export area=north-america/us/indiana + +# Build 1-month-old tiles +rm -fr data build cache +make destroy-db +make download-geofabrik area=$area +docker-compose run --rm --user=$(id -u):$(id -g) openmaptiles-tools sh -c "wget -nv -O data/$area.osm.pbf http://download.geofabrik.de/$area-$(date --date="$(date +%Y-%m-15) -1 month" +'%y%m01').osm.pbf" +./quickstart.sh $area +cat << EOM + +# Update with the changes since a month+ ago + +EOM +docker-compose run --rm --user=$(id -u):$(id -g) openmaptiles-tools sh -c "osmupdate --base-url=$(sed -n 's/ *\"replication_url\": //p' data/$area.repl.json) data/$area.osm.pbf data/changes.osc.gz" +make import-diff +make generate-tiles-pg +) < /dev/null +``` diff --git a/LICENSE.md b/LICENSE.md index 245afac2a..d48166223 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors. +Copyright (c) 2022, 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 cb50f1574..cf3594216 100644 --- a/Makefile +++ b/Makefile @@ -1,289 +1,688 @@ +# +# First section - common variable initialization +# + +# Ensure that errors don't hide inside pipes +SHELL = /bin/bash +.SHELLFLAGS = -o pipefail -c + +# Layers definition and meta data +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 -DC_OPTS?=--rm +# Containers run as the current user rather than root (so that created files are not root-owned) +DC_OPTS ?= --rm --user=$(shell id -u):$(shell id -g) + +# If set to a non-empty value, will use postgis-preloaded instead of postgis docker image +USE_PRELOADED_IMAGE ?= + +# Local port to use with postserve +PPORT ?= 8090 +export PPORT +# Local port to use with tileserver +TPORT ?= 8080 +export TPORT +STYLE_FILE := build/style/style.json +STYLE_HEADER_FILE := style/style-header.json + +# Allow a custom docker-compose project name +DC_PROJECT := $(or $(DC_PROJECT),$(shell (. .env; echo $${DC_PROJECT}))) +ifeq ($(DC_PROJECT),) + DC_PROJECT := $(notdir $(shell pwd)) + DOCKER_COMPOSE := docker-compose +else + DOCKER_COMPOSE := docker-compose --project-name $(DC_PROJECT) +endif -# container runs as the current user rather than root (so that created files are not root-owned) -DC_USER_OPTS?=$(DC_OPTS) -u $$(id -u $${USER}):$$(id -g $${USER}) +# Make some operations quieter (e.g. inside the test script) +ifeq ($(or $(QUIET),$(shell (. .env; echo $${QUIET})))),) + QUIET_FLAG := +else + QUIET_FLAG := --quiet +endif + +# Use `xargs --no-run-if-empty` flag, if supported +XARGS := xargs $(shell xargs --no-run-if-empty /dev/null && echo --no-run-if-empty) # If running in the test mode, compare files rather than copy them TEST_MODE?=no ifeq ($(TEST_MODE),yes) - COPY_TO_GIT=diff + # create images in ./build/devdoc and compare them to ./layers + GRAPH_PARAMS=./build/devdoc ./layers else - COPY_TO_GIT=cp + # update graphs in the ./layers dir + GRAPH_PARAMS=./layers +endif + +# Set OpenMapTiles 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 + + +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 +# If $(area) parameter is not set, and only one *.osm.pbf file is found in ./data, use it as $(area). +# Otherwise, all make targets requiring an area will show an error. +# Note: If no *.osm.pbf files are found, once the users call "make download area=..." +# they will not need to use an "area=" parameter again because there will be just a single file. +# + +# 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 ($(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)),) + define assert_area_is_given + @echo "" + @echo "ERROR: The 'area' parameter or environment variable have not been set, and there several 'area' options:" + @$(patsubst data/%.osm.pbf,echo " '%'";,$(data_files)) + @echo "" + @echo "To specify an area use:" + @echo " make $@ area=" + @echo "" + @exit 1 + endef + else + ifeq ($(word 1,$(data_files)),) + define assert_area_is_given + @echo "" + @echo "ERROR: The 'area' parameter (or env var) has not been set, and there are no data/*.osm.pbf files" + @echo "" + @echo "To specify an area use" + @echo " make $@ area=" + @echo "" + @echo "To download an area, use make download area=" + @echo "To list downloadable areas, use make list-geofabrik and/or make list-bbbike" + @exit 1 + @echo "" + endef + else + # Keep just the name of the data file, without the .osm.pbf extension + area := $(patsubst data/%.osm.pbf,%,$(data_files)) + # Rename area-latest.osm.pbf to area.osm.pbf + # TODO: This if statement could be removed in a few months once everyone is using the file without the `-latest`? + ifneq ($(area),$(area:-latest=)) + $(shell mv "data/$(area).osm.pbf" "data/$(area:-latest=).osm.pbf") + area := $(area:-latest=) + $(warning ATTENTION: File data/$(area)-latest.osm.pbf was renamed to $(area).osm.pbf.) + AREA_INFO := Detected area=$(area) based on finding a 'data/$(area)-latest.osm.pbf' file - renamed to '$(area).osm.pbf'. Use 'area' parameter or environment variable to override. + else + AREA_INFO := Detected area=$(area) based on finding a 'data/$(area).osm.pbf' file. Use 'area' parameter or environment variable to override. + endif + endif + endif endif +ifneq ($(AREA_INFO),) + define assert_area_is_given + @echo "$(AREA_INFO)" + endef +endif + +# If set, this file will be downloaded in download-osm and imported in the import-osm targets +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. +DOWNLOAD_AREA := $(or $(url), $(area)) + +# 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) + +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 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 + +# Load area-specific bbox file that gets generated by the download-osm --bbox +AREA_BBOX_FILE ?= data/$(area).bbox +ifneq (,$(wildcard $(AREA_BBOX_FILE))) + cat := $(if $(filter $(OS),Windows_NT),type,cat) + BBOX := $(shell $(cat) ${AREA_BBOX_FILE}) + export BBOX +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: build/openmaptiles.tm2source/data.yml build/mapping.yaml build/tileset.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 download-geofabrik-list # list actual geofabrik OSM extracts for download -> <> " - @echo " make list # list actual geofabrik OSM extracts for download -> <> " - @echo " ./quickstart.sh <> # example: ./quickstart.sh madagascar " - @echo " " - @echo "Hints for designers:" - @echo " make start-postserve # start Postserver + Maputnik Editor [ see localhost:8088 ] " - @echo " make start-tileserver # start klokantech/tileserver-gl [ see localhost:8080 ] " - @echo " " - @echo "Hints for developers:" - @echo " make # build source code" - @echo " make download-geofabrik area=albania # download OSM data from geofabrik, and create config file" - @echo " make psql # start PostgreSQL console" - @echo " make psql-list-tables # list all PostgreSQL tables" - @echo " make psql-vacuum-analyze # PostgreSQL: VACUUM ANALYZE" - @echo " make psql-analyze # PostgreSQL: ANALYZE" - @echo " make generate-qareports # generate reports [./build/qareports]" - @echo " make generate-devdoc # generate devdoc including graphs for all layers [./build/devdoc]" - @echo " make etl-graph # hint for generating a single etl graph" - @echo " make mapping-graph # hint for generating a single mapping graph" - @echo " make import-sql-dev # start import-sql /bin/bash terminal" - @echo " make import-osm-dev # start import-osm /bin/bash terminal (imposm3)" - @echo " make clean-docker # remove docker containers, PG data volume" - @echo " make forced-clean-sql # drop all PostgreSQL tables for clean environment" - @echo " make docker-unnecessary-clean # 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 pgclimb-list-views # list PostgreSQL public schema views" - @echo " make pgclimb-list-tables # list PostgreSQL public schema tables" - @echo " cat .env # list PG database and MIN_ZOOM and MAX_ZOOM information" - @echo " cat quickstart.log # backup of the last ./quickstart.sh" - @echo " make help # help about available commands" - @echo "==============================================================================" - -.PHONY: build -build: - mkdir -p build - -build/openmaptiles.tm2source/data.yml: build - mkdir -p build/openmaptiles.tm2source - docker-compose run $(DC_OPTS) openmaptiles-tools generate-tm2source openmaptiles.yaml --host="postgres" --port=5432 --database="openmaptiles" --user="openmaptiles" --password="openmaptiles" > build/openmaptiles.tm2source/data.yml - -build/mapping.yaml: build - docker-compose run $(DC_OPTS) openmaptiles-tools generate-imposm3 openmaptiles.yaml > build/mapping.yaml - -build/tileset.sql: build - docker-compose run $(DC_OPTS) openmaptiles-tools generate-sql openmaptiles.yaml > build/tileset.sql + @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 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 bash -c \ + 'generate-tm2source $(TILESET_FILE) > $@' +endif + +build/mapping.yaml: init-dirs +ifeq (,$(wildcard build/mapping.yaml)) + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools bash -c \ + 'generate-imposm3 $(TILESET_FILE) > $@' +endif + +.PHONY: build-sql +build-sql: init-dirs +ifeq (,$(wildcard build/sql/run_last.sql)) + @mkdir -p build/sql/parallel + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools bash -c \ + 'generate-sql $(TILESET_FILE) --dir ./build/sql \ + && generate-sqltomvt $(TILESET_FILE) \ + --key --gzip --postgis-ver 3.0.1 \ + --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: - rm -f build/openmaptiles.tm2source/data.yml && rm -f build/mapping.yaml && rm -f build/tileset.sql - -.PHONY: clean-docker -clean-docker: - docker-compose down -v --remove-orphans - docker-compose rm -fv - docker volume ls -q | grep openmaptiles | xargs -r docker volume rm || true - -.PHONY: db-start -db-start: - docker-compose up -d postgres +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 +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=^$(DOCKER_PROJECT)_" | $(XARGS) docker volume rm + rm -rf cache + mkdir cache + +.PHONY: start-db-nowait +start-db-nowait: init-dirs + @echo "Starting postgres docker compose target using $${POSTGIS_IMAGE:-default} image (no recreate if exists)" && \ + $(DOCKER_COMPOSE) up --no-recreate -d postgres + +.PHONY: start-db +start-db: start-db-nowait @echo "Wait for PostgreSQL to start..." - docker-compose run $(DC_OPTS) import-osm ./pgwait.sh - -.PHONY: download-geofabrik -download-geofabrik: - @echo =============== download-geofabrik ======================= - @echo Download area : $(area) - @echo [[ example: make download-geofabrik area=albania ]] - @echo [[ list areas: make download-geofabrik-list ]] - docker-compose run $(DC_OPTS) import-osm ./download-geofabrik.sh $(area) - ls -la ./data/$(area).* - @echo "Generated config file: ./data/docker-compose-config.yml" - @echo " " - cat ./data/docker-compose-config.yml - @echo " " + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools pgwait + +# Wrap start-db target but use the preloaded image +.PHONY: start-db-preloaded +start-db-preloaded: export POSTGIS_IMAGE=openmaptiles/postgis-preloaded +start-db-preloaded: export COMPOSE_HTTP_TIMEOUT=180 +start-db-preloaded: start-db + +.PHONY: stop-db +stop-db: + @echo "Stopping PostgreSQL..." + $(DOCKER_COMPOSE) stop postgres + +.PHONY: list-geofabrik +list-geofabrik: init-dirs + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools download-osm list geofabrik + +.PHONY: list-bbbike +list-bbbike: init-dirs + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools download-osm list bbbike + +# +# download, download-geofabrik, download-osmfr, and download-bbbike are handled here +# The --imposm-cfg will fail for some of the sources, but we ignore that error -- only needed for diff mode +# +OSM_SERVERS := geofabrik osmfr bbbike +ALL_DOWNLOADS := $(addprefix download-,$(OSM_SERVERS)) download +OSM_SERVER=$(patsubst download,,$(patsubst download-%,%,$@)) +.PHONY: $(ALL_DOWNLOADS) +$(ALL_DOWNLOADS): init-dirs + @$(assert_area_is_given) +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 ($(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)" \ + --bbox "$(AREA_BBOX_FILE)" \ + --output "$(PBF_FILE)" + else + @echo "Downloading $(DOWNLOAD_AREA) into $(PBF_FILE) from $(if $(OSM_SERVER),$(OSM_SERVER),any source)" + @$(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools download-osm $(OSM_SERVER) "$(DOWNLOAD_AREA)" \ + --bbox "$(AREA_BBOX_FILE)" \ + --output "$(PBF_FILE)" + endif + @echo "" +else + ifeq ($(DIFF_MODE),true) + ifeq (,$(wildcard $(IMPOSM_CONFIG_FILE))) + $(error \ + $(newline) Data files $(PBF_FILE) already exists, but $(IMPOSM_CONFIG_FILE) does not. \ + $(newline) You probably downloaded the data file before setting DIFF_MODE=true. \ + $(newline) You can delete the data file $(PBF_FILE) and re-run make download \ + $(newline) to re-download and generate config, or manually create $(IMPOSM_CONFIG_FILE) \ + $(newline) See example https://github.com/openmaptiles/openmaptiles-tools/blob/v5.2/bin/config/repl_config.json \ + $(newline)) + else + @echo "Data files $(PBF_FILE) and replication config $(IMPOSM_CONFIG_FILE) already exists, skipping the download." + endif + else + @echo "Data files $(PBF_FILE) already exists, skipping the download." + endif +endif + +.PHONY: generate-bbox-file +generate-bbox-file: + @$(assert_area_is_given) +ifeq (,$(wildcard $(AREA_BBOX_FILE))) + @$(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools download-osm bbox "$(PBF_FILE)" "$(AREA_BBOX_FILE)" +else + @echo "Configuration file $(AREA_BBOX_FILE) already exists, no need to regenerate. BBOX=$(BBOX)" +endif .PHONY: psql -psql: db-start - docker-compose run $(DC_OPTS) import-osm ./psql.sh +psql: start-db-nowait + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools sh -c 'pgwait && psql.sh' + +# 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 := $(filter-out --user=%,$(DC_OPTS)) +else + DC_OPTS_CACHE := $(DC_OPTS) +endif .PHONY: import-osm -import-osm: db-start all - docker-compose run $(DC_OPTS) import-osm +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: import-sql -import-sql: db-start all - docker-compose run $(DC_OPTS) openmaptiles-tools import-sql +.PHONY: start-update-osm +start-update-osm: start-db + @$(assert_area_is_given) + $(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: start-db-nowait + @$(assert_area_is_given) + $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) openmaptiles-tools sh -c 'pgwait && import-diff' -.PHONY: import-osmsql -import-osmsql: db-start all - docker-compose run $(DC_OPTS) import-osm - docker-compose run $(DC_OPTS) openmaptiles-tools import-sql +.PHONY: import-data +import-data: start-db + $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) import-data + +.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 '1{print; fflush()} $$0~".*ERROR" {txt=$$0} END{ if(txt){print "\n*** ERROR detected, aborting:"; print txt; exit(1)} }' .PHONY: generate-tiles -generate-tiles: db-start all - rm -rf data/tiles.mbtiles - if [ -f ./data/docker-compose-config.yml ]; then \ - docker-compose -f docker-compose.yml -f ./data/docker-compose-config.yml run $(DC_OPTS) generate-vectortiles; \ - else \ - docker-compose run $(DC_OPTS) generate-vectortiles; \ +generate-tiles: all start-db + @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 + @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: generate-tiles-pg +generate-tiles-pg: all start-db + @echo "Generating tiles into $(MBTILES_LOCAL_FILE) (will delete if already exists) using PostGIS ST_MVT()..." + @rm -rf "$(MBTILES_LOCAL_FILE)" +# 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 - docker-compose run $(DC_OPTS) openmaptiles-tools generate-metadata ./data/tiles.mbtiles - docker-compose run $(DC_OPTS) openmaptiles-tools chmod 666 ./data/tiles.mbtiles .PHONY: start-tileserver -start-tileserver: +start-tileserver: init-dirs @echo " " @echo "***********************************************************" @echo "* " - @echo "* Download/refresh klokantech/tileserver-gl docker image" - @echo "* see documentation: https://github.com/klokantech/tileserver-gl" + @echo "* Download/refresh maptiler/tileserver-gl docker image" + @echo "* see documentation: https://github.com/maptiler/tileserver-gl" @echo "* " @echo "***********************************************************" @echo " " - docker pull klokantech/tileserver-gl + docker pull maptiler/tileserver-gl @echo " " @echo "***********************************************************" @echo "* " - @echo "* Start klokantech/tileserver-gl " - @echo "* ----------------------------> check localhost:8080 " + @echo "* Start maptiler/tileserver-gl " + @echo "* ----------------------------> check $(OMT_HOST):$(TPORT) " @echo "* " @echo "***********************************************************" @echo " " - docker run $(DC_OPTS) -it --name tileserver-gl -v $$(pwd)/data:/data -p 8080:80 klokantech/tileserver-gl + $(DOCKER_COMPOSE) up -d tileserver-gl + +.PHONY: stop-tileserver +stop-tileserver: + $(DOCKER_COMPOSE) stop tileserver-gl .PHONY: start-postserve -start-postserve: db-start +start-postserve: start-db @echo " " @echo "***********************************************************" @echo "* " - @echo "* Bring up postserve at localhost:8090" + @echo "* Bring up postserve at $(OMT_HOST):$(PPORT)" + @echo "* --> can view it locally (use make start-maputnik)" + @echo "* --> or can use https://maputnik.github.io/editor" + @echo "* " + @echo "* set data source / TileJSON URL to $(OMT_HOST):$(PPORT)" @echo "* " @echo "***********************************************************" @echo " " - docker-compose up -d postserve - docker pull maputnik/editor + $(DOCKER_COMPOSE) up -d postserve + +.PHONY: stop-postserve +stop-postserve: + $(DOCKER_COMPOSE) stop postserve + +.PHONY: start-maputnik +start-maputnik: stop-maputnik start-postserve @echo " " @echo "***********************************************************" @echo "* " @echo "* Start maputnik/editor " - @echo "* ---> go to http://localhost:8088" - @echo "* ---> set 'data source' to http://localhost:8090" + @echo "* ---> go to $(OMT_HOST):8088 " + @echo "* ---> set data source / TileJSON URL to $(OMT_HOST):$(PPORT)" @echo "* " @echo "***********************************************************" @echo " " - docker rm -f maputnik_editor || true - docker run $(DC_OPTS) --name maputnik_editor -d -p 8088:8888 maputnik/editor - -.PHONY: generate-qareports -generate-qareports: - ./qa/run.sh - -build/devdoc: - mkdir -p ./build/devdoc + $(DOCKER_COMPOSE) up -d maputnik_editor +.PHONY: stop-maputnik +stop-maputnik: + -$(DOCKER_COMPOSE) stop maputnik_editor -layers = $(notdir $(wildcard layers/*)) # all layers - -.PHONY: etl-graph -etl-graph: - @echo 'Use' - @echo ' make etl-graph-[layer] to generate etl graph for [layer]' - @echo ' example: make etl-graph-poi' - @echo 'Valid layers: $(layers)' +# STAT_FUNCTION=frequency|toplength|variance +.PHONY: generate-qa +generate-qa: all start-db-nowait + @echo " " + @echo "e.g. make generate-qa STAT_FUNCTION=frequency LAYER=transportation ATTRIBUTE=class" + @echo " " + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools \ + layer-stats $(STAT_FUNCTION) $(TILESET_FILE) $(LAYER) $(ATTRIBUTE) -m 0 -n 14 -v -# generate etl graph for a certain layer, e.g. etl-graph-building, etl-graph-place -etl-graph-%: layers/% build/devdoc - docker-compose run $(DC_USER_OPTS) openmaptiles-tools generate-etlgraph layers/$*/$*.yaml ./build/devdoc - @$(COPY_TO_GIT) ./build/devdoc/etl_$*.png layers/$*/etl_diagram.png +# generate all etl and mapping graphs +.PHONY: generate-devdoc +generate-devdoc: init-dirs + mkdir -p ./build/devdoc && \ + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools sh -c \ + 'generate-etlgraph $(TILESET_FILE) $(GRAPH_PARAMS) && \ + generate-mapping-graph $(TILESET_FILE) $(GRAPH_PARAMS)' + +.PHONY: bash +bash: init-dirs + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools bash + +.PHONY: import-wikidata +import-wikidata: init-dirs + $(DOCKER_COMPOSE) $(DC_CONFIG_CACHE) run $(DC_OPTS_CACHE) openmaptiles-tools import-wikidata --cache /cache/wikidata-cache.json $(TILESET_FILE) + +.PHONY: reset-db-stats +reset-db-stats: init-dirs + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools psql.sh -v ON_ERROR_STOP=1 -P pager=off -c 'SELECT pg_stat_statements_reset();' + +.PHONY: list-views +list-views: init-dirs + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools psql.sh -v ON_ERROR_STOP=1 -A -F"," -P pager=off -P footer=off \ + -c "select viewname from pg_views where schemaname='public' order by viewname;" + +.PHONY: list-tables +list-tables: init-dirs + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools psql.sh -v ON_ERROR_STOP=1 -A -F"," -P pager=off -P footer=off \ + -c "select tablename from pg_tables where schemaname='public' order by tablename;" +.PHONY: psql-list-tables +psql-list-tables: init-dirs + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools psql.sh -v ON_ERROR_STOP=1 -P pager=off -c "\d+" -mappingLayers = $(notdir $(patsubst %/mapping.yaml,%, $(wildcard layers/*/mapping.yaml))) # layers with mapping.yaml +.PHONY: vacuum-db +vacuum-db: init-dirs + @echo "Start - postgresql: VACUUM ANALYZE VERBOSE;" + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools psql.sh -v ON_ERROR_STOP=1 -P pager=off -c 'VACUUM ANALYZE VERBOSE;' -# generate mapping graph for a certain layer, e.g. mapping-graph-building, mapping-graph-place -.PHONY: mapping-graph -mapping-graph: - @echo 'Use' - @echo ' make mapping-graph-[layer] to generate mapping graph for [layer]' - @echo ' example: make mapping-graph-poi' - @echo 'Valid layers: $(mappingLayers)' +.PHONY: analyze-db +analyze-db: init-dirs + @echo "Start - postgresql: ANALYZE VERBOSE;" + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools psql.sh -v ON_ERROR_STOP=1 -P pager=off -c 'ANALYZE VERBOSE;' -mapping-graph-%: ./layers/%/mapping.yaml build/devdoc - docker-compose run $(DC_USER_OPTS) openmaptiles-tools generate-mapping-graph layers/$*/$*.yaml ./build/devdoc/mapping-diagram-$* - @$(COPY_TO_GIT) ./build/devdoc/mapping-diagram-$*.png layers/$*/mapping_diagram.png +.PHONY: list-docker-images +list-docker-images: + docker images | grep openmaptiles -# generate all etl and mapping graphs -generate-devdoc: $(addprefix etl-graph-,$(layers)) $(addprefix mapping-graph-,$(mappingLayers)) +.PHONY: refresh-docker-images +refresh-docker-images: init-dirs +ifneq ($(NO_REFRESH),) + @echo "Skipping docker image refresh" +else + @echo "" + @echo "Refreshing docker images... Use NO_REFRESH=1 to skip." +ifneq ($(USE_PRELOADED_IMAGE),) + POSTGIS_IMAGE=openmaptiles/postgis-preloaded \ + docker-compose 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 +endif +endif -.PHONY: import-sql-dev -import-sql-dev: - docker-compose run $(DC_OPTS) openmaptiles-tools bash +.PHONY: remove-docker-images +remove-docker-images: + @echo "Deleting all openmaptiles related docker image(s)..." + @$(DOCKER_COMPOSE) down + @docker images "openmaptiles/*" -q | $(XARGS) docker rmi -f + @docker images "maputnik/editor" -q | $(XARGS) docker rmi -f + @docker images "maptiler/tileserver-gl" -q | $(XARGS) docker rmi -f -.PHONY: import-osm-dev -import-osm-dev: - docker-compose run $(DC_OPTS) import-osm /bin/bash +.PHONY: clean-unnecessary-docker +clean-unnecessary-docker: + @echo "Deleting unnecessary container(s)..." + @docker ps -a -q --filter "status=exited" | $(XARGS) docker rm + @echo "Deleting unnecessary image(s)..." + @docker images | awk -F" " '//{print $$3}' | $(XARGS) docker rmi -# the `download-geofabrik` error message mention `list`, if the area parameter is wrong. so I created a similar make command -.PHONY: list -list: - docker-compose run $(DC_OPTS) import-osm ./download-geofabrik-list.sh +.PHONY: test-perf-null +test-perf-null: init-dirs + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools test-perf $(TILESET_FILE) --test null --no-color -# same as a `make list` -.PHONY: download-geofabrik-list -download-geofabrik-list: - docker-compose run $(DC_OPTS) import-osm ./download-geofabrik-list.sh +.PHONY: build-test-pbf +build-test-pbf: init-dirs + docker-compose run $(DC_OPTS) openmaptiles-tools /tileset/.github/workflows/build-test-data.sh -.PHONY: download-wikidata -download-wikidata: - mkdir -p wikidata && docker-compose run $(DC_OPTS) --entrypoint /usr/src/app/download-gz.sh import-wikidata +.PHONY: debug +debug: ## Use this target when developing Makefile itself to verify loaded environment variables + @$(assert_area_is_given) + @echo file_exists = $(wildcard $(AREA_BBOX_FILE)) + @echo AREA_BBOX_FILE = $(AREA_BBOX_FILE) , $$AREA_ENV_FILE + @echo BBOX = $(BBOX) , $$BBOX + @echo MIN_ZOOM = $(MIN_ZOOM) , $$MIN_ZOOM + @echo MAX_ZOOM = $(MAX_ZOOM) , $$MAX_ZOOM -.PHONY: psql-list-tables -psql-list-tables: - docker-compose run $(DC_OPTS) import-osm ./psql.sh -P pager=off -c "\d+" +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' -.PHONY: psql-pg-stat-reset -psql-pg-stat-reset: - docker-compose run $(DC_OPTS) import-osm ./psql.sh -P pager=off -c 'SELECT pg_stat_statements_reset();' +data/changes.state.txt: + cp -f tests/changes.state.txt data/ -.PHONY: forced-clean-sql -forced-clean-sql: - docker-compose run $(DC_OPTS) import-osm ./psql.sh -c "DROP SCHEMA IF EXISTS public CASCADE ; CREATE SCHEMA IF NOT EXISTS public; " - docker-compose run $(DC_OPTS) import-osm ./psql.sh -c "CREATE EXTENSION hstore; CREATE EXTENSION postgis; CREATE EXTENSION unaccent; CREATE EXTENSION fuzzystrmatch; CREATE EXTENSION osml10n; CREATE EXTENSION pg_stat_statements;" - docker-compose run $(DC_OPTS) import-osm ./psql.sh -c "GRANT ALL ON SCHEMA public TO public;COMMENT ON SCHEMA public IS 'standard public schema';" +data/last.state.txt: + cp -f tests/last.state.txt data/ -.PHONY: pgclimb-list-views -pgclimb-list-views: - docker-compose run $(DC_OPTS) import-osm ./pgclimb.sh -c "select schemaname,viewname from pg_views where schemaname='public' order by viewname;" csv +data/changes.repl.json: + cp -f tests/changes.repl.json data/ -.PHONY: pgclimb-list-tables -pgclimb-list-tables: - docker-compose run $(DC_OPTS) import-osm ./pgclimb.sh -c "select schemaname,tablename from pg_tables where schemaname='public' order by tablename;" csv +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' -.PHONY: psql-vacuum-analyze -psql-vacuum-analyze: - @echo "Start - postgresql: VACUUM ANALYZE VERBOSE;" - docker-compose run $(DC_OPTS) import-osm ./psql.sh -P pager=off -c 'VACUUM ANALYZE VERBOSE;' +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) -.PHONY: psql-analyze -psql-analyze: - @echo "Start - postgresql: ANALYZE VERBOSE ;" - docker-compose run $(DC_OPTS) import-osm ./psql.sh -P pager=off -c 'ANALYZE VERBOSE;' + @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 -.PHONY: list-docker-images -list-docker-images: - docker images | grep openmaptiles + @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)} }' -.PHONY: refresh-docker-images -refresh-docker-images: - docker-compose pull --ignore-pull-failures + @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' -.PHONY: remove-docker-images -remove-docker-images: - @echo "Deleting all openmaptiles related docker image(s)..." - @docker-compose down - @docker images | grep "openmaptiles" | awk -F" " '{print $$3}' | xargs --no-run-if-empty docker rmi -f - @docker images | grep "osm2vectortiles/mapbox-studio" | awk -F" " '{print $$3}' | xargs --no-run-if-empty docker rmi -f - @docker images | grep "klokantech/tileserver-gl" | awk -F" " '{print $$3}' | xargs --no-run-if-empty docker rmi -f + @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' -.PHONY: docker-unnecessary-clean -docker-unnecessary-clean: - @echo "Deleting unnecessary container(s)..." - @docker ps -a | grep Exited | awk -F" " '{print $$1}' | xargs --no-run-if-empty docker rm - @echo "Deleting unnecessary image(s)..." - @docker images | grep \ | awk -F" " '{print $$3}' | xargs --no-run-if-empty docker rmi + @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' qwant: - ./generate_qwant.sh + ./generate_qwant.sh \ No newline at end of file diff --git a/QUICKSTART.md b/QUICKSTART.md index 90fed7ef3..268736055 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -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" ] @@ -363,12 +363,13 @@ This is generating `.mbtiles` for your area : [ MIN_ZOOM: "0" - MAX_ZOOM: "7" ./quickstart.sh yukon # Yukon, Canada ``` ### Using your own OSM data -Mbtiles can be generated from an arbitrary osm.pbf (e.g. for a region that is not covered by an existing extract) by making the `data/` directory and placing an osm.pbf inside/ +Mbtiles can be generated from an arbitrary osm.pbf (e.g. for a region that is not covered by an existing extract) by making the `data/` directory and placing an *.osm.pbf (e.g. `mydata.osm.pbf`) inside. ``` mkdir -p data -mv my.osm.pbf data/ -./quickstart.sh my +mv mydata.osm.pbf data/ +make generate-bbox-file area=mydata +./quickstart.sh mydata ``` ### Check postserve @@ -384,20 +385,26 @@ and the generated maps are going to be available in webbrowser on [localhost:808 This is only a quick preview, because your mbtiles only generated to zoom level 7 ! -### Change MIN_ZOOM and MAX_ZOOM +### Set which zooms to generate -modify the settings in the `.env` file, the defaults : -* QUICKSTART_MIN_ZOOM=0 -* QUICKSTART_MAX_ZOOM=7 - -and re-start `./quickstart.sh ` -* the new config file re-generating to here ./data/docker-compose-config.yml -* Known problems: - * If you use same area - then the ./data/docker-compose-config.yml not re-generating, so you have to modify by hand! +modify the settings in the `.env` file, the defaults: +* `MIN_ZOOM=0` +* `MAX_ZOOM=7` Hints: -* Small increments! Never starts with the MAX_ZOOM = 14 -* The suggested MAX_ZOOM = 14 - use only with small extracts +* Small increments! Never starts with the `MAX_ZOOM = 14` +* The suggested `MAX_ZOOM = 14` - use only with small extracts + +### Set the bounding box to generate + +By default, tile generation is done for the full extent of the area. +If you want to generate a tiles for a smaller extent, modify the settings in the `.env` file, the default: +* `BBOX=-180.0,-85.0511,180.0,85.0511` + +Delete the `./data/.bbox` file, and re-start `./quickstart.sh ` + +Hint: +* The [boundingbox.klokantech.com](https://boundingbox.klokantech.com/) site can be used to find a bounding box (CSV format) using a map. ### Check other commands @@ -408,36 +415,59 @@ 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 # 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 klokantech/tileserver-gl [ see localhost:8080 ] + make start-maputnik # start Maputnik Editor + dynamic tile server [ see http://localhost:8088 ] + make start-postserve # start dynamic tile server [ see http://localhost:8090 ] + make stop-postserve # stop dynamic tile server + make start-tileserver # start maptiler/tileserver-gl [ see http://localhost:8080 ] 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 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-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-qareports # generate reports [./build/qareports] - make generate-devdoc # generate devdoc [./build/devdoc] - make import-sql-dev # start import-sql /bin/bash terminal - make import-osm-dev # start import-osm /bin/bash terminal (imposm3) - make clean-docker # remove docker containers, PG data volume - make forced-clean-sql # drop all PostgreSQL tables for clean environment - make docker-unnecessary-clean # clean unnecessary docker image(s) and container(s) + 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 pgclimb-list-views # list PostgreSQL public schema views - make pgclimb-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 list-docker-images # show a list of available docker images ============================================================================== ``` diff --git a/README.md b/README.md index 5e050ada6..3e1ac21c0 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ To generate them, you need to: * run `CONFIG_DIR= make qwant` -## OpenMapTiles [![Build Status](https://travis-ci.org/openmaptiles/openmaptiles.svg?branch=master)](https://travis-ci.org/openmaptiles/openmaptiles) +## OpenMapTiles [![Build Status](https://github.com/openmaptiles/openmaptiles/workflows/OMT_CI/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 [openmaptiles.com](https://openmaptiles.com/). +OpenMapTiles is an extensible and open tile schema based on the OpenStreetMap. This project is used to generate vector tiles for online zoomable maps. OpenMapTiles is about creating a beautiful basemaps with general layers containing topographic information. More information [openmaptiles.org](https://openmaptiles.org/) and [maptiler.com/data/](https://www.maptiler.com/data/). We encourage you to collaborate, reuse and adapt existing layers, or add your own layers. You may use our approach for your own vector tile project. Feel free to fork the repo and experiment. The repository is built on top of the [openmaptiles/openmaptiles-tools](https://github.com/openmaptiles/openmaptiles-tools) to simplify vector tile creation. @@ -24,7 +24,7 @@ Please keep in mind that OpenMapTiles schema should display general topographic - :link: Schema https://openmaptiles.org/schema - :link: Docs https://openmaptiles.org/docs -- :link: Production package: https://openmaptiles.com/production-package/ +- :link: Data for download: https://www.maptiler.com/data/ - :link: Hosting https://www.maptiler.com/cloud/ - :link: Create own layer https://github.com/openmaptiles/openmaptiles-skiing - :link: Discuss at the #openmaptiles channel at [OSM Slack](https://osmus-slack.herokuapp.com/) @@ -36,14 +36,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) -- [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) +- [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) -- [Toner](https://github.com/openmaptiles/toner-gl-style) +- [MapTiler Toner](https://github.com/openmaptiles/maptiler-toner-gl-style) - [OSM Liberty](https://github.com/maputnik/osm-liberty) +- [Positron](https://github.com/openmaptiles/positron-gl-style) +- [Dark Matter](https://github.com/openmaptiles/dark-matter-gl-style) We also ported over our favorite old raster styles (TM2). @@ -87,6 +88,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. @@ -99,10 +104,10 @@ make ``` You can execute the following manual steps (for better understanding) -or use the provided `quickstart.sh` script. +or use the provided `quickstart.sh` script to automatically download and import given area. If area is not given, albania will be imported. ``` -./quickstart.sh +./quickstart.sh ``` ### Prepare the Database @@ -110,43 +115,44 @@ or use the provided `quickstart.sh` script. Now start up the database container. ```bash -docker-compose up -d postgres +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 -docker-compose run import-water -docker-compose run import-natural-earth -docker-compose run import-lakelines -docker-compose run import-osmborder +make import-data ``` -**[Optional]** -Import latest 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. - -Beware that current [Wikidata dump](https://dumps.wikimedia.org/wikidatawiki/entities/latest-all.json.gz) is more than 55GB, it takes time to download and import it. If you just want to have a quickstart on OpenMapTiles, just skip this step. +Download OpenStreetMap data extracts from any source like [Geofabrik](http://download.geofabrik.de/), and store the PBF file in the `./data` directory. To use a specific download source, use `download-geofabrik`, `download-bbbike`, or `download-osmfr`, or use `download` to make it auto-pick the area. You can use `area=planet` for the entire OSM dataset (very large). Note that if you have more than one `data/*.osm.pbf` file, every `make` command will always require `area=...` parameter (or you can just `export area=...` first). ```bash -make download-wikidata -docker-compose run import-wikidata +make download area=albania ``` -[Download OpenStreetMap data extracts](http://download.geofabrik.de/) and store the PBF file in the `./data` directory. +[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. ```bash -cd data -wget http://download.geofabrik.de/europe/albania-latest.osm.pbf +make import-osm ``` -[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`). +Import labels from Wikidata. If an OSM feature has [Key:wikidata](https://wiki.openstreetmap.org/wiki/Key:wikidata), OpenMapTiles check corresponding item in Wikidata and use its [labels](https://www.wikidata.org/wiki/Help:Label) for languages listed in [openmaptiles.yaml](openmaptiles.yaml). So the generated vector tiles includes multi-languages in name field. + +This step uses [Wikidata Query Service](https://query.wikidata.org) to download just the Wikidata IDs that already exist in the database. ```bash -docker-compose run import-osm +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`. @@ -156,16 +162,43 @@ make make import-sql ``` -Now you are ready to **generate the vector tiles**. Using environment variables -you can limit the bounding box and zoom levels of what you want to generate (`docker-compose.yml`). +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-pg # generate tiles ``` -docker-compose run generate-vectortiles + +### 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 000000000..f77c6032c --- /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 20e5afe54..7b29c1b06 100644 --- a/UPDATE.md +++ b/UPDATE.md @@ -1,38 +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 ``` -docker-compose run 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 ``` -docker-compose run import-osm-diff +make import-diff ``` -## Generate Changed Tiles +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. -After the import has finished **imposm3** will store a list of tiles in text format in the `diffdir`. -Copy the as `tiles.txt` to the import folder. +#### 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 -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-MINGW64.yml b/docker-compose-MINGW64.yml new file mode 100644 index 000000000..2f3312972 --- /dev/null +++ b/docker-compose-MINGW64.yml @@ -0,0 +1,11 @@ +# This version must match the MAKE_DC_VERSION in docker-compose.yml +version: "3" + +volumes: + cache: + +services: + + openmaptiles-tools: + volumes: + - cache:/cache diff --git a/docker-compose.yml b/docker-compose.yml index e13d8829a..1e317e54a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,99 +1,75 @@ -version: "2" +# This version must match the MAKE_DC_VERSION value below +version: "3" + volumes: pgdata: - cache: + +networks: + postgres: + driver: bridge + services: + postgres: - image: "openmaptiles/postgis:${TOOLS_VERSION}" + image: "${POSTGIS_IMAGE:-openmaptiles/postgis}:${TOOLS_VERSION}" + # Use "command: postgres -c jit=off" for PostgreSQL 11+ because of slow large MVT query processing volumes: - pgdata:/var/lib/postgresql/data networks: - - postgres_conn + - postgres ports: - - "5432" - env_file: .env - import-natural-earth: - image: "openmaptiles/import-natural-earth:${TOOLS_VERSION}" - env_file: .env - networks: - - postgres_conn - import-water: - image: "openmaptiles/import-water:${TOOLS_VERSION}" - env_file: .env - networks: - - postgres_conn - import-lakelines: - image: "openmaptiles/import-lakelines:${TOOLS_VERSION}" - env_file: .env - networks: - - postgres_conn - import-osm: - image: "openmaptiles/import-osm:${TOOLS_VERSION}" + - "${PGPORT:-5432}:${PGPORT:-5432}" env_file: .env environment: - DIFF_MODE: ${DIFF_MODE} - networks: - - postgres_conn - volumes: - - ./data:/import - - ./build:/mapping - - cache:/cache - import-osmborder: - image: "openmaptiles/import-osmborder:${TOOLS_VERSION}" + # postgress container uses old variable names + POSTGRES_DB: ${PGDATABASE:-openmaptiles} + POSTGRES_USER: ${PGUSER:-openmaptiles} + POSTGRES_PASSWORD: ${PGPASSWORD:-openmaptiles} + PGPORT: ${PGPORT:-5432} + + import-data: + image: "openmaptiles/import-data:${TOOLS_VERSION}" env_file: .env networks: - - postgres_conn - import-osm-diff: - image: "openmaptiles/import-osm:${TOOLS_VERSION}" + - postgres + + openmaptiles-tools: &openmaptiles-tools + image: "openmaptiles/openmaptiles-tools:${TOOLS_VERSION}" env_file: .env - command: ./import_diff.sh environment: + # Must match the version of this file (first line) + # download-osm will use it when generating a composer file + MAKE_DC_VERSION: "3" + # Allow DIFF_MODE, MIN_ZOOM, and MAX_ZOOM to be overwritten from shell DIFF_MODE: ${DIFF_MODE} + MIN_ZOOM: ${MIN_ZOOM} + MAX_ZOOM: ${MAX_ZOOM} + #Provide BBOX from *.bbox file if exists, else from .env + BBOX: ${BBOX} + # Imposm configuration file describes how to load updates when enabled + IMPOSM_CONFIG_FILE: ${IMPOSM_CONFIG_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_conn + - postgres volumes: + - .:/tileset - ./data:/import + - ./data:/export + - ./build/sql:/sql - ./build:/mapping - - cache:/cache + - ./cache:/cache + - ./style:/style + update-osm: - image: "openmaptiles/import-osm:${TOOLS_VERSION}" - env_file: .env - environment: - DIFF_MODE: ${DIFF_MODE} - command: ./import_update.sh - networks: - - postgres_conn - volumes: - - ./data:/import - - ./build:/mapping - - cache:/cache - import-sql: - # This target is obsolete, and was left for backwards compatibility - # Use openmaptiles-tools target instead - image: "openmaptiles/openmaptiles-tools:${TOOLS_VERSION}" - command: import-sql - env_file: .env - networks: - - postgres_conn - volumes: - - .:/tileset - - ./build:/sql - import-wikidata: - image: "openmaptiles/import-wikidata:${TOOLS_VERSION}" - env_file: .env - command: import-wikidata - networks: - - postgres_conn - volumes: - - ./wikidata:/import - openmaptiles-tools: - image: "openmaptiles/openmaptiles-tools:${TOOLS_VERSION}" - env_file: .env - networks: - - postgres_conn - volumes: - - .:/tileset - - ./build:/sql + <<: *openmaptiles-tools + command: import-update + generate-changed-vectortiles: image: "openmaptiles/generate-vectortiles:${TOOLS_VERSION}" command: ./export-list.sh @@ -101,31 +77,66 @@ services: - ./data:/export - ./build/openmaptiles.tm2source:/tm2source networks: - - postgres_conn + - postgres env_file: .env + environment: + 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}" volumes: - ./data:/export - ./build/openmaptiles.tm2source:/tm2source networks: - - postgres_conn + - postgres env_file: .env environment: + MBTILES_NAME: ${MBTILES_FILE} BBOX: ${BBOX} MIN_ZOOM: ${MIN_ZOOM} MAX_ZOOM: ${MAX_ZOOM} + # 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}" - command: postserve openmaptiles.yaml --verbose + command: "postserve ${TILESET_FILE} --verbose --serve=${OMT_HOST:-http://localhost}:${PPORT:-8090}" env_file: .env + environment: + TILESET_FILE: ${TILESET_FILE} networks: - - postgres_conn + - postgres ports: - - "8090:8090" + - "${PPORT:-8090}:${PPORT:-8090}" volumes: - .:/tileset -networks: - postgres_conn: - driver: bridge + 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/generate_qwant.sh b/generate_qwant.sh index e481729de..ab45ad677 100755 --- a/generate_qwant.sh +++ b/generate_qwant.sh @@ -6,17 +6,17 @@ set -e -for tiles in 'base' 'poi' 'lite'; do +for tiles in 'base' 'poi'; do tileset="openmaptiles_$tiles.yaml" - if [[ $tiles != "lite" ]]; then - # no sql and no mapping for the lite tiles - generate-sql $tileset > $CONFIG_DIR/imposm/generated_$tiles.sql - generate-imposm3 $tileset > $CONFIG_DIR/imposm/generated_mapping_$tiles.yaml - # Use "single id space" to store osm_id as integers in a deterministic way - # And be able to transform it back to string with osm type (node/way/relation) - echo 'use_single_id_space: true' >> $CONFIG_DIR/imposm/generated_mapping_$tiles.yaml - fi + # no sql and no mapping for the lite tiles + generate-sql $tileset > $CONFIG_DIR/imposm/generated_$tiles.sql + generate-imposm3 $tileset > $CONFIG_DIR/imposm/generated_mapping_$tiles.yaml + + # Use "single id space" to store osm_id as integers in a deterministic way + # And be able to transform it back to string with osm type (node/way/relation) + echo 'use_single_id_space: true' >> $CONFIG_DIR/imposm/generated_mapping_$tiles.yaml generate-tm2source $tileset --host="localhost" --port=5432 --database="gis" --user="nice_user" --password="nice_password" > $CONFIG_DIR/tilerator/data_tm2source_$tiles.yml + done diff --git a/layers/aerodrome_label/aerodrome_label.sql b/layers/aerodrome_label/aerodrome_label.sql new file mode 100644 index 000000000..b3c36258a --- /dev/null +++ b/layers/aerodrome_label/aerodrome_label.sql @@ -0,0 +1,62 @@ + +-- etldoc: layer_aerodrome_label[shape=record fillcolor=lightpink, style="rounded,filled", label="layer_aerodrome_label | z8 | z9 | z10+" ] ; + +CREATE OR REPLACE FUNCTION layer_aerodrome_label(bbox geometry, + zoom_level integer) + RETURNS TABLE + ( + id bigint, + geometry geometry, + name text, + name_en text, + name_de text, + tags hstore, + class text, + iata text, + icao text, + ele int, + ele_ft int + ) +AS +$$ +SELECT + -- etldoc: osm_aerodrome_label_point -> layer_aerodrome_label:z8 + -- etldoc: osm_aerodrome_label_point -> layer_aerodrome_label:z9 + ABS(osm_id) AS id, -- mvt feature IDs can't be negative + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + aerodrome_type AS class, + NULLIF(iata, '') AS iata, + NULLIF(icao, '') AS icao, + substring(ele FROM E'^(-?\\d+)(\\D|$)')::int AS ele, + round(substring(ele FROM E'^(-?\\d+)(\\D|$)')::int * 3.2808399)::int AS ele_ft +FROM osm_aerodrome_label_point +WHERE geometry && bbox + AND aerodrome_type = 'international' + AND iata <> '' + AND zoom_level BETWEEN 8 AND 9 + +UNION ALL + +SELECT + -- etldoc: osm_aerodrome_label_point -> layer_aerodrome_label:z10_ + ABS(osm_id) AS id, -- mvt feature IDs can't be negative + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + aerodrome_type AS class, + NULLIF(iata, '') AS iata, + NULLIF(icao, '') AS icao, + substring(ele FROM E'^(-?\\d+)(\\D|$)')::int AS ele, + round(substring(ele FROM E'^(-?\\d+)(\\D|$)')::int * 3.2808399)::int AS ele_ft +FROM osm_aerodrome_label_point +WHERE geometry && bbox + AND zoom_level >= 10; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/aerodrome_label/aerodrome_label.yaml b/layers/aerodrome_label/aerodrome_label.yaml index 015d8c25e..3a48cf6b9 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 @@ -15,25 +15,36 @@ layer: [`aerodrome`](http://wiki.openstreetmap.org/wiki/Proposed_features/Aerodrome) and `aerodrome:type` tags. values: - - international - - public - - regional - - military - - private - - other + international: + aerodrome: 'international' + aerodrome_type: 'international' + public: + aerodrome: 'public' + aerodrome_type: ['%public%', 'civil'] + regional: + aerodrome: 'regional' + aerodrome_type: 'regional' + military: + aerodrome: 'military' + aerodrome_type: '%military%' + military: 'airfield' + private: + aerodrome: 'private' + aerodrome_type: 'private' + other: iata: 3-character code issued by the IATA. icao: 4-letter code issued by the ICAO. ele: Elevation (`ele`) in meters. ele_ft: Elevation (`ele`) in feets. datasource: geometry_field: geometry - key_field: 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!), zres(z(!scale_denominator!))::numeric)) 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.sql - - ./layer.sql + - ./update_aerodrome_label_point.sql + - ./aerodrome_label.sql datasources: - type: imposm3 mapping_file: ./mapping.yaml diff --git a/layers/aerodrome_label/etl_diagram.png b/layers/aerodrome_label/etl_diagram.png index 387966441..c659ab4d4 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/layer.sql b/layers/aerodrome_label/layer.sql deleted file mode 100644 index 8d5adfa1b..000000000 --- a/layers/aerodrome_label/layer.sql +++ /dev/null @@ -1,60 +0,0 @@ - --- etldoc: layer_aerodrome_label[shape=record fillcolor=lightpink, style="rounded,filled", label="layer_aerodrome_label | z10+" ] ; - -CREATE OR REPLACE FUNCTION layer_aerodrome_label( - bbox geometry, - zoom_level integer, - pixel_width numeric) - RETURNS TABLE( - osm_id bigint, - geometry geometry, - name text, - name_en text, - name_de text, - tags hstore, - class text, - iata text, - icao text, - ele int, - ele_ft int) AS -$$ - -- etldoc: osm_aerodrome_label_point -> layer_aerodrome_label:z10_ - SELECT - osm_id, - geometry, - name, - COALESCE(NULLIF(name_en, ''), name) AS name_en, - COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, - tags, - CASE - WHEN aerodrome = 'international' - OR aerodrome_type = 'international' - THEN 'international' - WHEN - aerodrome = 'public' - OR aerodrome_type LIKE '%public%' - OR aerodrome_type = 'civil' - THEN 'public' - WHEN - aerodrome = 'regional' - OR aerodrome_type = 'regional' - THEN 'regional' - WHEN - aerodrome = 'military' - OR aerodrome_type LIKE '%military%' - OR military = 'airfield' - THEN 'military' - WHEN - aerodrome = 'private' - OR aerodrome_type = 'private' - THEN 'private' - ELSE 'other' - END AS class, - NULLIF(iata, '') AS iata, - NULLIF(icao, '') AS icao, - substring(ele from E'^(-?\\d+)(\\D|$)')::int AS ele, - round(substring(ele from E'^(-?\\d+)(\\D|$)')::int*3.2808399)::int AS ele_ft - FROM osm_aerodrome_label_point - WHERE geometry && bbox AND zoom_level >= 10; - -$$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/aerodrome_label/mapping.yaml b/layers/aerodrome_label/mapping.yaml index 46cdb71d9..ff0f22c08 100644 --- a/layers/aerodrome_label/mapping.yaml +++ b/layers/aerodrome_label/mapping.yaml @@ -3,7 +3,7 @@ tables: # etldoc: imposm3 -> osm_aerodrome_label_point aerodrome_label_point: type: geometry - fields: + columns: - name: osm_id type: id - name: geometry diff --git a/layers/aerodrome_label/mapping_diagram.png b/layers/aerodrome_label/mapping_diagram.png index cbbb89913..7596a7c33 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 000000000..30d73f37a --- /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": 181 + } + ] +} \ No newline at end of file diff --git a/layers/aerodrome_label/update.sql b/layers/aerodrome_label/update.sql deleted file mode 100644 index 541d7baed..000000000 --- a/layers/aerodrome_label/update.sql +++ /dev/null @@ -1,51 +0,0 @@ -DROP TRIGGER IF EXISTS trigger_flag ON osm_aerodrome_label_point; -DROP TRIGGER IF EXISTS trigger_refresh ON aerodrome_label.updates; - --- etldoc: osm_aerodrome_label_point -> osm_aerodrome_label_point -CREATE OR REPLACE FUNCTION update_aerodrome_label_point() RETURNS VOID AS $$ -BEGIN - UPDATE osm_aerodrome_label_point - SET geometry = ST_Centroid(geometry) - WHERE ST_GeometryType(geometry) <> 'ST_Point'; - - UPDATE osm_aerodrome_label_point - SET tags = update_tags(tags, geometry) - WHERE COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL; -END; -$$ LANGUAGE plpgsql; - -SELECT update_aerodrome_label_point(); - --- Handle updates - -CREATE SCHEMA IF NOT EXISTS aerodrome_label; - -CREATE TABLE IF NOT EXISTS aerodrome_label.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION aerodrome_label.flag() RETURNS trigger AS $$ -BEGIN - INSERT INTO aerodrome_label.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; -END; -$$ language plpgsql; - -CREATE OR REPLACE FUNCTION aerodrome_label.refresh() RETURNS trigger AS - $BODY$ - BEGIN - RAISE LOG 'Refresh aerodrome_label'; - PERFORM update_aerodrome_label_point(); - DELETE FROM aerodrome_label.updates; - RETURN null; - END; - $BODY$ -language plpgsql; - -CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE ON osm_aerodrome_label_point - FOR EACH STATEMENT - EXECUTE PROCEDURE aerodrome_label.flag(); - -CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON aerodrome_label.updates - INITIALLY DEFERRED - FOR EACH ROW - EXECUTE PROCEDURE aerodrome_label.refresh(); diff --git a/layers/aerodrome_label/update_aerodrome_label_point.sql b/layers/aerodrome_label/update_aerodrome_label_point.sql new file mode 100644 index 000000000..72eb175ac --- /dev/null +++ b/layers/aerodrome_label/update_aerodrome_label_point.sql @@ -0,0 +1,108 @@ +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 +); + +-- etldoc: osm_aerodrome_label_point -> osm_aerodrome_label_point +CREATE OR REPLACE FUNCTION update_aerodrome_label_point(full_update boolean) RETURNS void AS +$$ + UPDATE osm_aerodrome_label_point + SET geometry = ST_Centroid(geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM aerodrome_label.osm_ids)) + AND ST_GeometryType(geometry) <> 'ST_Point'; + + UPDATE osm_aerodrome_label_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM aerodrome_label.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); + + UPDATE osm_aerodrome_label_point + SET aerodrome_type= + CASE + %%FIELD_MAPPING: class %% + ELSE 'other' END + WHERE (full_update OR osm_id IN (SELECT osm_id FROM aerodrome_label.osm_ids)) + AND aerodrome_type != + CASE + %%FIELD_MAPPING: class %% + ELSE 'other' END; +$$ LANGUAGE SQL; + +SELECT update_aerodrome_label_point(true); + +-- Handle updates + +CREATE OR REPLACE FUNCTION aerodrome_label.store() RETURNS trigger AS +$$ +BEGIN + 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; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS aerodrome_label.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION aerodrome_label.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO aerodrome_label.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION aerodrome_label.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh aerodrome_label'; + PERFORM update_aerodrome_label_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM aerodrome_label.osm_ids; + -- noinspection SqlWithoutWhere + DELETE FROM aerodrome_label.updates; + + RAISE LOG 'Refresh aerodrome_label done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_aerodrome_label_point + FOR EACH ROW +EXECUTE PROCEDURE aerodrome_label.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE + ON osm_aerodrome_label_point + FOR EACH STATEMENT +EXECUTE PROCEDURE aerodrome_label.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON aerodrome_label.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE aerodrome_label.refresh(); diff --git a/layers/aeroway/aeroway.sql b/layers/aeroway/aeroway.sql new file mode 100644 index 000000000..0c3952a7e --- /dev/null +++ b/layers/aeroway/aeroway.sql @@ -0,0 +1,70 @@ +-- etldoc: layer_aeroway[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_aeroway | z10| z11| z12| z13| z14+" ]; + +CREATE OR REPLACE FUNCTION layer_aeroway(bbox geometry, zoom_level int) + RETURNS TABLE + ( + geometry geometry, + class text, + ref text + ) +AS +$$ +SELECT geometry, aeroway AS class, ref +FROM ( + -- etldoc: osm_aeroway_linestring_gen_z10 -> layer_aeroway:z10 + SELECT geometry, aeroway, ref + FROM osm_aeroway_linestring_gen_z10 + WHERE zoom_level = 10 + UNION ALL + -- etldoc: osm_aeroway_linestring_gen_z11 -> layer_aeroway:z11 + SELECT geometry, aeroway, ref + FROM osm_aeroway_linestring_gen_z11 + WHERE zoom_level = 11 + UNION ALL + -- etldoc: osm_aeroway_linestring_gen_z12 -> layer_aeroway:z12 + SELECT geometry, aeroway, ref + FROM osm_aeroway_linestring_gen_z12 + WHERE zoom_level = 12 + UNION ALL + -- etldoc: osm_aeroway_linestring -> layer_aeroway:z13 + -- etldoc: osm_aeroway_linestring -> layer_aeroway:z14_ + SELECT geometry, aeroway, ref + FROM osm_aeroway_linestring + WHERE zoom_level >= 13 + UNION ALL + -- etldoc: osm_aeroway_polygon_gen_z10 -> layer_aeroway:z10 + SELECT geometry, aeroway, ref + FROM osm_aeroway_polygon_gen_z10 + WHERE zoom_level = 10 + UNION ALL + -- etldoc: osm_aeroway_polygon_gen_z11 -> layer_aeroway:z11 + SELECT geometry, aeroway, ref + FROM osm_aeroway_polygon_gen_z11 + WHERE zoom_level = 11 + UNION ALL + -- etldoc: osm_aeroway_polygon_gen_z12 -> layer_aeroway:z12 + SELECT geometry, aeroway, ref + FROM osm_aeroway_polygon_gen_z12 + WHERE zoom_level = 12 + UNION ALL + -- etldoc: osm_aeroway_polygon_gen_z13 -> layer_aeroway:z13 + SELECT geometry, aeroway, ref + FROM osm_aeroway_polygon_gen_z13 + WHERE zoom_level = 13 + UNION ALL + -- etldoc: osm_aeroway_polygon -> layer_aeroway:z14_ + SELECT geometry, aeroway, ref + FROM osm_aeroway_polygon + WHERE zoom_level >= 14 + UNION ALL + + -- etldoc: osm_aeroway_point -> layer_aeroway:z14_ + SELECT geometry, aeroway, ref + FROM osm_aeroway_point + WHERE zoom_level >= 14 + ) AS zoom_levels +WHERE geometry && bbox; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/aeroway/aeroway.yaml b/layers/aeroway/aeroway.yaml index 6ba7ab378..51cc2a7de 100644 --- a/layers/aeroway/aeroway.yaml +++ b/layers/aeroway/aeroway.yaml @@ -19,11 +19,12 @@ layer: - helipad - taxiway - apron + - gate datasource: geometry_field: geometry query: (SELECT geometry, ref, class FROM layer_aeroway(!bbox!, z(!scale_denominator!))) AS t schema: - - ./layer.sql + - ./aeroway.sql datasources: - type: imposm3 mapping_file: ./mapping.yaml diff --git a/layers/aeroway/etl_diagram.png b/layers/aeroway/etl_diagram.png index a895735f9..20e76223b 100644 Binary files a/layers/aeroway/etl_diagram.png and b/layers/aeroway/etl_diagram.png differ diff --git a/layers/aeroway/layer.sql b/layers/aeroway/layer.sql deleted file mode 100644 index c182a629b..000000000 --- a/layers/aeroway/layer.sql +++ /dev/null @@ -1,43 +0,0 @@ --- etldoc: layer_aeroway[shape=record fillcolor=lightpink, style="rounded,filled", --- etldoc: label="layer_aeroway | z10| z11| z12| z13| z14+" ]; - -CREATE OR REPLACE FUNCTION layer_aeroway(bbox geometry, zoom_level int) -RETURNS TABLE(geometry geometry, class text, ref text) AS $$ - SELECT geometry, aeroway AS class, ref FROM ( - -- etldoc: osm_aeroway_linestring_gen3 -> layer_aeroway:z10 - SELECT geometry, aeroway, ref - FROM osm_aeroway_linestring_gen3 WHERE zoom_level = 10 - UNION ALL - -- etldoc: osm_aeroway_linestring_gen2 -> layer_aeroway:z11 - SELECT geometry, aeroway, ref - FROM osm_aeroway_linestring_gen2 WHERE zoom_level = 11 - UNION ALL - -- etldoc: osm_aeroway_linestring_gen1 -> layer_aeroway:z12 - SELECT geometry, aeroway, ref - FROM osm_aeroway_linestring_gen1 WHERE zoom_level = 12 - UNION ALL - -- etldoc: osm_aeroway_linestring -> layer_aeroway:z13 - -- etldoc: osm_aeroway_linestring -> layer_aeroway:z14_ - SELECT geometry, aeroway, ref - FROM osm_aeroway_linestring WHERE zoom_level >= 13 - UNION ALL - - -- etldoc: osm_aeroway_polygon_gen3 -> layer_aeroway:z10 - -- etldoc: osm_aeroway_polygon_gen3 -> layer_aeroway:z11 - SELECT geometry, aeroway, ref - FROM osm_aeroway_polygon_gen3 WHERE zoom_level BETWEEN 10 AND 11 - UNION ALL - -- etldoc: osm_aeroway_polygon_gen2 -> layer_aeroway:z12 - SELECT geometry, aeroway, ref - FROM osm_aeroway_polygon_gen2 WHERE zoom_level = 12 - UNION ALL - -- etldoc: osm_aeroway_polygon_gen1 -> layer_aeroway:z13 - SELECT geometry, aeroway, ref - FROM osm_aeroway_polygon_gen1 WHERE zoom_level = 13 - UNION ALL - -- etldoc: osm_aeroway_polygon -> layer_aeroway:z14_ - SELECT geometry, aeroway, ref - FROM osm_aeroway_polygon WHERE zoom_level >= 14 - ) AS zoom_levels - WHERE geometry && bbox; -$$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/aeroway/mapping.yaml b/layers/aeroway/mapping.yaml index 7c90897d3..6e97819fc 100644 --- a/layers/aeroway/mapping.yaml +++ b/layers/aeroway/mapping.yaml @@ -1,34 +1,40 @@ generalized_tables: - # etldoc: imposm3 -> osm_aeroway_linestring_gen3 - aeroway_linestring_gen3: - source: aeroway_linestring_gen2 + # etldoc: osm_aeroway_linestring_gen_z11 -> osm_aeroway_linestring_gen_z10 + aeroway_linestring_gen_z10: + source: aeroway_linestring_gen_z11 tolerance: ZRES11 - # etldoc: imposm3 -> osm_aeroway_linestring_gen2 - aeroway_linestring_gen2: - source: aeroway_linestring_gen1 + # etldoc: osm_aeroway_linestring_gen_z12 -> osm_aeroway_linestring_gen_z11 + aeroway_linestring_gen_z11: + source: aeroway_linestring_gen_z12 tolerance: ZRES12 - # etldoc: imposm3 -> osm_aeroway_linestring_gen1 - aeroway_linestring_gen1: + # etldoc: osm_aeroway_linestring -> osm_aeroway_linestring_gen_z12 + aeroway_linestring_gen_z12: source: aeroway_linestring sql_filter: ST_IsValid(geometry) tolerance: ZRES13 - # etldoc: imposm3 -> osm_aeroway_polygon_gen3 - aeroway_polygon_gen3: - source: aeroway_polygon_gen2 + # etldoc: osm_aeroway_polygon_gen_z11 -> osm_aeroway_polygon_gen_z10 + aeroway_polygon_gen_z10: + source: aeroway_polygon_gen_z11 + sql_filter: area>power(ZRES9,2) + tolerance: ZRES10 + + # etldoc: osm_aeroway_polygon_gen_z12 -> osm_aeroway_polygon_gen_z11 + aeroway_polygon_gen_z11: + source: aeroway_polygon_gen_z12 sql_filter: area>power(ZRES10,2) tolerance: ZRES11 - # etldoc: imposm3 -> osm_aeroway_polygon_gen2 - aeroway_polygon_gen2: - source: aeroway_polygon_gen1 + # etldoc: osm_aeroway_polygon_gen_z13 -> osm_aeroway_polygon_gen_z12 + aeroway_polygon_gen_z12: + source: aeroway_polygon_gen_z13 sql_filter: area>power(ZRES11,2) tolerance: ZRES12 - # etldoc: imposm3 -> osm_aeroway_polygon_gen1 - aeroway_polygon_gen1: + # etldoc: osm_aeroway_polygon -> osm_aeroway_polygon_gen_z13 + aeroway_polygon_gen_z13: source: aeroway_polygon sql_filter: area>power(ZRES12,2) AND ST_IsValid(geometry) tolerance: ZRES13 @@ -50,7 +56,7 @@ tables: # etldoc: imposm3 -> osm_aeroway_polygon aeroway_polygon: type: polygon - fields: + columns: - *ref - name: osm_id type: id @@ -67,7 +73,7 @@ tables: # etldoc: imposm3 -> osm_aeroway_linestring aeroway_linestring: type: linestring - fields: + columns: - *ref - name: osm_id type: id @@ -80,3 +86,19 @@ tables: aeroway: - runway - taxiway + + # etldoc: imposm3 -> osm_aeroway_point + aeroway_point: + type: point + columns: + - *ref + - name: osm_id + type: id + - name: geometry + type: geometry + - name: aeroway + key: aeroway + type: string + mapping: + aeroway: + - gate \ No newline at end of file diff --git a/layers/aeroway/mapping_diagram.png b/layers/aeroway/mapping_diagram.png index ca19fec55..7536f47fd 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 000000000..86d8fbc3a --- /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": 182 + }, + { + "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": 183 + } + ] +} \ No newline at end of file diff --git a/layers/boundary/boundary.sql b/layers/boundary/boundary.sql index c4eec0ef4..fa73bd166 100644 --- a/layers/boundary/boundary.sql +++ b/layers/boundary/boundary.sql @@ -1,362 +1,730 @@ -CREATE OR REPLACE FUNCTION edit_name(name VARCHAR) RETURNS TEXT AS $$ - SELECT CASE - WHEN POSITION(' at ' in name) > 0 - THEN replace(SUBSTRING(name, POSITION(' at ' in name)+4), ' ', '') - ELSE replace(replace(name,' ',''),'Extentof','') - END; -$$ LANGUAGE SQL IMMUTABLE; - - --- etldoc: ne_110m_admin_0_boundary_lines_land -> boundary_z0 -CREATE OR REPLACE VIEW boundary_z0 AS ( - SELECT geometry, - 2 AS admin_level, - (CASE WHEN featurecla LIKE 'Disputed%' THEN true ELSE false END) AS disputed, - NULL::text AS disputed_name, - NULL::text AS claimed_by, - false AS maritime - FROM ne_110m_admin_0_boundary_lines_land -); - --- etldoc: ne_50m_admin_0_boundary_lines_land -> boundary_z1 --- etldoc: ne_50m_admin_1_states_provinces_lines -> boundary_z1 --- etldoc: osm_border_disp_linestring_gen11 -> boundary_z1 -CREATE OR REPLACE VIEW boundary_z1 AS ( - SELECT geometry, - 2 AS admin_level, - (CASE WHEN featurecla LIKE 'Disputed%' THEN true ELSE false END) AS disputed, - NULL AS disputed_name, - NULL AS claimed_by, - false AS maritime - FROM ne_50m_admin_0_boundary_lines_land - UNION ALL - SELECT geometry, - 4 AS admin_level, - false AS disputed, - NULL AS disputed_name, - NULL AS claimed_by, - false AS maritime - FROM ne_50m_admin_1_states_provinces_lines - UNION ALL - SELECT geometry, - admin_level, - true AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime - FROM osm_border_disp_linestring_gen11 -); - - --- etldoc: ne_50m_admin_0_boundary_lines_land -> boundary_z3 --- etldoc: ne_50m_admin_1_states_provinces_lines -> boundary_z3 --- etldoc: osm_border_disp_linestring_gen11 -> boundary_z3 -CREATE OR REPLACE VIEW boundary_z3 AS ( - SELECT geometry, - 2 AS admin_level, - (CASE WHEN featurecla LIKE 'Disputed%' THEN true ELSE false END) AS disputed, - NULL AS disputed_name, - NULL AS claimed_by, - false AS maritime - FROM ne_50m_admin_0_boundary_lines_land - UNION ALL - SELECT geometry, - 4 AS admin_level, - false AS disputed, - NULL AS disputed_name, - NULL AS claimed_by, - false AS maritime - FROM ne_50m_admin_1_states_provinces_lines - UNION ALL - SELECT geometry, - admin_level, - true AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime - FROM osm_border_disp_linestring_gen11 -); - - --- etldoc: ne_10m_admin_0_boundary_lines_land -> boundary_z4 --- etldoc: ne_10m_admin_1_states_provinces_lines -> boundary_z4 --- etldoc: osm_border_linestring_gen10 -> boundary_z4 --- etldoc: osm_border_disp_linestring_gen10 -> boundary_z4 -CREATE OR REPLACE VIEW boundary_z4 AS ( - SELECT geometry, - 2 AS admin_level, - (CASE WHEN featurecla LIKE 'Disputed%' THEN true ELSE false END) AS disputed, - NULL AS disputed_name, - NULL AS claimed_by, - false AS maritime - FROM ne_10m_admin_0_boundary_lines_land - WHERE featurecla <> 'Lease limit' - UNION ALL - SELECT geometry, - 4 AS admin_level, - false AS disputed, - NULL AS disputed_name, - NULL AS claimed_by, - false AS maritime - FROM ne_10m_admin_1_states_provinces_lines - WHERE min_zoom <= 5 - UNION ALL - SELECT geometry, - admin_level, - disputed, - NULL AS disputed_name, - NULL AS claimed_by, - maritime - FROM osm_border_linestring_gen10 - WHERE maritime=true AND admin_level <= 2 - UNION ALL - SELECT geometry, - admin_level, - true AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime - FROM osm_border_disp_linestring_gen10 -); - --- etldoc: osm_border_linestring_gen9 -> boundary_z5 --- etldoc: osm_border_disp_linestring_gen9 -> boundary_z5 -CREATE OR REPLACE VIEW boundary_z5 AS ( - SELECT geometry, - admin_level, - disputed, - NULL AS disputed_name, - NULL AS claimed_by, - maritime - FROM osm_border_linestring_gen9 - WHERE admin_level <= 4 - AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen9) - UNION ALL - SELECT geometry, - admin_level, - true AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime - FROM osm_border_disp_linestring_gen9 -); - --- etldoc: osm_border_linestring_gen8 -> boundary_z6 --- etldoc: osm_border_disp_linestring_gen8 -> boundary_z6 -CREATE OR REPLACE VIEW boundary_z6 AS ( - SELECT geometry, - admin_level, - disputed, - NULL AS disputed_name, - NULL AS claimed_by, - maritime - FROM osm_border_linestring_gen8 - WHERE admin_level <= 4 - AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen8) - UNION ALL - SELECT geometry, - admin_level, - true AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime - FROM osm_border_disp_linestring_gen8 -); - --- etldoc: osm_border_linestring_gen7 -> boundary_z7 --- etldoc: osm_border_disp_linestring_gen7 -> boundary_z7 -CREATE OR REPLACE VIEW boundary_z7 AS ( - SELECT geometry, - admin_level, - disputed, - NULL AS disputed_name, - NULL AS claimed_by, - maritime - FROM osm_border_linestring_gen7 - WHERE admin_level <= 4 - AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen7) - UNION ALL - SELECT geometry, - admin_level, - true AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime - FROM osm_border_disp_linestring_gen7 -); - --- etldoc: osm_border_linestring_gen6 -> boundary_z8 --- etldoc: osm_border_disp_linestring_gen6 -> boundary_z8 -CREATE OR REPLACE VIEW boundary_z8 AS ( - SELECT geometry, - admin_level, - disputed, - NULL AS disputed_name, - NULL AS claimed_by, - maritime - FROM osm_border_linestring_gen6 - WHERE admin_level <= 4 - AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen6) - UNION ALL - SELECT geometry, - admin_level, - true AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime - FROM osm_border_disp_linestring_gen6 -); - --- etldoc: osm_border_linestring_gen5 -> boundary_z9 --- etldoc: osm_border_disp_linestring_gen5 -> boundary_z9 -CREATE OR REPLACE VIEW boundary_z9 AS ( - SELECT geometry, - admin_level, - disputed, - NULL AS disputed_name, - NULL AS claimed_by, - maritime - FROM osm_border_linestring_gen5 - WHERE admin_level <= 6 - AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen5) - UNION ALL - SELECT geometry, - admin_level, - true AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime - FROM osm_border_disp_linestring_gen5 -); - --- etldoc: osm_border_linestring_gen4 -> boundary_z10 --- etldoc: osm_border_disp_linestring_gen4 -> boundary_z10 -CREATE OR REPLACE VIEW boundary_z10 AS ( - SELECT geometry, - admin_level, - disputed, - NULL AS disputed_name, - NULL AS claimed_by, - maritime - FROM osm_border_linestring_gen4 - WHERE admin_level <= 6 - AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen4) - UNION ALL - SELECT geometry, - admin_level, - true AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime - FROM osm_border_disp_linestring_gen4 -); - --- etldoc: osm_border_linestring_gen3 -> boundary_z11 --- etldoc: osm_border_disp_linestring_gen3 -> boundary_z11 -CREATE OR REPLACE VIEW boundary_z11 AS ( - SELECT geometry, - admin_level, - disputed, - NULL AS disputed_name, - NULL AS claimed_by, - maritime - FROM osm_border_linestring_gen3 - WHERE admin_level <= 8 - AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen3) - UNION ALL - SELECT geometry, - admin_level, - true AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime - FROM osm_border_disp_linestring_gen3 -); - --- etldoc: osm_border_linestring_gen2 -> boundary_z12 --- etldoc: osm_border_disp_linestring_gen2 -> boundary_z12 -CREATE OR REPLACE VIEW boundary_z12 AS ( - SELECT geometry, - admin_level, - disputed, - NULL AS disputed_name, - NULL AS claimed_by, - maritime - FROM osm_border_linestring_gen2 - WHERE osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen2) - UNION ALL - SELECT geometry, - admin_level, - true AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime - FROM osm_border_disp_linestring_gen2 -); - --- etldoc: osm_border_linestring_gen1 -> boundary_z13 --- etldoc: osm_border_disp_linestring_gen1 -> boundary_z13 -CREATE OR REPLACE VIEW boundary_z13 AS ( - SELECT geometry, - admin_level, - disputed, - NULL AS disputed_name, - NULL AS claimed_by, - maritime - FROM osm_border_linestring_gen1 - WHERE osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring_gen1) - UNION ALL - SELECT geometry, - admin_level, - true AS disputed, - edit_name(name) AS disputed_name, - claimed_by, - maritime - FROM osm_border_disp_linestring_gen1 -); +-- etldoc: osm_border_linestring -> osm_border_linestring_gen_z13 +-- etldoc: osm_border_linestring_adm -> osm_border_linestring_gen_z13 +-- etldoc: osm_border_disp_linestring -> osm_border_linestring_gen_z13 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z13 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z13 AS +( +SELECT ST_Simplify(ST_Collect(geometry), ZRes(14)) AS geometry, + MAX(adm0_l) AS adm0_l, + MAX(adm0_r) AS adm0_r, + MIN(admin_level) AS admin_level, + BOOL_OR(disputed) AS disputed, + MAX(name) AS name, + MAX(claimed_by) AS claimed_by, + BOOL_OR(maritime) AS maritime +FROM ( + -- All admin 3-10 boundaries + SELECT osm_id, + geometry, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + MIN(admin_level) AS admin_level, + BOOL_OR(disputed) + OR BOOL_OR(dispute) + OR BOOL_OR(border_status = 'disputed') + OR BOOL_OR(disputed_by <> '') AS disputed, + NULLIF(name, '') AS name, + NULLIF(claimed_by, '') AS claimed_by, + BOOL_OR(maritime) AS maritime + FROM osm_border_linestring + WHERE admin_level BETWEEN 3 AND 10 + AND type = 1 -- ways only + GROUP BY osm_id, geometry, name, claimed_by + + UNION ALL + + -- All non-disputed admin 2 boundaries + SELECT osm_id, + geometry, + adm0_l, + adm0_r, + admin_level, + FALSE AS disputed, + NULL::text AS name, + NULL::text AS claimed_by, + maritime + FROM osm_border_linestring_adm + + UNION ALL + + -- All disputed admin 2 boundaries + SELECT osm_id, + geometry, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + 2::int AS admin_level, + TRUE AS disputed, + NULLIF(name, '') AS name, + NULLIF(claimed_by, '') AS claimed_by, + maritime + FROM osm_border_disp_linestring + GROUP BY osm_id, geometry, name, claimed_by, maritime + ) AS merged_boundary +GROUP by osm_id +)/* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z13_idx ON osm_border_linestring_gen_z13 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z13 -> osm_border_linestring_gen_z12 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z12 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z12 AS +( +SELECT ST_Simplify(geometry, ZRes(13)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z13 +WHERE admin_level BETWEEN 2 AND 10 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z12_idx ON osm_border_linestring_gen_z12 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z12 -> osm_border_linestring_gen_z11 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z11 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z11 AS +( +SELECT ST_Simplify(geometry, ZRes(12)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z12 +WHERE admin_level BETWEEN 2 AND 8 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z11_idx ON osm_border_linestring_gen_z11 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z11 -> osm_border_linestring_gen_z10 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z10 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z10 AS +( +SELECT ST_Simplify(geometry, ZRes(11)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z11 +WHERE admin_level BETWEEN 2 AND 6 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z10_idx ON osm_border_linestring_gen_z10 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z10 -> osm_border_linestring_gen_z9 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z9 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z9 AS +( +SELECT ST_Simplify(geometry, ZRes(10)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z10 +-- WHERE admin_level BETWEEN 2 AND 6 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z9_idx ON osm_border_linestring_gen_z9 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z9 -> osm_border_linestring_gen_z8 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z8 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z8 AS +( +SELECT ST_Simplify(geometry, ZRes(9)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z9 +WHERE admin_level BETWEEN 2 AND 4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z8_idx ON osm_border_linestring_gen_z8 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z8 -> osm_border_linestring_gen_z7 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z7 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z7 AS +( +SELECT ST_Simplify(geometry, ZRes(8)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z8 +-- WHERE admin_level BETWEEN 2 AND 4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z7_idx ON osm_border_linestring_gen_z7 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z7 -> osm_border_linestring_gen_z6 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z6 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z6 AS +( +SELECT ST_Simplify(geometry, ZRes(7)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z7 +-- WHERE admin_level BETWEEN 2 AND 4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z6_idx ON osm_border_linestring_gen_z6 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z6 -> osm_border_linestring_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z5 AS +( +SELECT ST_Simplify(geometry, ZRes(6)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z6 +-- WHERE admin_level BETWEEN 2 AND 4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_border_linestring_gen_z5_idx ON osm_border_linestring_gen_z5 USING gist (geometry); + +-- etldoc: osm_border_linestring_gen_z5 -> osm_border_linestring_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS osm_border_linestring_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW osm_border_linestring_gen_z4 AS +( +SELECT ST_Simplify(geometry, ZRes(5)) AS geometry, adm0_l, adm0_r, admin_level, disputed, name, claimed_by, maritime +FROM osm_border_linestring_gen_z5 +WHERE admin_level = 2 AND (maritime 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_z2_idx ON osm_border_disp_linestring_gen_z2 USING gist (geometry); + +-- ne_10m_admin_0_boundary_lines_land +-- etldoc: ne_10m_admin_0_boundary_lines_land -> ne_10m_admin_0_boundary_lines_land_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_admin_0_boundary_lines_land_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_admin_0_boundary_lines_land_gen_z4 AS +( +SELECT ST_Simplify(geometry, ZRes(6)) as geometry, + 2 AS admin_level, + (CASE WHEN featurecla LIKE 'Disputed%' THEN TRUE ELSE FALSE END) AS disputed, + (CASE WHEN featurecla LIKE 'Disputed%' THEN 'ne10m_' || ogc_fid ELSE NULL::text END) AS disputed_name, + NULL::text AS claimed_by, + FALSE AS maritime +FROM ne_10m_admin_0_boundary_lines_land +WHERE featurecla <> 'Lease limit' + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_admin_0_boundary_lines_land_gen_z4_idx ON ne_10m_admin_0_boundary_lines_land_gen_z4 USING gist (geometry); + +-- ne_10m_admin_1_states_provinces_lines +-- etldoc: ne_10m_admin_1_states_provinces_lines -> ne_10m_admin_1_states_provinces_lines_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_admin_1_states_provinces_lines_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_admin_1_states_provinces_lines_gen_z4 AS +( +SELECT ST_Simplify(geometry, ZRes(6)) as geometry, + 4 AS admin_level, + FALSE AS disputed, + NULL::text AS disputed_name, + NULL::text AS claimed_by, + FALSE AS maritime, + min_zoom +FROM ne_10m_admin_1_states_provinces_lines +WHERE min_zoom <= 7.7 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_admin_1_states_provinces_lines_gen_z4_idx ON ne_10m_admin_1_states_provinces_lines_gen_z4 USING gist (geometry); + + +-- etldoc: ne_10m_admin_1_states_provinces_lines_gen_z4 -> ne_10m_admin_1_states_provinces_lines_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_admin_1_states_provinces_lines_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_admin_1_states_provinces_lines_gen_z3 AS +( +SELECT ST_Simplify(geometry, ZRes(5)) as geometry, + admin_level, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z4 +WHERE min_zoom <= 7 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_admin_1_states_provinces_lines_gen_z3_idx ON ne_10m_admin_1_states_provinces_lines_gen_z3 USING gist (geometry); + +-- etldoc: ne_10m_admin_1_states_provinces_lines_gen_z3 -> ne_10m_admin_1_states_provinces_lines_gen_z2 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_admin_1_states_provinces_lines_gen_z2 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_admin_1_states_provinces_lines_gen_z2 AS +( +SELECT ST_Simplify(geometry, ZRes(4)) as geometry, + admin_level, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z3 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_admin_1_states_provinces_lines_gen_z2_idx ON ne_10m_admin_1_states_provinces_lines_gen_z2 USING gist (geometry); + +-- etldoc: ne_10m_admin_1_states_provinces_lines_gen_z2 -> ne_10m_admin_1_states_provinces_lines_gen_z1 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_admin_1_states_provinces_lines_gen_z1 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_admin_1_states_provinces_lines_gen_z1 AS +( +SELECT ST_Simplify(geometry, ZRes(3)) as geometry, + admin_level, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z2 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_admin_1_states_provinces_lines_gen_z1_idx ON ne_10m_admin_1_states_provinces_lines_gen_z1 USING gist (geometry); + +-- ne_50m_admin_0_boundary_lines_land +-- etldoc: ne_50m_admin_0_boundary_lines_land -> ne_50m_admin_0_boundary_lines_land_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_admin_0_boundary_lines_land_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_admin_0_boundary_lines_land_gen_z3 AS +( +SELECT ST_Simplify(geometry, ZRes(5)) as geometry, + 2 AS admin_level, + (CASE WHEN featurecla LIKE 'Disputed%' THEN TRUE ELSE FALSE END) AS disputed, + (CASE WHEN featurecla LIKE 'Disputed%' THEN 'ne50m_' || ogc_fid ELSE NULL::text END) AS disputed_name, + NULL::text AS claimed_by, + FALSE AS maritime +FROM ne_50m_admin_0_boundary_lines_land + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_admin_0_boundary_lines_land_gen_z3_idx ON ne_50m_admin_0_boundary_lines_land_gen_z3 USING gist (geometry); + +-- etldoc: ne_50m_admin_0_boundary_lines_land_gen_z3 -> ne_50m_admin_0_boundary_lines_land_gen_z2 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_admin_0_boundary_lines_land_gen_z2 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_admin_0_boundary_lines_land_gen_z2 AS +( +SELECT ST_Simplify(geometry, ZRes(4)) as geometry, + admin_level, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_50m_admin_0_boundary_lines_land_gen_z3 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_admin_0_boundary_lines_land_gen_z2_idx ON ne_50m_admin_0_boundary_lines_land_gen_z2 USING gist (geometry); + +-- etldoc: ne_50m_admin_0_boundary_lines_land_gen_z2 -> ne_50m_admin_0_boundary_lines_land_gen_z1 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_admin_0_boundary_lines_land_gen_z1 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_admin_0_boundary_lines_land_gen_z1 AS +( +SELECT ST_Simplify(geometry, ZRes(3)) as geometry, + admin_level, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_50m_admin_0_boundary_lines_land_gen_z2 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_admin_0_boundary_lines_land_gen_z1_idx ON ne_50m_admin_0_boundary_lines_land_gen_z1 USING gist (geometry); + +-- ne_110m_admin_0_boundary_lines_land +-- etldoc: ne_110m_admin_0_boundary_lines_land -> ne_110m_admin_0_boundary_lines_land_gen_z0 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_admin_0_boundary_lines_land_gen_z0 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_admin_0_boundary_lines_land_gen_z0 AS +( +SELECT ST_Simplify(geometry, ZRes(2)) as geometry, + 2 AS admin_level, + (CASE WHEN featurecla LIKE 'Disputed%' THEN TRUE ELSE FALSE END) AS disputed, + (CASE WHEN featurecla LIKE 'Disputed%' THEN 'ne110m_' || ogc_fid ELSE NULL::text END) AS disputed_name, + NULL::text AS claimed_by, + FALSE AS maritime +FROM ne_110m_admin_0_boundary_lines_land + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_admin_0_boundary_lines_land_gen_z0_idx ON ne_110m_admin_0_boundary_lines_land_gen_z0 USING gist (geometry); + + +CREATE OR REPLACE FUNCTION edit_name(name varchar) RETURNS text AS +$$ +SELECT CASE + WHEN POSITION(' at ' IN name) > 0 + THEN replace(SUBSTRING(name, POSITION(' at ' IN name) + 4), ' ', '') + ELSE replace(replace(name, ' ', ''), 'Extentof', '') + END; +$$ LANGUAGE SQL IMMUTABLE + -- STRICT + PARALLEL SAFE + ; + + +-- etldoc: ne_110m_admin_0_boundary_lines_land_gen_z0 -> boundary_z0 +CREATE OR REPLACE VIEW boundary_z0 AS +( +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_110m_admin_0_boundary_lines_land_gen_z0 + ); + +-- etldoc: ne_50m_admin_0_boundary_lines_land_gen_z1 -> boundary_z1 +-- etldoc: ne_10m_admin_1_states_provinces_lines_gen_z1 -> boundary_z1 +-- etldoc: osm_border_disp_linestring_gen_z1 -> boundary_z1 +CREATE OR REPLACE VIEW boundary_z1 AS +( +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_50m_admin_0_boundary_lines_land_gen_z1 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z1 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + TRUE AS disputed, + edit_name(name) AS disputed_name, + claimed_by, + maritime +FROM osm_border_disp_linestring_gen_z1 + ); + + +-- etldoc: ne_50m_admin_0_boundary_lines_land_gen_z2 -> boundary_z2 +-- etldoc: ne_10m_admin_1_states_provinces_lines_gen_z2 -> boundary_z2 +-- etldoc: osm_border_disp_linestring_gen_z2 -> boundary_z2 +CREATE OR REPLACE VIEW boundary_z2 AS +( +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_50m_admin_0_boundary_lines_land_gen_z2 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z2 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + TRUE AS disputed, + edit_name(name) AS disputed_name, + claimed_by, + maritime +FROM osm_border_disp_linestring_gen_z2 + ); + +-- etldoc: ne_50m_admin_0_boundary_lines_land_gen_z3 -> boundary_z3 +-- etldoc: ne_10m_admin_1_states_provinces_lines_gen_z3 -> boundary_z3 +-- etldoc: osm_border_disp_linestring_gen_z3 -> boundary_z3 +CREATE OR REPLACE VIEW boundary_z3 AS +( +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_50m_admin_0_boundary_lines_land_gen_z3 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z3 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + TRUE AS disputed, + edit_name(name) AS disputed_name, + claimed_by, + maritime +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 +CREATE OR REPLACE VIEW boundary_z4 AS +( +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_0_boundary_lines_land_gen_z4 +UNION ALL +SELECT geometry, + admin_level, + NULL::text AS adm0_l, + NULL::text AS adm0_r, + disputed, + disputed_name, + claimed_by, + maritime +FROM ne_10m_admin_1_states_provinces_lines_gen_z4 +UNION ALL +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z4 + ); + +-- etldoc: osm_border_linestring_gen_z5 -> boundary_z5 +CREATE OR REPLACE VIEW boundary_z5 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z5 +WHERE admin_level <= 4 + ); + +-- etldoc: osm_border_linestring_gen_z6 -> boundary_z6 +CREATE OR REPLACE VIEW boundary_z6 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z6 +WHERE admin_level <= 4 + ); + +-- etldoc: osm_border_linestring_gen_z7 -> boundary_z7 +CREATE OR REPLACE VIEW boundary_z7 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z7 +WHERE admin_level <= 6 + ); + +-- etldoc: osm_border_linestring_gen_z8 -> boundary_z8 +CREATE OR REPLACE VIEW boundary_z8 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z8 +WHERE admin_level <= 6 + ); + +-- etldoc: osm_border_linestring_gen_z9 -> boundary_z9 +CREATE OR REPLACE VIEW boundary_z9 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z9 +WHERE admin_level <= 6 + ); + +-- etldoc: osm_border_linestring_gen_z10 -> boundary_z10 +CREATE OR REPLACE VIEW boundary_z10 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z10 +WHERE admin_level <= 6 + ); + +-- etldoc: osm_border_linestring_gen_z11 -> boundary_z11 +CREATE OR REPLACE VIEW boundary_z11 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z11 +WHERE admin_level <= 8 + ); + +-- etldoc: osm_border_linestring_gen_z12 -> boundary_z12 +CREATE OR REPLACE VIEW boundary_z12 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z12 + ); + +-- etldoc: osm_border_linestring_gen_z13 -> boundary_z13 +CREATE OR REPLACE VIEW boundary_z13 AS +( +SELECT geometry, + admin_level, + adm0_l, + adm0_r, + disputed, + CASE WHEN disputed THEN edit_name(name) END AS disputed_name, + claimed_by, + maritime +FROM osm_border_linestring_gen_z13 + ); -- etldoc: layer_boundary[shape=record fillcolor=lightpink, style="rounded,filled", --- etldoc: label=" layer_boundary | z0 | z1_2 | z3 | z4 | z5 | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13+"] -CREATE OR REPLACE FUNCTION layer_boundary (bbox geometry, zoom_level int) -RETURNS TABLE(geometry geometry, admin_level int, disputed int, disputed_name text, claimed_by text, maritime int) AS $$ - SELECT geometry, admin_level, disputed::int, disputed_name, claimed_by, maritime::int FROM ( - -- etldoc: boundary_z0 -> layer_boundary:z0 - SELECT * FROM boundary_z0 WHERE geometry && bbox AND zoom_level = 0 - UNION ALL - -- etldoc: boundary_z1 -> layer_boundary:z1_2 - SELECT * FROM boundary_z1 WHERE geometry && bbox AND zoom_level BETWEEN 1 AND 2 - UNION ALL - -- etldoc: boundary_z3 -> layer_boundary:z3 - SELECT * FROM boundary_z3 WHERE geometry && bbox AND zoom_level = 3 - UNION ALL - -- etldoc: boundary_z4 -> layer_boundary:z4 - SELECT * FROM boundary_z4 WHERE geometry && bbox AND zoom_level = 4 - UNION ALL - -- etldoc: boundary_z5 -> layer_boundary:z5 - SELECT * FROM boundary_z5 WHERE geometry && bbox AND zoom_level = 5 - UNION ALL - -- etldoc: boundary_z6 -> layer_boundary:z6 - SELECT * FROM boundary_z6 WHERE geometry && bbox AND zoom_level = 6 - UNION ALL - -- etldoc: boundary_z7 -> layer_boundary:z7 - SELECT * FROM boundary_z7 WHERE geometry && bbox AND zoom_level = 7 - UNION ALL - -- etldoc: boundary_z8 -> layer_boundary:z8 - SELECT * FROM boundary_z8 WHERE geometry && bbox AND zoom_level = 8 - UNION ALL - -- etldoc: boundary_z9 -> layer_boundary:z9 - SELECT * FROM boundary_z9 WHERE geometry && bbox AND zoom_level = 9 - UNION ALL - -- etldoc: boundary_z10 -> layer_boundary:z10 - SELECT * FROM boundary_z10 WHERE geometry && bbox AND zoom_level = 10 - UNION ALL - -- etldoc: boundary_z11 -> layer_boundary:z11 - SELECT * FROM boundary_z11 WHERE geometry && bbox AND zoom_level = 11 - UNION ALL - -- etldoc: boundary_z12 -> layer_boundary:z12 - SELECT * FROM boundary_z12 WHERE geometry && bbox AND zoom_level = 12 - UNION ALL - -- etldoc: boundary_z13 -> layer_boundary:z13 - SELECT * FROM boundary_z13 WHERE geometry && bbox AND zoom_level >= 13 - ) AS zoom_levels; -$$ LANGUAGE SQL IMMUTABLE; +-- etldoc: label=" layer_boundary | z0 | z1 | z2 | z3 | z4 | z5 | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13+"] +CREATE OR REPLACE FUNCTION layer_boundary(bbox geometry, zoom_level int) + RETURNS TABLE + ( + geometry geometry, + admin_level int, + adm0_l text, + adm0_r text, + disputed int, + disputed_name text, + claimed_by text, + maritime int + ) +AS +$$ +SELECT geometry, admin_level, adm0_l, adm0_r, disputed::int, disputed_name, claimed_by, maritime::int +FROM ( + -- etldoc: boundary_z0 -> layer_boundary:z0 + SELECT * + FROM boundary_z0 + WHERE geometry && bbox + AND zoom_level = 0 + UNION ALL + -- etldoc: boundary_z1 -> layer_boundary:z1 + SELECT * + FROM boundary_z1 + WHERE geometry && bbox + AND zoom_level = 1 + UNION ALL + -- etldoc: boundary_z2 -> layer_boundary:z2 + SELECT * + FROM boundary_z2 + WHERE geometry && bbox + AND zoom_level = 2 + UNION ALL + -- etldoc: boundary_z3 -> layer_boundary:z3 + SELECT * + FROM boundary_z3 + WHERE geometry && bbox + AND zoom_level = 3 + UNION ALL + -- etldoc: boundary_z4 -> layer_boundary:z4 + SELECT * + FROM boundary_z4 + WHERE geometry && bbox + AND zoom_level = 4 + UNION ALL + -- etldoc: boundary_z5 -> layer_boundary:z5 + SELECT * + FROM boundary_z5 + WHERE geometry && bbox + AND zoom_level = 5 + UNION ALL + -- etldoc: boundary_z6 -> layer_boundary:z6 + SELECT * + FROM boundary_z6 + WHERE geometry && bbox + AND zoom_level = 6 + UNION ALL + -- etldoc: boundary_z7 -> layer_boundary:z7 + SELECT * + FROM boundary_z7 + WHERE geometry && bbox + AND zoom_level = 7 + UNION ALL + -- etldoc: boundary_z8 -> layer_boundary:z8 + SELECT * + FROM boundary_z8 + WHERE geometry && bbox + AND zoom_level = 8 + UNION ALL + -- etldoc: boundary_z9 -> layer_boundary:z9 + SELECT * + FROM boundary_z9 + WHERE geometry && bbox + AND zoom_level = 9 + UNION ALL + -- etldoc: boundary_z10 -> layer_boundary:z10 + SELECT * + FROM boundary_z10 + WHERE geometry && bbox + AND zoom_level = 10 + UNION ALL + -- etldoc: boundary_z11 -> layer_boundary:z11 + SELECT * + FROM boundary_z11 + WHERE geometry && bbox + AND zoom_level = 11 + UNION ALL + -- etldoc: boundary_z12 -> layer_boundary:z12 + SELECT * + FROM boundary_z12 + WHERE geometry && bbox + AND zoom_level = 12 + UNION ALL + -- etldoc: boundary_z13 -> layer_boundary:z13 + SELECT * + FROM boundary_z13 + WHERE geometry && bbox + AND zoom_level >= 13 + ) AS zoom_levels; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/boundary/boundary.yaml b/layers/boundary/boundary.yaml index 4bfbbeba9..78f895501 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 @@ -14,6 +22,10 @@ layer: The `admin_level` corresponds to the lowest `admin_level` the line participates in. At low zoom levels the Natural Earth boundaries are mapped to the equivalent admin levels. + adm0_l: | + State name on the left of the border. For country boundaries only (`admin_level = 2`). + adm0_r: | + State name on the right of the border. For country boundaries only (`admin_level = 2`). disputed: description: | Mark with `1` if the border is disputed. @@ -46,8 +58,9 @@ layer: buffer_size: 4 datasource: geometry_field: geometry - query: (SELECT geometry, admin_level, disputed, disputed_name, claimed_by, maritime FROM layer_boundary(!bbox!, z(!scale_denominator!))) AS t + query: (SELECT geometry, admin_level, adm0_l, adm0_r, disputed, disputed_name, claimed_by, maritime FROM layer_boundary(!bbox!, z(!scale_denominator!))) AS t schema: + - ./boundary_name.sql - ./boundary.sql datasources: - type: imposm3 diff --git a/layers/boundary/boundary_name.sql b/layers/boundary/boundary_name.sql new file mode 100644 index 000000000..4be7dac1f --- /dev/null +++ b/layers/boundary/boundary_name.sql @@ -0,0 +1,105 @@ +DROP TABLE IF EXISTS osm_border_linestring_adm CASCADE; + +-- etldoc: osm_border_linestring -> osm_border_linestring_adm +-- etldoc: osm_border_disp_linestring -> osm_border_linestring_adm +-- etldoc: ne_10m_admin_0_countries -> osm_border_linestring_adm +CREATE TABLE IF NOT EXISTS osm_border_linestring_adm AS ( + WITH + -- Prepare lines from osm to be merged + multiline AS ( + SELECT osm_id, + ST_Node(ST_Collect(geometry)) AS geometry, + BOOL_OR(maritime) AS maritime, + FALSE AS disputed + FROM osm_border_linestring + WHERE admin_level = 2 AND ST_Dimension(geometry) = 1 + AND osm_id NOT IN (SELECT DISTINCT osm_id FROM osm_border_disp_linestring) + GROUP BY osm_id + ), + + mergedline AS ( + SELECT osm_id, + (ST_Dump(ST_LineMerge(geometry))).geom AS geometry, + maritime, + disputed + FROM multiline + ), + -- Create polygons from all boundaries to preserve real shape of country + polyg AS ( + SELECT (ST_Dump( + ST_Polygonize(geometry))).geom AS geometry + FROM ( + SELECT (ST_Dump( + ST_LineMerge(geometry))).geom AS geometry + FROM (SELECT ST_Node( + ST_Collect(geometry)) AS geometry + FROM osm_border_linestring + WHERE admin_level = 2 AND ST_Dimension(geometry) = 1 + ) nodes + ) linemerge + ), + + centroids AS ( + SELECT polyg.geometry, + ne.adm0_a3 + FROM polyg, + ne_10m_admin_0_countries AS ne + WHERE ST_Within( + ST_PointOnSurface(polyg.geometry), ne.geometry) + ), + + country_osm_polyg AS ( + SELECT country.adm0_a3, + border.geometry + FROM polyg border, + centroids country + WHERE ST_Within(country.geometry, border.geometry) + ), + + rights AS ( + SELECT osm_id, + adm0_r, + geometry, + maritime, + disputed + FROM ( + SELECT a.osm_id AS osm_id, + b.adm0_a3 AS adm0_r, + a.geometry, + a.maritime, + a.disputed + FROM mergedline AS a + LEFT JOIN country_osm_polyg AS b + -- Create short line on the right of the boundary (mergedline) and find state where line lies. + ON ST_Within( + ST_OffsetCurve( + (ST_LineSubString(a.geometry, 0.3,0.3004)), 70, 'quad_segs=4 join=mitre'), b.geometry) + ) line_rights + ) + + SELECT osm_id, + adm0_l, + adm0_r, + geometry, + maritime, + 2::integer AS admin_level, + disputed + FROM ( + SELECT r.osm_id AS osm_id, + b.adm0_a3 AS adm0_l, + r.adm0_r AS adm0_r, + r.geometry, + r.maritime, + r.disputed + FROM rights AS r + LEFT JOIN country_osm_polyg AS b + -- Create short line on the left of the boundary (mergedline) and find state where line lies. + ON ST_Within( + ST_OffsetCurve( + (ST_LineSubString(r.geometry, 0.4,0.4004)), -70, 'quad_segs=4 join=mitre'), b.geometry) + ) both_lines +); + +CREATE INDEX IF NOT EXISTS osm_border_linestring_adm_geom_idx + ON osm_border_linestring_adm + USING GIST (geometry); diff --git a/layers/boundary/etl_diagram.png b/layers/boundary/etl_diagram.png index c47c5bb80..f45adcfd1 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 9b3e67b3f..45b88662b 100644 --- a/layers/boundary/mapping.yaml +++ b/layers/boundary/mapping.yaml @@ -1,95 +1,34 @@ generalized_tables: - - # etldoc: osm_border_disp_linestring -> osm_border_disp_linestring_gen11 - border_disp_linestring_gen11: - source: border_disp_linestring - sql_filter: admin_level = 2 - tolerance: 9600 - - # etldoc: osm_border_disp_linestring -> osm_border_disp_linestring_gen10 - border_disp_linestring_gen10: - source: border_disp_linestring - sql_filter: admin_level = 2 - tolerance: 4800 - - # etldoc: osm_border_disp_linestring -> osm_border_disp_linestring_gen9 - border_disp_linestring_gen9: - source: border_disp_linestring - sql_filter: admin_level = 2 - tolerance: 2400 - - # etldoc: osm_border_disp_linestring -> osm_border_disp_linestring_gen8 - border_disp_linestring_gen8: - source: border_disp_linestring - sql_filter: admin_level = 2 - tolerance: 1200 - - # etldoc: osm_border_disp_linestring -> osm_border_disp_linestring_gen7 - border_disp_linestring_gen7: - source: border_disp_linestring - sql_filter: admin_level = 2 - tolerance: 600 - - # etldoc: osm_border_disp_linestring -> osm_border_disp_linestring_gen6 - border_disp_linestring_gen6: - source: border_disp_linestring - sql_filter: admin_level = 2 - tolerance: 300 - - # etldoc: osm_border_disp_linestring -> osm_border_disp_linestring_gen5 - border_disp_linestring_gen5: - source: border_disp_linestring - sql_filter: admin_level = 2 - tolerance: 160 - - # etldoc: osm_border_disp_linestring -> osm_border_disp_linestring_gen4 - border_disp_linestring_gen4: - source: border_disp_linestring - sql_filter: admin_level = 2 - tolerance: 80 - - # etldoc: osm_border_disp_linestring -> osm_border_disp_linestring_gen3 - border_disp_linestring_gen3: - source: border_disp_linestring - sql_filter: admin_level = 2 - tolerance: 40 - - # etldoc: osm_border_disp_linestring -> osm_border_disp_linestring_gen2 - border_disp_linestring_gen2: - source: border_disp_linestring - sql_filter: admin_level = 2 - tolerance: 20 - - # etldoc: osm_border_disp_linestring -> osm_border_disp_linestring_gen1 - border_disp_linestring_gen1: - source: border_disp_linestring - sql_filter: admin_level = 2 - tolerance: 10 - - # 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 - fields: + 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 @@ -99,15 +38,36 @@ 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 from_member: true + - key: boundary_type + name: boundary_type + type: string + from_member: true + - key: natural + name: natural + type: string + from_member: true + relation_types: [boundary] mapping: - type: [boundary] - filters: - require: - #admin_level: ['2'] - admin_level: [__any__] - claimed_by: [__any__] - + boundary: + - administrative + border_status: + - dispute + boundary_type: + - maritime \ No newline at end of file diff --git a/layers/boundary/mapping_diagram.png b/layers/boundary/mapping_diagram.png index 1eb7fb23b..db85cc42c 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 000000000..e44c2677e --- /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": 137 + }, + { + "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": 138 + }, + { + "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": 139 + }, + { + "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": 140 + }, + { + "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": 141 + } + ] +} \ No newline at end of file diff --git a/layers/building/building.sql b/layers/building/building.sql index 4e1557359..845a7498a 100644 --- a/layers/building/building.sql +++ b/layers/building/building.sql @@ -1,151 +1,135 @@ -- etldoc: layer_building[shape=record fillcolor=lightpink, style="rounded,filled", -- etldoc: label="layer_building | z14+ " ] ; -CREATE OR REPLACE FUNCTION as_numeric(text) RETURNS NUMERIC AS $$ - -- Inspired by http://stackoverflow.com/questions/16195986/isnumeric-with-postgresql/16206123#16206123 -DECLARE test NUMERIC; -BEGIN - test = $1::NUMERIC; - RETURN test; -EXCEPTION WHEN others THEN - RETURN -1; -END; -$$ STRICT -LANGUAGE plpgsql IMMUTABLE; +CREATE INDEX IF NOT EXISTS osm_building_relation_building_idx ON osm_building_relation (building) WHERE building = '' AND ST_GeometryType(geometry) = 'ST_Polygon'; +CREATE INDEX IF NOT EXISTS osm_building_relation_member_idx ON osm_building_relation (member) WHERE role = 'outline'; -CREATE INDEX IF NOT EXISTS osm_building_relation_building_idx ON osm_building_relation(building) WHERE ST_GeometryType(geometry) = 'ST_Polygon'; -CREATE INDEX IF NOT EXISTS osm_building_relation_member_idx ON osm_building_relation(member); ---CREATE INDEX IF NOT EXISTS osm_building_associatedstreet_role_idx ON osm_building_associatedstreet(role) WHERE ST_GeometryType(geometry) = 'ST_Polygon'; ---CREATE INDEX IF NOT EXISTS osm_building_street_role_idx ON osm_building_street(role) WHERE ST_GeometryType(geometry) = 'ST_Polygon'; +CREATE OR REPLACE VIEW osm_all_buildings AS +( +SELECT + -- etldoc: osm_building_relation -> layer_building:z14_ + -- Buildings built from relations + member AS osm_id, + geometry, + COALESCE(CleanNumeric(height), CleanNumeric(buildingheight)) AS height, + COALESCE(CleanNumeric(min_height), CleanNumeric(buildingmin_height)) AS min_height, + COALESCE(CleanNumeric(levels), CleanNumeric(buildinglevels)) AS levels, + COALESCE(CleanNumeric(min_level), CleanNumeric(buildingmin_level)) AS min_level, + nullif(material, '') AS material, + nullif(colour, '') AS colour, + FALSE AS hide_3d +FROM osm_building_relation +WHERE building = '' + AND ST_GeometryType(geometry) = 'ST_Polygon' +UNION ALL -CREATE OR REPLACE VIEW osm_all_buildings AS ( - -- etldoc: osm_building_relation -> layer_building:z14_ - -- Buildings built from relations - SELECT member AS osm_id,geometry, - COALESCE(nullif(as_numeric(height),-1),nullif(as_numeric(buildingheight),-1)) as height, - COALESCE(nullif(as_numeric(min_height),-1),nullif(as_numeric(buildingmin_height),-1)) as min_height, - COALESCE(nullif(as_numeric(levels),-1),nullif(as_numeric(buildinglevels),-1)) as levels, - COALESCE(nullif(as_numeric(min_level),-1),nullif(as_numeric(buildingmin_level),-1)) as min_level, - nullif(material, '') AS material, - nullif(colour, '') AS colour, - FALSE as hide_3d - FROM - osm_building_relation WHERE building = '' AND ST_GeometryType(geometry) = 'ST_Polygon' - UNION ALL - - -- etldoc: osm_building_associatedstreet -> layer_building:z14_ - -- Buildings in associatedstreet relations - SELECT member AS osm_id,geometry, - COALESCE(nullif(as_numeric(height),-1),nullif(as_numeric(buildingheight),-1)) as height, - COALESCE(nullif(as_numeric(min_height),-1),nullif(as_numeric(buildingmin_height),-1)) as min_height, - COALESCE(nullif(as_numeric(levels),-1),nullif(as_numeric(buildinglevels),-1)) as levels, - COALESCE(nullif(as_numeric(min_level),-1),nullif(as_numeric(buildingmin_level),-1)) as min_level, - nullif(material, '') AS material, - nullif(colour, '') AS colour, - FALSE as hide_3d - FROM - osm_building_associatedstreet WHERE role = 'house' AND ST_GeometryType(geometry) = 'ST_Polygon' - UNION ALL - -- etldoc: osm_building_street -> layer_building:z14_ - -- Buildings in street relations - SELECT member AS osm_id,geometry, - COALESCE(nullif(as_numeric(height),-1),nullif(as_numeric(buildingheight),-1)) as height, - COALESCE(nullif(as_numeric(min_height),-1),nullif(as_numeric(buildingmin_height),-1)) as min_height, - COALESCE(nullif(as_numeric(levels),-1),nullif(as_numeric(buildinglevels),-1)) as levels, - COALESCE(nullif(as_numeric(min_level),-1),nullif(as_numeric(buildingmin_level),-1)) as min_level, - nullif(material, '') AS material, - nullif(colour, '') AS colour, - FALSE as hide_3d - FROM - osm_building_street WHERE role = 'house' AND ST_GeometryType(geometry) = 'ST_Polygon' - UNION ALL - - -- etldoc: osm_building_multipolygon -> layer_building:z14_ - -- Buildings that are inner/outer - SELECT osm_id,geometry, - COALESCE(nullif(as_numeric(height),-1),nullif(as_numeric(buildingheight),-1)) as height, - COALESCE(nullif(as_numeric(min_height),-1),nullif(as_numeric(buildingmin_height),-1)) as min_height, - COALESCE(nullif(as_numeric(levels),-1),nullif(as_numeric(buildinglevels),-1)) as levels, - COALESCE(nullif(as_numeric(min_level),-1),nullif(as_numeric(buildingmin_level),-1)) as min_level, - nullif(material, '') AS material, - nullif(colour, '') AS colour, - FALSE as hide_3d - FROM - osm_building_polygon obp WHERE EXISTS (SELECT 1 FROM osm_building_multipolygon obm WHERE obp.osm_id = obm.osm_id) - UNION ALL - -- etldoc: osm_building_polygon -> layer_building:z14_ - -- Standalone buildings - SELECT obp.osm_id,obp.geometry, - COALESCE(nullif(as_numeric(obp.height),-1),nullif(as_numeric(obp.buildingheight),-1)) as height, - COALESCE(nullif(as_numeric(obp.min_height),-1),nullif(as_numeric(obp.buildingmin_height),-1)) as min_height, - COALESCE(nullif(as_numeric(obp.levels),-1),nullif(as_numeric(obp.buildinglevels),-1)) as levels, - COALESCE(nullif(as_numeric(obp.min_level),-1),nullif(as_numeric(obp.buildingmin_level),-1)) as min_level, - nullif(obp.material, '') AS material, - nullif(obp.colour, '') AS colour, - CASE WHEN obr.role='outline' THEN TRUE ELSE FALSE END as hide_3d - FROM - osm_building_polygon obp - LEFT JOIN osm_building_relation obr ON (obr.member = obp.osm_id) - WHERE obp.osm_id >= -1e17 - -- we want to keep the ways - -- they have osm_id >= -1e17 if use_single_id_space is true, osm_id >= 0 otherwise -); +SELECT + -- etldoc: osm_building_polygon -> layer_building:z14_ + -- Standalone buildings + obp.osm_id, + obp.geometry, + COALESCE(CleanNumeric(obp.height), CleanNumeric(obp.buildingheight)) AS height, + COALESCE(CleanNumeric(obp.min_height), CleanNumeric(obp.buildingmin_height)) AS min_height, + COALESCE(CleanNumeric(obp.levels), CleanNumeric(obp.buildinglevels)) AS levels, + COALESCE(CleanNumeric(obp.min_level), CleanNumeric(obp.buildingmin_level)) AS min_level, + nullif(obp.material, '') AS material, + nullif(obp.colour, '') AS colour, + obr.role IS NOT NULL AS hide_3d +FROM osm_building_polygon obp + LEFT JOIN osm_building_relation obr ON + obp.osm_id >= 0 AND + obr.member = obp.osm_id AND + obr.role = 'outline' +WHERE ST_GeometryType(obp.geometry) IN ('ST_Polygon', 'ST_MultiPolygon') + ); CREATE OR REPLACE FUNCTION layer_building(bbox geometry, zoom_level int) -RETURNS TABLE(geometry geometry, osm_id bigint, render_height int, render_min_height int, colour text, hide_3d boolean) AS $$ - SELECT geometry, osm_id, render_height, render_min_height, + RETURNS TABLE + ( + geometry geometry, + osm_id bigint, + render_height int, + render_min_height int, + colour text, + hide_3d boolean + ) +AS +$$ +SELECT geometry, + osm_id, + render_height, + render_min_height, COALESCE(colour, CASE material -- Ordered by count from taginfo - WHEN 'cement_block' THEN '#6a7880' - WHEN 'brick' THEN '#bd8161' - WHEN 'plaster' THEN '#dadbdb' - WHEN 'wood' THEN '#d48741' - WHEN 'concrete' THEN '#d3c2b0' - WHEN 'metal' THEN '#b7b1a6' - WHEN 'stone' THEN '#b4a995' - WHEN 'mud' THEN '#9d8b75' - WHEN 'steel' THEN '#b7b1a6' -- same as metal - WHEN 'glass' THEN '#5a81a0' - WHEN 'traditional' THEN '#bd8161' -- same as brick - WHEN 'masonry' THEN '#bd8161' -- same as brick - WHEN 'Brick' THEN '#bd8161' -- same as brick - WHEN 'tin' THEN '#b7b1a6' -- same as metal - WHEN 'timber_framing' THEN '#b3b0a9' - WHEN 'sandstone' THEN '#b4a995' -- same as stone - WHEN 'clay' THEN '#9d8b75' -- same as mud - END) AS colour, - CASE WHEN hide_3d THEN TRUE ELSE NULL::boolean END AS hide_3d - FROM ( - -- etldoc: osm_building_polygon -> layer_building:z14_ - SELECT DISTINCT ON (osm_id) - osm_id, geometry, - ceil( COALESCE(height, levels*3.66,5))::int AS render_height, - floor(COALESCE(min_height, min_level*3.66,0))::int AS render_min_height, - material, - colour, - hide_3d - FROM osm_all_buildings - WHERE - (levels IS NULL OR levels < 1000) AND - (min_level IS NULL OR min_level < 1000) AND - (height IS NULL OR height < 3000) AND - (min_height IS NULL OR min_height < 3000) AND - zoom_level >= 14 AND geometry && bbox - ) AS zoom_levels - ORDER BY render_height ASC, ST_YMin(geometry) DESC; -$$ LANGUAGE SQL IMMUTABLE; + WHEN 'cement_block' THEN '#6a7880' + WHEN 'brick' THEN '#bd8161' + WHEN 'plaster' THEN '#dadbdb' + WHEN 'wood' THEN '#d48741' + WHEN 'concrete' THEN '#d3c2b0' + WHEN 'metal' THEN '#b7b1a6' + WHEN 'stone' THEN '#b4a995' + WHEN 'mud' THEN '#9d8b75' + WHEN 'steel' THEN '#b7b1a6' -- same as metal + WHEN 'glass' THEN '#5a81a0' + WHEN 'traditional' THEN '#bd8161' -- same as brick + WHEN 'masonry' THEN '#bd8161' -- same as brick + WHEN 'Brick' THEN '#bd8161' -- same as brick + WHEN 'tin' THEN '#b7b1a6' -- same as metal + WHEN 'timber_framing' THEN '#b3b0a9' + WHEN 'sandstone' THEN '#b4a995' -- same as stone + WHEN 'clay' THEN '#9d8b75' -- same as mud + END) AS colour, + CASE WHEN hide_3d THEN TRUE END AS hide_3d +FROM ( + SELECT + -- etldoc: osm_building_block_gen_z13 -> layer_building:z13 + osm_id, + geometry, + NULL::int AS render_height, + NULL::int AS render_min_height, + NULL::text AS material, + NULL::text AS colour, + FALSE AS hide_3d + FROM osm_building_block_gen_z13 + WHERE zoom_level = 13 + AND geometry && bbox + UNION ALL + SELECT + -- etldoc: osm_building_polygon -> layer_building:z14_ + DISTINCT ON (osm_id) osm_id, + geometry, + ceil(COALESCE(height, levels * 3.66, 5))::int AS render_height, + floor(COALESCE(min_height, min_level * 3.66, 0))::int AS render_min_height, + material, + colour, + hide_3d + FROM osm_all_buildings + WHERE (levels IS NULL OR levels < 1000) + AND (min_level IS NULL OR min_level < 1000) + AND (height IS NULL OR height < 3000) + AND (min_height IS NULL OR min_height < 3000) + AND zoom_level >= 14 + AND geometry && bbox + ) AS zoom_levels +ORDER BY render_height ASC, ST_YMin(geometry) DESC; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE + ; CREATE OR REPLACE FUNCTION layer_building_no_height(bbox geometry, zoom_level int) RETURNS TABLE(geometry geometry, osm_id bigint) AS $$ - SELECT geometry, osm_id - FROM ( - -- etldoc: osm_building_polygon -> layer_building:z14_ - SELECT DISTINCT ON (osm_id) - osm_id, geometry - FROM osm_all_buildings - WHERE zoom_level >= 14 AND geometry && bbox - ) AS zoom_levels - ORDER BY ST_YMin(geometry) DESC; -$$ LANGUAGE SQL IMMUTABLE; - +SELECT geometry, osm_id +FROM ( + -- etldoc: osm_building_polygon -> layer_building:z14_ + SELECT DISTINCT ON (osm_id) + osm_id, geometry + FROM osm_all_buildings + WHERE zoom_level >= 14 AND geometry && bbox + ) AS zoom_levels +ORDER BY ST_YMin(geometry) DESC; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE + ; -- not handled: where a building outline covers building parts diff --git a/layers/building/building.yaml b/layers/building/building.yaml index f346238dd..d1db1c7d3 100644 --- a/layers/building/building.yaml +++ b/layers/building/building.yaml @@ -1,14 +1,19 @@ 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: - query: (SELECT geometry FROM layer_building_no_height(!bbox!, z(!scale_denominator!))) AS t + geometry_field: geometry + key_field: osm_id + key_field_as_attribute: no + srid: 900913 + query: (SELECT osm_id, geometry FROM layer_building_no_height(!bbox!, z(!scale_denominator!))) AS t fields: {} schema: + - ./update_building.sql - ./building.sql datasources: - type: imposm3 - mapping_file: ./mapping.yaml + mapping_file: ./mapping.yaml \ No newline at end of file diff --git a/layers/building/etl_diagram.png b/layers/building/etl_diagram.png index 0b0c985c1..0f28da2a6 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 8cb2d8c69..e32e48ee8 100644 --- a/layers/building/mapping.yaml +++ b/layers/building/mapping.yaml @@ -1,7 +1,14 @@ +#generalized_tables: +# # etldoc: imposm3 -> osm_building_polygon_gen1 +# building_polygon_gen1: +# source: building_polygon +# sql_filter: area>power(ZRES12,2) AND ST_IsValid(geometry) +# tolerance: ZRES14 + tables: # etldoc: imposm3 -> osm_building_polygon building_polygon: - fields: + columns: - name: osm_id type: id - name: geometry @@ -53,200 +60,19 @@ 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_street - building_street: - fields: - - name: osm_id - type: id - - name: geometry - type: validated_geometry - - name: area - type: area - - name: building - key: building - type: string - from_member: true - - name: material - key: building:material - type: string - - name: colour - key: building:colour - type: string - - name: buildingpart - key: building:part - type: string - from_member: true - - name: buildingheight - key: building:height - type: string - from_member: true - - name: height - key: height - type: string - from_member: true - - name: buildingmin_height - key: building:min_height - type: string - from_member: true - - name: min_height - key: min_height - type: string - from_member: true - - name: buildinglevels - key: building:levels - type: string - from_member: true - - name: levels - key: levels - type: string - from_member: true - - name: buildingmin_level - key: building:min_level - type: string - from_member: true - - name: min_level - key: min_level - type: string - from_member: true - - name: relbuildingheight - key: building:height - type: string - - name: relheight - key: height - type: string - - name: relbuildingmin_height - key: building:min_height - type: string - - name: relmin_height - key: min_height - type: string - - name: relbuildinglevels - key: building:levels - type: string - - name: rellevels - key: levels - type: string - - name: relbuildingmin_level - key: building:min_level - type: string - - name: relmin_level - key: min_level - type: string - - name: member - type: member_id - - name: index - type: member_index - - name: role - type: member_role - from_member: true - - name: type - type: member_type - mapping: - type: [street] - type: relation_member - - # etldoc: imposm3 -> osm_building_associatedstreet - building_associatedstreet: - fields: - - name: osm_id - type: id - - name: geometry - type: validated_geometry - - name: area - type: area - - name: building - key: building - type: string - from_member: true - - name: material - key: building:material - type: string - - name: colour - key: building:colour - type: string - - name: buildingpart - key: building:part - type: string - from_member: true - - name: buildingheight - key: building:height - type: string - from_member: true - - name: height - key: height - type: string - from_member: true - - name: buildingmin_height - key: building:min_height - type: string - from_member: true - - name: min_height - key: min_height - type: string - from_member: true - - name: buildinglevels - key: building:levels - type: string - from_member: true - - name: levels - key: levels - type: string - from_member: true - - name: buildingmin_level - key: building:min_level - type: string - from_member: true - - name: min_level - key: min_level - type: string - from_member: true - - name: relbuildingheight - key: building:height - type: string - - name: relheight - key: height - type: string - - name: relbuildingmin_height - key: building:min_height - type: string - - name: relmin_height - key: min_height - type: string - - name: relbuildinglevels - key: building:levels - type: string - - name: rellevels - key: levels - type: string - - name: relbuildingmin_level - key: building:min_level - type: string - - name: relmin_level - key: min_level - type: string - - name: member - type: member_id - - name: index - type: member_index - - name: role - type: member_role - from_member: true - - name: type - type: member_type - mapping: - type: [associatedStreet] - type: relation_member - # etldoc: imposm3 -> osm_building_relation building_relation: - fields: + columns: - name: osm_id type: id - name: geometry @@ -335,76 +161,3 @@ tables: mapping: type: [building] type: relation_member - - # etldoc: imposm3 -> osm_building_multipolygon - building_multipolygon: - fields: - - name: osm_id - type: id - - name: geometry - type: validated_geometry - - name: area - type: area - - name: building - key: building - type: string - from_member: true - - name: material - key: building:material - type: string - - name: colour - key: building:colour - type: string - - name: buildingpart - key: building:part - type: string - from_member: true - - name: buildingheight - key: building:height - type: string - from_member: true - - name: height - key: height - type: string - from_member: true - - name: buildingmin_height - key: building:min_height - type: string - from_member: true - - name: min_height - key: min_height - type: string - from_member: true - - name: buildinglevels - key: building:levels - type: string - from_member: true - - name: levels - key: levels - type: string - from_member: true - - name: buildingmin_level - key: building:min_level - type: string - from_member: true - - name: min_level - key: min_level - type: string - from_member: true - - name: member - type: member_id - - name: index - type: member_index - - name: role - type: member_role - from_member: true - - name: type - type: member_type - mapping: - type: [multipolygon] - filters: - reject: - building: ["no","none","No"] - building:part: ["no","none","No"] - man_made: ["bridge"] - type: relation_member diff --git a/layers/building/mapping_diagram.png b/layers/building/mapping_diagram.png index 8d8341f2b..767b00eb8 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 000000000..4834f68ae --- /dev/null +++ b/layers/building/style.json @@ -0,0 +1,44 @@ +{ + "layers": [ + { + "id": "building", + "type": "fill", + "source": "openmaptiles", + "source-layer": "building", + "minzoom": 12, + "maxzoom": 24, + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": { + "stops": [ + [ + 13, + "rgba(222, 213, 207, 1)" + ], + [ + 16, + "#d9d0c9" + ] + ] + }, + "fill-outline-color": { + "base": 1, + "stops": [ + [ + 13, + "#9A918A" + ], + [ + 16, + "rgba(166, 157, 150, 1)" + ] + ] + } + }, + "metadata": {}, + "order": 19 + } + ] +} \ No newline at end of file diff --git a/layers/building/update_building.sql b/layers/building/update_building.sql new file mode 100644 index 000000000..d363eacad --- /dev/null +++ b/layers/building/update_building.sql @@ -0,0 +1,185 @@ +DROP TRIGGER IF EXISTS trigger_refresh ON buildings.updates; +DROP TRIGGER IF EXISTS trigger_flag ON osm_building_polygon; + +-- Creating aggregated building blocks with removed small polygons and small +-- holes. Aggregated polygons are simplified by Visvalingam-Whyatt algorithm. +-- Aggregating is made block by block using country_osm_grid polygon table. + +-- Function returning recordset for matview. +-- Returning recordset of buildings aggregates by zres 14, with removed small +-- holes and with removed small buildings/blocks. + +CREATE OR REPLACE FUNCTION osm_building_block_gen1() + RETURNS table + ( + osm_id bigint, + geometry geometry + ) +AS +$$ +DECLARE + zres14 float := Zres(14); + zres12 float := Zres(12); + zres14vw float := Zres(14) * Zres(14); + polyg_world record; + +BEGIN + FOR polyg_world IN + SELECT ST_Transform(country.geometry, 3857) AS geometry + FROM country_osm_grid country + + LOOP + FOR osm_id, geometry IN + WITH dta AS ( -- CTE is used because of optimization + SELECT o.osm_id, + o.geometry, + ST_ClusterDBSCAN(o.geometry, eps := zres14, minpoints := 1) OVER () cid + FROM osm_building_polygon o + WHERE ST_Intersects(o.geometry, polyg_world.geometry) + ) + SELECT (array_agg(dta.osm_id))[1] AS osm_id, + ST_Buffer( + ST_Union( + ST_Buffer( + ST_SnapToGrid(dta.geometry, 0.000001) + , zres14, 'join=mitre') + ) + , -zres14, 'join=mitre') AS geometry + FROM dta + GROUP BY cid + + LOOP + -- removing holes smaller than + IF ST_NumInteriorRings(geometry) > 0 THEN -- only from geometries wih holes + geometry := ( + -- there are some multi-geometries in this layer + SELECT ST_Collect(gn) + FROM ( + -- in some cases are "holes" NULL, because all holes are smaller than + SELECT COALESCE( + -- exterior ring + ST_MakePolygon(ST_ExteriorRing(dmp.geom), holes), + ST_MakePolygon(ST_ExteriorRing(dmp.geom)) + ) gn + + FROM ST_Dump(geometry) dmp, -- 1 dump polygons + LATERAL ( + SELECT array_agg(ST_Boundary(rg.geom)) holes -- 2 create array + FROM ST_DumpRings(dmp.geom) rg -- 3 from rings + WHERE rg.path[1] > 0 -- 5 except inner ring + AND ST_Area(rg.geom) >= power(zres12, 2) -- 4 bigger than + ) holes + ) new_geom + ); + END IF; + + IF ST_Area(geometry) < power(zres12, 2) THEN + CONTINUE; + END IF; + + -- simplify + geometry := ST_SimplifyVW(geometry, zres14vw); + + RETURN NEXT; + END LOOP; + END LOOP; +END; +$$ LANGUAGE plpgsql STABLE + STRICT + PARALLEL SAFE; + + +DROP MATERIALIZED VIEW IF EXISTS osm_building_block_gen1_dup CASCADE; + +CREATE MATERIALIZED VIEW osm_building_block_gen1_dup AS +SELECT * +FROM osm_building_block_gen1(); + +CREATE INDEX ON osm_building_block_gen1_dup USING gist (geometry); + +-- etldoc: osm_building_polygon -> osm_building_block_gen_z13 +DROP MATERIALIZED VIEW IF EXISTS osm_building_block_gen_z13; +CREATE MATERIALIZED VIEW osm_building_block_gen_z13 AS +( +WITH + counts AS ( + SELECT count(osm_id) AS counts, + osm_id + FROM osm_building_block_gen1_dup + GROUP BY osm_id + ), + + duplicates AS ( + SELECT counts.osm_id + FROM counts + WHERE counts.counts > 1 + ) + +SELECT osm.osm_id, + ST_Union( + ST_MakeValid(osm.geometry)) AS geometry + FROM osm_building_block_gen1_dup osm, + duplicates + WHERE osm.osm_id = duplicates.osm_id + GROUP BY osm.osm_id + + UNION ALL + + SELECT osm.osm_id, + osm.geometry + FROM osm_building_block_gen1_dup osm, + counts + WHERE counts.counts = 1 + AND osm.osm_id = counts.osm_id +); + +CREATE INDEX ON osm_building_block_gen_z13 USING gist (geometry); +CREATE UNIQUE INDEX ON osm_building_block_gen_z13 USING btree (osm_id); + +-- Handle updates + +CREATE SCHEMA IF NOT EXISTS buildings; + +CREATE TABLE IF NOT EXISTS buildings.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); + +CREATE OR REPLACE FUNCTION buildings.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO buildings.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION buildings.refresh() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh buildings block'; + REFRESH MATERIALIZED VIEW osm_building_block_gen1_dup; + REFRESH MATERIALIZED VIEW osm_building_block_gen_z13; + -- noinspection SqlWithoutWhere + DELETE FROM buildings.updates; + + RAISE LOG 'Update buildings block done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE + ON osm_building_polygon + FOR EACH STATEMENT +EXECUTE PROCEDURE buildings.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON buildings.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE buildings.refresh(); \ No newline at end of file diff --git a/layers/housenumber/etl_diagram.png b/layers/housenumber/etl_diagram.png index add427d5c..d93f9718b 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 new file mode 100644 index 000000000..84cb4d83c --- /dev/null +++ b/layers/housenumber/housenumber.sql @@ -0,0 +1,23 @@ +-- etldoc: layer_housenumber[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_housenumber | z14+" ] ; + +CREATE OR REPLACE FUNCTION layer_housenumber(bbox geometry, zoom_level integer) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + housenumber text + ) +AS +$$ +SELECT + -- etldoc: osm_housenumber_point -> layer_housenumber:z14_ + osm_id, + geometry, + housenumber +FROM osm_housenumber_point +WHERE zoom_level >= 14 + AND geometry && bbox; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/housenumber/housenumber.yaml b/layers/housenumber/housenumber.yaml index 46a909ec7..e3df87843 100644 --- a/layers/housenumber/housenumber.yaml +++ b/layers/housenumber/housenumber.yaml @@ -13,7 +13,7 @@ layer: query: (SELECT geometry, housenumber FROM layer_housenumber(!bbox!, z(!scale_denominator!))) AS t schema: - ./housenumber_centroid.sql - - ./layer.sql + - ./housenumber.sql datasources: - type: imposm3 mapping_file: ./mapping.yaml diff --git a/layers/housenumber/housenumber_centroid.sql b/layers/housenumber/housenumber_centroid.sql index e168d4c85..b2cd51b5a 100644 --- a/layers/housenumber/housenumber_centroid.sql +++ b/layers/housenumber/housenumber_centroid.sql @@ -1,51 +1,91 @@ DROP TRIGGER IF EXISTS trigger_flag ON osm_housenumber_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_housenumber_point; DROP TRIGGER IF EXISTS trigger_refresh ON housenumber.updates; +CREATE SCHEMA IF NOT EXISTS housenumber; + +CREATE TABLE IF NOT EXISTS housenumber.osm_ids +( + osm_id bigint +); + -- etldoc: osm_housenumber_point -> osm_housenumber_point -CREATE OR REPLACE FUNCTION convert_housenumber_point() RETURNS VOID AS $$ -BEGIN - UPDATE osm_housenumber_point - SET geometry = - CASE WHEN ST_NPoints(ST_ConvexHull(geometry))=ST_NPoints(geometry) - THEN ST_Centroid(geometry) - ELSE ST_PointOnSurface(ST_MakeValid(geometry)) - END - WHERE ST_GeometryType(geometry) <> 'ST_Point'; -END; -$$ LANGUAGE plpgsql; +CREATE OR REPLACE FUNCTION convert_housenumber_point(full_update boolean) RETURNS void AS +$$ + UPDATE osm_housenumber_point + SET geometry = + CASE + WHEN ST_NPoints(ST_ConvexHull(geometry)) = ST_NPoints(geometry) + THEN ST_Centroid(geometry) + ELSE ST_PointOnSurface(ST_MakeValid(geometry)) + END + WHERE (full_update OR osm_id IN (SELECT osm_id FROM housenumber.osm_ids)) + AND ST_GeometryType(geometry) <> 'ST_Point' + AND ST_IsValid(geometry); +$$ LANGUAGE SQL; -SELECT convert_housenumber_point(); +SELECT convert_housenumber_point(true); -- Handle updates -CREATE SCHEMA IF NOT EXISTS housenumber; +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; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; -CREATE TABLE IF NOT EXISTS housenumber.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION housenumber.flag() RETURNS trigger AS $$ +CREATE TABLE IF NOT EXISTS housenumber.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION housenumber.flag() RETURNS trigger AS +$$ BEGIN - INSERT INTO housenumber.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + INSERT INTO housenumber.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; END; -$$ language plpgsql; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION housenumber.refresh() RETURNS trigger AS - $BODY$ - BEGIN +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN RAISE LOG 'Refresh housenumber'; - PERFORM convert_housenumber_point(); + PERFORM convert_housenumber_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM housenumber.osm_ids; + -- noinspection SqlWithoutWhere DELETE FROM housenumber.updates; - RETURN null; - END; - $BODY$ -language plpgsql; + + RAISE LOG 'Refresh housenumber done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_housenumber_point + FOR EACH ROW +EXECUTE PROCEDURE housenumber.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE ON osm_housenumber_point + AFTER INSERT OR UPDATE OR DELETE + ON osm_housenumber_point FOR EACH STATEMENT - EXECUTE PROCEDURE housenumber.flag(); +EXECUTE PROCEDURE housenumber.flag(); CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON housenumber.updates + AFTER INSERT + ON housenumber.updates INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE housenumber.refresh(); +EXECUTE PROCEDURE housenumber.refresh(); diff --git a/layers/housenumber/layer.sql b/layers/housenumber/layer.sql deleted file mode 100644 index eed865fea..000000000 --- a/layers/housenumber/layer.sql +++ /dev/null @@ -1,10 +0,0 @@ - --- etldoc: layer_housenumber[shape=record fillcolor=lightpink, style="rounded,filled", --- etldoc: label="layer_housenumber | z14+" ] ; - -CREATE OR REPLACE FUNCTION layer_housenumber(bbox geometry, zoom_level integer) -RETURNS TABLE(osm_id bigint, geometry geometry, housenumber text) AS $$ - -- etldoc: osm_housenumber_point -> layer_housenumber:z14_ - SELECT osm_id, geometry, housenumber FROM osm_housenumber_point - WHERE zoom_level >= 14 AND geometry && bbox; -$$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/housenumber/mapping.yaml b/layers/housenumber/mapping.yaml index cf5f25575..174335bb0 100644 --- a/layers/housenumber/mapping.yaml +++ b/layers/housenumber/mapping.yaml @@ -4,7 +4,7 @@ tables: # etldoc: imposm3 -> osm_housenumber_point housenumber_point: type: geometry - fields: + columns: - name: osm_id type: id - name: geometry diff --git a/layers/housenumber/mapping_diagram.png b/layers/housenumber/mapping_diagram.png index a30e718a7..089216909 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 000000000..70aa6c7a7 --- /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": 145 + } + ] +} \ No newline at end of file diff --git a/layers/landcover/etl_diagram.png b/layers/landcover/etl_diagram.png index 8fb941a76..03b3e1911 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 new file mode 100644 index 000000000..1dc52f034 --- /dev/null +++ b/layers/landcover/generalized.sql @@ -0,0 +1,287 @@ +DROP TABLE IF EXISTS osm_landcover_gen_z7; +DROP TABLE IF EXISTS osm_landcover_gen_z8; +DROP TABLE IF EXISTS osm_landcover_gen_z9; +DROP TABLE IF EXISTS osm_landcover_gen_z10; +DROP TABLE IF EXISTS osm_landcover_gen_z11; +DROP TABLE IF EXISTS osm_landcover_gen_z12; +DROP TABLE IF EXISTS osm_landcover_gen_z13; +DROP TABLE IF EXISTS simplify_vw_z7 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z8 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z9 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z10 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z11 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z12 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z13 CASCADE; + +-- etldoc: osm_landcover_polygon -> simplify_vw_z13 +CREATE TABLE simplify_vw_z13 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(13),2)), + 0.001)) AS geometry + FROM osm_landcover_polygon + WHERE ST_Area(geometry) > power(zres(12),2) +); +CREATE INDEX ON simplify_vw_z13 USING GIST (geometry); + +-- etldoc: simplify_vw_z13 -> osm_landcover_gen_z13 +CREATE TABLE osm_landcover_gen_z13 AS +( + SELECT subclass, ST_MakeValid((ST_dump(ST_Union(geometry))).geom) AS geometry + FROM ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) over () AS cid, geometry + FROM simplify_vw_z13 + WHERE ST_NPoints(geometry) < 300 + AND subclass IN ('wood', 'forest')) union_geom300 + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + geometry + FROM simplify_vw_z13 + WHERE (ST_NPoints(geometry) >= 300 AND subclass IN ('wood', 'forest')) + OR (subclass NOT IN ('wood', 'forest')) + ); + +CREATE INDEX ON osm_landcover_gen_z13 USING GIST (geometry); + + +-- etldoc: simplify_vw_z13 -> simplify_vw_z12 +CREATE TABLE simplify_vw_z12 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(12),2)), + 0.001)) AS geometry + FROM simplify_vw_z13 + WHERE ST_Area(geometry) > power(zres(11),2) +); +CREATE INDEX ON simplify_vw_z12 USING GIST (geometry); + +-- etldoc: simplify_vw_z12 -> osm_landcover_gen_z12 +CREATE TABLE osm_landcover_gen_z12 AS +( + SELECT subclass, ST_MakeValid((ST_dump(ST_Union(geometry))).geom) AS geometry + FROM ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) over () AS cid, geometry + FROM simplify_vw_z12 + WHERE ST_NPoints(geometry) < 300 + AND subclass IN ('wood', 'forest')) union_geom300 + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + geometry + FROM simplify_vw_z12 + WHERE (ST_NPoints(geometry) >= 300 AND subclass IN ('wood', 'forest')) + OR (subclass NOT IN ('wood', 'forest')) + ); + +CREATE INDEX ON osm_landcover_gen_z12 USING GIST (geometry); + + +-- etldoc: simplify_vw_z12 -> simplify_vw_z11 +CREATE TABLE simplify_vw_z11 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(11),2)), + 0.001)) AS geometry + FROM simplify_vw_z12 + WHERE ST_Area(geometry) > power(zres(10),2) +); +CREATE INDEX ON simplify_vw_z11 USING GIST (geometry); + +-- etldoc: simplify_vw_z11 -> osm_landcover_gen_z11 +CREATE TABLE osm_landcover_gen_z11 AS +( + SELECT subclass, ST_MakeValid((ST_dump(ST_Union(geometry))).geom) AS geometry + FROM ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) over () AS cid, geometry + FROM simplify_vw_z11 + WHERE ST_NPoints(geometry) < 300 + AND subclass IN ('wood', 'forest')) union_geom300 + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + geometry + FROM simplify_vw_z11 + WHERE (ST_NPoints(geometry) >= 300 AND subclass IN ('wood', 'forest')) + OR (subclass NOT IN ('wood', 'forest')) + ); + +CREATE INDEX ON osm_landcover_gen_z11 USING GIST (geometry); + + +-- etldoc: simplify_vw_z11 -> simplify_vw_z10 +CREATE TABLE simplify_vw_z10 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(10),2)), + 0.001)) AS geometry + FROM simplify_vw_z11 + WHERE ST_Area(geometry) > power(zres(9),2) +); +CREATE INDEX ON simplify_vw_z10 USING GIST (geometry); + +-- etldoc: simplify_vw_z10 -> osm_landcover_gen_z10 +CREATE TABLE osm_landcover_gen_z10 AS +( + SELECT subclass, ST_MakeValid((ST_dump(ST_Union(geometry))).geom) AS geometry + FROM ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) over () AS cid, geometry + FROM simplify_vw_z10 + WHERE ST_NPoints(geometry) < 300 + AND subclass IN ('wood', 'forest')) union_geom300 + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + geometry + FROM simplify_vw_z10 + WHERE (ST_NPoints(geometry) >= 300 AND subclass IN ('wood', 'forest')) + OR (subclass NOT IN ('wood', 'forest')) + ); + +CREATE INDEX ON osm_landcover_gen_z10 USING GIST (geometry); + + +-- etldoc: simplify_vw_z10 -> simplify_vw_z9 +CREATE TABLE simplify_vw_z9 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(9),2)), + 0.001)) AS geometry + FROM simplify_vw_z10 + WHERE ST_Area(geometry) > power(zres(8),2) +); +CREATE INDEX ON simplify_vw_z9 USING GIST (geometry); + +-- etldoc: simplify_vw_z9 -> osm_landcover_gen_z9 +CREATE TABLE osm_landcover_gen_z9 AS +( + SELECT subclass, ST_MakeValid((ST_dump(ST_Union(geometry))).geom) AS geometry + FROM ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) over () AS cid, geometry + FROM simplify_vw_z9 + WHERE ST_NPoints(geometry) < 300 + AND subclass IN ('wood', 'forest')) union_geom300 + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + ST_MakeValid( + (ST_Dump( + ST_Union(geometry))).geom) AS geometry + FROM ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) over () AS cid, geometry + FROM simplify_vw_z9 + WHERE ST_NPoints(geometry) >= 300 + AND subclass IN ('wood', 'forest')) union_geom_rest + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + geometry + FROM simplify_vw_z9 + WHERE subclass NOT IN ('wood', 'forest') + ); + +CREATE INDEX ON osm_landcover_gen_z9 USING GIST (geometry); + + +-- etldoc: simplify_vw_z9 -> simplify_vw_z8 +CREATE TABLE simplify_vw_z8 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(8),2)), + 0.001)) AS geometry + FROM simplify_vw_z9 + WHERE ST_Area(geometry) > power(zres(7),2) + ); +CREATE INDEX ON simplify_vw_z8 USING GIST (geometry); + +-- etldoc: simplify_vw_z8 -> osm_landcover_gen_z8 +CREATE TABLE osm_landcover_gen_z8 AS +( +SELECT subclass, + ST_MakeValid( + (ST_Dump( + ST_Union(geometry))).geom) AS geometry + FROM + ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) OVER () AS cid, + geometry + FROM simplify_vw_z8 + WHERE subclass IN ('wood', 'forest') + ) union_geom + GROUP BY subclass, + cid + UNION ALL + SELECT subclass, + geometry + FROM simplify_vw_z8 + WHERE subclass NOT IN ('wood', 'forest') + ); + +CREATE INDEX ON osm_landcover_gen_z8 USING GIST (geometry); + + +-- etldoc: simplify_vw_z8 -> simplify_vw_z7 +CREATE TABLE simplify_vw_z7 AS +( + SELECT subclass, + ST_MakeValid( + ST_SnapToGrid( + ST_SimplifyVW(geometry, power(zres(7),2)), + 0.001)) AS geometry + FROM simplify_vw_z8 + WHERE ST_Area(geometry) > power(zres(6),2) +); +CREATE INDEX ON simplify_vw_z7 USING GIST (geometry); + +-- etldoc: simplify_vw_z7 -> osm_landcover_gen_z7 +CREATE TABLE osm_landcover_gen_z7 AS +( +SELECT subclass, + ST_MakeValid( + (ST_Dump( + ST_Union(geometry))).geom) AS geometry + FROM + ( + SELECT subclass, + ST_ClusterDBSCAN(geometry, eps := 0, minpoints := 1) OVER () AS cid, + geometry + FROM simplify_vw_z7 + ) union_geom +GROUP BY subclass, + cid + ); + +CREATE INDEX ON osm_landcover_gen_z7 USING GIST (geometry); + +DROP TABLE IF EXISTS simplify_vw_z7 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z8 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z9 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z10 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z11 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z12 CASCADE; +DROP TABLE IF EXISTS simplify_vw_z13 CASCADE; diff --git a/layers/landcover/landcover.sql b/layers/landcover/landcover.sql index d90ac62d5..1b032bda0 100644 --- a/layers/landcover/landcover.sql +++ b/layers/landcover/landcover.sql @@ -9,158 +9,402 @@ --); --CREATE INDEX IF NOT EXISTS landcover_grouped_gen2_geometry_idx ON landcover_grouped_gen2 USING gist(geometry); -CREATE OR REPLACE FUNCTION landcover_class(subclass VARCHAR) RETURNS TEXT AS $$ - SELECT CASE - WHEN subclass IN ('farmland', 'farm', 'orchard', 'vineyard', 'plant_nursery') THEN 'farmland' - WHEN subclass IN ('glacier', 'ice_shelf') THEN 'ice' - WHEN subclass IN ('wood', 'forest') THEN 'wood' - WHEN subclass IN ('bare_rock', 'scree') THEN 'rock' - WHEN subclass IN ('fell', 'grassland', 'heath', 'scrub', 'tundra', 'grass', 'meadow', 'allotments', - 'park', 'village_green', 'recreation_ground', 'garden', 'golf_course') THEN 'grass' - WHEN subclass IN ('wetland', 'bog', 'swamp', 'wet_meadow', 'marsh', 'reedbed', - 'saltern', 'tidalflat', 'saltmarsh', 'mangrove') THEN 'wetland' - WHEN subclass IN ('beach', 'sand', 'dune') THEN 'sand' - ELSE NULL - END; -$$ LANGUAGE SQL IMMUTABLE; - --- etldoc: ne_110m_glaciated_areas -> landcover_z0 +CREATE OR REPLACE FUNCTION landcover_class(subclass varchar) RETURNS text AS +$$ +SELECT CASE + %%FIELD_MAPPING: class %% + END; +$$ LANGUAGE SQL IMMUTABLE + -- STRICT + PARALLEL SAFE; + +-- ne_50m_antarctic_ice_shelves_polys +-- etldoc: ne_50m_antarctic_ice_shelves_polys -> ne_50m_antarctic_ice_shelves_polys_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_antarctic_ice_shelves_polys_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_antarctic_ice_shelves_polys_gen_z4 AS +( +SELECT + ST_Simplify(geometry, ZRes(6)) as geometry, + 'ice_shelf'::text AS subclass, + ST_Area(geometry) AS area +FROM ne_50m_antarctic_ice_shelves_polys + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_antarctic_ice_shelves_polys_gen_z4_idx ON ne_50m_antarctic_ice_shelves_polys_gen_z4 USING gist (geometry); + +-- ne_110m_glaciated_areas +-- etldoc: ne_110m_glaciated_areas -> ne_110m_glaciated_areas_gen_z1 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_glaciated_areas_gen_z1 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_glaciated_areas_gen_z1 AS +( +SELECT + ST_Simplify(geometry, ZRes(3)) as geometry, + 'glacier'::text AS subclass, + ST_Area(geometry) AS area +FROM ne_110m_glaciated_areas + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_glaciated_areas_gen_z1_idx ON ne_110m_glaciated_areas_gen_z1 USING gist (geometry); + +-- etldoc: ne_110m_glaciated_areas_gen_z1 -> ne_110m_glaciated_areas_gen_z0 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_glaciated_areas_gen_z0 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_glaciated_areas_gen_z0 AS +( +SELECT + ST_Simplify(geometry, ZRes(2)) as geometry, + subclass, + ST_Area(geometry) AS area +FROM ne_110m_glaciated_areas_gen_z1 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_glaciated_areas_gen_z0_idx ON ne_110m_glaciated_areas_gen_z0 USING gist (geometry); + +-- etldoc: ne_50m_antarctic_ice_shelves_polys_gen_z4 -> ne_50m_antarctic_ice_shelves_polys_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_antarctic_ice_shelves_polys_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_antarctic_ice_shelves_polys_gen_z3 AS +( +SELECT + ST_Simplify(geometry, ZRes(5)) as geometry, + subclass, + ST_Area(geometry) AS area +FROM ne_50m_antarctic_ice_shelves_polys_gen_z4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_antarctic_ice_shelves_polys_gen_z3_idx ON ne_50m_antarctic_ice_shelves_polys_gen_z3 USING gist (geometry); + +-- etldoc: ne_50m_antarctic_ice_shelves_polys_gen_z3 -> ne_50m_antarctic_ice_shelves_polys_gen_z2 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_antarctic_ice_shelves_polys_gen_z2 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_antarctic_ice_shelves_polys_gen_z2 AS +( +SELECT + ST_Simplify(geometry, ZRes(4)) as geometry, + subclass, + ST_Area(geometry) AS area +FROM ne_50m_antarctic_ice_shelves_polys_gen_z3 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_antarctic_ice_shelves_polys_gen_z2_idx ON ne_50m_antarctic_ice_shelves_polys_gen_z2 USING gist (geometry); + +-- ne_50m_glaciated_areas +-- etldoc: ne_50m_glaciated_areas -> ne_50m_glaciated_areas_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_glaciated_areas_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_glaciated_areas_gen_z4 AS +( +SELECT + ST_Simplify(geometry, ZRes(6)) as geometry, + 'glacier'::text AS subclass, + ST_Area(geometry) AS area +FROM ne_50m_glaciated_areas + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_glaciated_areas_gen_z4_idx ON ne_50m_glaciated_areas_gen_z4 USING gist (geometry); + +-- etldoc: ne_50m_glaciated_areas_gen_z4 -> ne_50m_glaciated_areas_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_glaciated_areas_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_glaciated_areas_gen_z3 AS +( +SELECT + ST_Simplify(geometry, ZRes(5)) as geometry, + subclass, + ST_Area(geometry) AS area +FROM ne_50m_glaciated_areas_gen_z4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_glaciated_areas_gen_z3_idx ON ne_50m_glaciated_areas_gen_z3 USING gist (geometry); + +-- etldoc: ne_50m_glaciated_areas_gen_z3 -> ne_50m_glaciated_areas_gen_z2 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_glaciated_areas_gen_z2 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_glaciated_areas_gen_z2 AS +( +SELECT + ST_Simplify(geometry, ZRes(4)) as geometry, + subclass, + ST_Area(geometry) AS area +FROM ne_50m_glaciated_areas_gen_z3 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_glaciated_areas_gen_z2_idx ON ne_50m_glaciated_areas_gen_z2 USING gist (geometry); + +-- ne_10m_glaciated_areas +-- etldoc: ne_10m_glaciated_areas -> ne_10m_glaciated_areas_gen_z6 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_glaciated_areas_gen_z6 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_glaciated_areas_gen_z6 AS +( +SELECT + ST_Simplify(geometry, ZRes(8)) as geometry, + 'glacier'::text AS subclass, + ST_Area(geometry) AS area +FROM ne_10m_glaciated_areas + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_glaciated_areas_gen_z6_idx ON ne_10m_glaciated_areas_gen_z6 USING gist (geometry); + +-- etldoc: ne_10m_glaciated_areas_gen_z6 -> ne_10m_glaciated_areas_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_glaciated_areas_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_glaciated_areas_gen_z5 AS +( +SELECT + ST_Simplify(geometry, ZRes(7)) as geometry, + subclass, + ST_Area(geometry) AS area +FROM ne_10m_glaciated_areas_gen_z6 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_glaciated_areas_gen_z5_idx ON ne_10m_glaciated_areas_gen_z5 USING gist (geometry); + +-- ne_10m_antarctic_ice_shelves_polys +-- etldoc: ne_10m_antarctic_ice_shelves_polys -> ne_10m_antarctic_ice_shelves_polys_gen_z6 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_antarctic_ice_shelves_polys_gen_z6 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_antarctic_ice_shelves_polys_gen_z6 AS +( +SELECT + ST_Simplify(geometry, ZRes(8)) as geometry, + 'ice_shelf'::text AS subclass, + ST_Area(geometry) AS area +FROM ne_10m_antarctic_ice_shelves_polys + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_antarctic_ice_shelves_polys_gen_z6_idx ON ne_10m_antarctic_ice_shelves_polys_gen_z6 USING gist (geometry); + +-- etldoc: ne_10m_antarctic_ice_shelves_polys_gen_z6 -> ne_10m_antarctic_ice_shelves_polys_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_antarctic_ice_shelves_polys_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_antarctic_ice_shelves_polys_gen_z5 AS +( +SELECT + ST_Simplify(geometry, ZRes(7)) as geometry, + subclass, + ST_Area(geometry) AS area +FROM ne_10m_antarctic_ice_shelves_polys_gen_z6 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_antarctic_ice_shelves_polys_gen_z5_idx ON ne_10m_antarctic_ice_shelves_polys_gen_z5 USING gist (geometry); + +-- etldoc: ne_110m_glaciated_areas_gen_z0 -> landcover_z0 CREATE OR REPLACE VIEW landcover_z0 AS ( SELECT - NULL::bigint AS osm_id, + geometry, - 'glacier'::text AS subclass, + subclass, ST_Area(geometry) AS area - FROM ne_110m_glaciated_areas -); + FROM ne_110m_glaciated_areas_gen_z0 + ); + +-- etldoc: ne_110m_glaciated_areas_gen_z1 -> landcover_z1 +CREATE OR REPLACE VIEW landcover_z1 AS +( +SELECT + geometry, + subclass, + ST_Area(geometry) AS area +FROM ne_110m_glaciated_areas_gen_z1 + ); -CREATE OR REPLACE VIEW landcover_z2 AS ( - -- etldoc: ne_50m_glaciated_areas -> landcover_z2 +CREATE OR REPLACE VIEW landcover_z2 AS +( +-- etldoc: ne_50m_glaciated_areas_gen_z2 -> landcover_z2 SELECT - NULL::bigint AS osm_id, + geometry, - 'glacier'::text AS subclass, + subclass, ST_Area(geometry) AS area - FROM ne_50m_glaciated_areas - UNION ALL - -- etldoc: ne_50m_antarctic_ice_shelves_polys -> landcover_z2 + FROM ne_50m_glaciated_areas_gen_z2 +UNION ALL +-- etldoc: ne_50m_antarctic_ice_shelves_polys_gen_z2 -> landcover_z2 SELECT - NULL::bigint AS osm_id, geometry, - 'ice_shelf'::text AS subclass, - ST_Area(geometry) AS area - FROM ne_50m_antarctic_ice_shelves_polys -); + subclass, + ST_Area(geometry) AS area +FROM ne_50m_antarctic_ice_shelves_polys_gen_z2 + ); -CREATE OR REPLACE VIEW landcover_z5 AS ( - -- etldoc: ne_10m_glaciated_areas -> landcover_z5 - SELECT - NULL::bigint AS osm_id, - geometry, - 'glacier'::text AS subclass, - ST_Area(geometry) AS area - FROM ne_10m_glaciated_areas - UNION ALL - -- etldoc: ne_10m_antarctic_ice_shelves_polys -> landcover_z5 - SELECT - NULL::bigint AS osm_id, +CREATE OR REPLACE VIEW landcover_z3 AS +( +-- etldoc: ne_50m_glaciated_areas_gen_z3 -> landcover_z3 +SELECT + geometry, + subclass, + ST_Area(geometry) AS area +FROM ne_50m_glaciated_areas_gen_z3 +UNION ALL +-- etldoc: ne_50m_antarctic_ice_shelves_polys_gen_z3 -> landcover_z3 +SELECT geometry, - 'ice_shelf'::text AS subclass, + subclass, + ST_Area(geometry) AS area +FROM ne_50m_antarctic_ice_shelves_polys_gen_z3 + ); + +CREATE OR REPLACE VIEW landcover_z4 AS +( +-- etldoc: ne_50m_glaciated_areas_gen_z4 -> landcover_z4 +SELECT + geometry, + subclass, + ST_Area(geometry) AS area +FROM ne_50m_glaciated_areas_gen_z4 +UNION ALL +-- etldoc: ne_50m_antarctic_ice_shelves_polys_gen_z4 -> landcover_z4 +SELECT + geometry, subclass, ST_Area(geometry) AS area - FROM ne_10m_antarctic_ice_shelves_polys -); - -CREATE OR REPLACE VIEW landcover_z7 AS ( - -- etldoc: osm_landcover_polygon_gen7 -> landcover_z7 - SELECT osm_id, geometry, subclass, area FROM osm_landcover_polygon_gen7 -); - -CREATE OR REPLACE VIEW landcover_z8 AS ( - -- etldoc: osm_landcover_polygon_gen6 -> landcover_z8 - SELECT osm_id, geometry, subclass, area FROM osm_landcover_polygon_gen6 -); - -CREATE OR REPLACE VIEW landcover_z9 AS ( - -- etldoc: osm_landcover_polygon_gen5 -> landcover_z9 - SELECT osm_id, geometry, subclass, area FROM osm_landcover_polygon_gen5 -); - -CREATE OR REPLACE VIEW landcover_z10 AS ( - -- etldoc: osm_landcover_polygon_gen4 -> landcover_z10 - SELECT osm_id, geometry, subclass, area FROM osm_landcover_polygon_gen4 -); - -CREATE OR REPLACE VIEW landcover_z11 AS ( - -- etldoc: osm_landcover_polygon_gen3 -> landcover_z11 - SELECT osm_id, geometry, subclass, area FROM osm_landcover_polygon_gen3 -); - -CREATE OR REPLACE VIEW landcover_z12 AS ( - -- etldoc: osm_landcover_polygon_gen2 -> landcover_z12 - SELECT osm_id, geometry, subclass, area FROM osm_landcover_polygon_gen2 -); - -CREATE OR REPLACE VIEW landcover_z13 AS ( - -- etldoc: osm_landcover_polygon_gen1 -> landcover_z13 - SELECT osm_id, geometry, subclass, area FROM osm_landcover_polygon_gen1 -); - -CREATE OR REPLACE VIEW landcover_z14 AS ( - -- etldoc: osm_landcover_polygon -> landcover_z14 - SELECT osm_id, geometry, subclass, area FROM osm_landcover_polygon -); - --- etldoc: layer_landcover[shape=record fillcolor=lightpink, style="rounded, filled", label="layer_landcover | z0-z1 | z2-z4 | z5-z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; + FROM ne_50m_antarctic_ice_shelves_polys_gen_z4 + ); + +CREATE OR REPLACE VIEW landcover_z5 AS +( +-- etldoc: ne_10m_glaciated_areas_gen_z5 -> landcover_z5 +SELECT + geometry, + subclass, + ST_Area(geometry) AS area +FROM ne_10m_glaciated_areas_gen_z5 +UNION ALL +-- etldoc: ne_10m_antarctic_ice_shelves_polys_gen_z5 -> landcover_z5 +SELECT + geometry, + subclass, + ST_Area(geometry) AS area +FROM ne_10m_antarctic_ice_shelves_polys_gen_z5 + ); + +CREATE OR REPLACE VIEW landcover_z6 AS +( +-- etldoc: ne_10m_glaciated_areas_gen_z6 -> landcover_z6 +SELECT + geometry, subclass, ST_Area(geometry) AS area FROM ne_10m_glaciated_areas_gen_z6 +UNION ALL +-- etldoc: ne_10m_antarctic_ice_shelves_polys_gen_z6 -> landcover_z6 +SELECT + geometry, + subclass, + ST_Area(geometry) AS area +FROM ne_10m_antarctic_ice_shelves_polys_gen_z6 + ); + +-- etldoc: layer_landcover[shape=record fillcolor=lightpink, style="rounded, filled", label="layer_landcover | z0 | z1 | z2 | z3 | z4 | z5 | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; CREATE OR REPLACE FUNCTION layer_landcover(bbox geometry, zoom_level int) -RETURNS TABLE(osm_id bigint, geometry geometry, class text, subclass text) AS $$ - SELECT osm_id, geometry, - landcover_class(subclass) AS class, - subclass - FROM ( - -- etldoc: landcover_z0 -> layer_landcover:z0_1 - SELECT * FROM landcover_z0 - WHERE zoom_level BETWEEN 0 AND 1 AND geometry && bbox - UNION ALL - -- etldoc: landcover_z2 -> layer_landcover:z2_4 - SELECT * FROM landcover_z2 - WHERE zoom_level BETWEEN 2 AND 4 AND geometry && bbox - UNION ALL - -- etldoc: landcover_z5 -> layer_landcover:z5_6 - SELECT * FROM landcover_z5 - WHERE zoom_level BETWEEN 5 AND 6 AND geometry && bbox - UNION ALL - -- etldoc: landcover_z7 -> layer_landcover:z7 - SELECT * - FROM landcover_z7 WHERE zoom_level = 7 AND geometry && bbox - UNION ALL - -- etldoc: landcover_z8 -> layer_landcover:z8 - SELECT * - FROM landcover_z8 WHERE zoom_level = 8 AND geometry && bbox - UNION ALL - -- etldoc: landcover_z9 -> layer_landcover:z9 - SELECT * - FROM landcover_z9 WHERE zoom_level = 9 AND geometry && bbox - UNION ALL - -- etldoc: landcover_z10 -> layer_landcover:z10 - SELECT * - FROM landcover_z10 WHERE zoom_level = 10 AND geometry && bbox - UNION ALL - -- etldoc: landcover_z11 -> layer_landcover:z11 - SELECT * - FROM landcover_z11 WHERE zoom_level = 11 AND geometry && bbox - UNION ALL - -- etldoc: landcover_z12 -> layer_landcover:z12 - SELECT * - FROM landcover_z12 WHERE zoom_level = 12 AND geometry && bbox - UNION ALL - -- etldoc: landcover_z13 -> layer_landcover:z13 - SELECT * - FROM landcover_z13 WHERE zoom_level = 13 AND geometry && bbox - UNION ALL - -- etldoc: landcover_z14 -> layer_landcover:z14_ - SELECT * - FROM landcover_z14 WHERE zoom_level >= 14 AND geometry && bbox - ) AS zoom_levels - ORDER BY area DESC; -$$ LANGUAGE SQL IMMUTABLE; + RETURNS TABLE + ( + geometry geometry, + class text, + subclass text + ) +AS +$$ +SELECT geometry, + landcover_class(subclass) AS class, + subclass +FROM ( + -- etldoc: landcover_z0 -> layer_landcover:z0 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM landcover_z0 + WHERE zoom_level = 0 + AND geometry && bbox + UNION ALL + -- etldoc: landcover_z1 -> layer_landcover:z1 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM landcover_z1 + WHERE zoom_level = 1 + AND geometry && bbox + UNION ALL + -- etldoc: landcover_z2 -> layer_landcover:z2 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM landcover_z2 + WHERE zoom_level = 2 + AND geometry && bbox + UNION ALL + -- etldoc: landcover_z3 -> layer_landcover:z3 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM landcover_z3 + WHERE zoom_level = 3 + AND geometry && bbox + UNION ALL + -- etldoc: landcover_z4 -> layer_landcover:z4 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM landcover_z4 + WHERE zoom_level = 4 + AND geometry && bbox + UNION ALL + -- etldoc: landcover_z5 -> layer_landcover:z5 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM landcover_z5 + WHERE zoom_level = 5 + AND geometry && bbox + UNION ALL + -- etldoc: landcover_z6 -> layer_landcover:z6 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM landcover_z6 + WHERE zoom_level = 6 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z7 -> layer_landcover:z7 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM osm_landcover_gen_z7 + WHERE zoom_level = 7 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z8 -> layer_landcover:z8 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM osm_landcover_gen_z8 + WHERE zoom_level = 8 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z9 -> layer_landcover:z9 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM osm_landcover_gen_z9 + WHERE zoom_level = 9 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z10 -> layer_landcover:z10 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM osm_landcover_gen_z10 + WHERE zoom_level = 10 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z11 -> layer_landcover:z11 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM osm_landcover_gen_z11 + WHERE zoom_level = 11 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z12 -> layer_landcover:z12 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM osm_landcover_gen_z12 + WHERE zoom_level = 12 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_gen_z13 -> layer_landcover:z13 + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM osm_landcover_gen_z13 + WHERE zoom_level = 13 + AND geometry && bbox + UNION ALL + -- etldoc: osm_landcover_polygon -> layer_landcover:z14_ + SELECT geometry, + subclass, + ST_Area(geometry) AS area + FROM osm_landcover_polygon + WHERE zoom_level >= 14 + AND geometry && bbox + ORDER BY area DESC + ) AS zoom_levels; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; \ No newline at end of file diff --git a/layers/landcover/landcover.yaml b/layers/landcover/landcover.yaml index 09e4e26f4..ca876dba0 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 @@ -10,12 +17,20 @@ layer: description: | Use the **class** to assign natural colors for **landcover**. values: - - farmland - - ice - - wood - - grass - - wetland - - sand + farmland: + subclass: ['farmland', 'farm', 'orchard', 'vineyard', 'plant_nursery'] + ice: + subclass: ['glacier', 'ice_shelf'] + wood: + subclass: ['wood', 'forest'] + rock: + subclass: ['bare_rock', 'scree'] + grass: + subclass: ['fell', 'grassland', 'heath', 'scrub', 'shrubbery', 'tundra', 'grass', 'meadow', 'allotments', 'park', 'village_green', 'recreation_ground', 'garden', 'golf_course'] + wetland: + subclass: ['wetland', 'bog', 'swamp', 'wet_meadow', 'marsh', 'reedbed', 'saltern', 'tidalflat', 'saltmarsh', 'mangrove'] + sand: + subclass: ['beach', 'sand', 'dune'] subclass: description: | Use **subclass** to do more precise styling. @@ -31,6 +46,7 @@ layer: - bog - dune - scrub + - shrubbery - farm - farmland - fell @@ -65,6 +81,7 @@ layer: geometry_field: geometry query: (SELECT geometry, class, subclass FROM layer_landcover(!bbox!, z(!scale_denominator!))) AS t schema: + - ./generalized.sql - ./landcover.sql datasources: - type: imposm3 diff --git a/layers/landcover/mapping.yaml b/layers/landcover/mapping.yaml index 952111cf5..351bf3a29 100644 --- a/layers/landcover/mapping.yaml +++ b/layers/landcover/mapping.yaml @@ -1,52 +1,7 @@ - -generalized_tables: - - # etldoc: imposm3 -> osm_landcover_polygon_gen7 - landcover_polygon_gen7: - source: landcover_polygon_gen6 - sql_filter: area>power(ZRES5,2) - tolerance: ZRES7 - - # etldoc: imposm3 -> osm_landcover_polygon_gen6 - landcover_polygon_gen6: - source: landcover_polygon_gen5 - sql_filter: area>power(ZRES6,2) - tolerance: ZRES8 - - # etldoc: imposm3 -> osm_landcover_polygon_gen5 - landcover_polygon_gen5: - source: landcover_polygon_gen4 - sql_filter: area>power(ZRES7,2) - tolerance: ZRES9 - - # etldoc: imposm3 -> osm_landcover_polygon_gen4 - landcover_polygon_gen4: - source: landcover_polygon_gen3 - sql_filter: area>power(ZRES8,2) - tolerance: ZRES10 - - # etldoc: imposm3 -> osm_landcover_polygon_gen3 - landcover_polygon_gen3: - source: landcover_polygon_gen2 - sql_filter: area>power(ZRES8,2) - tolerance: ZRES11 - - # etldoc: imposm3 -> osm_landcover_polygon_gen2 - landcover_polygon_gen2: - source: landcover_polygon_gen1 - sql_filter: area>power(ZRES9,2) - tolerance: ZRES12 - - # etldoc: imposm3 -> osm_landcover_polygon_gen1 - landcover_polygon_gen1: - source: landcover_polygon - sql_filter: area>power(ZRES10,2) AND ST_IsValid(geometry) - tolerance: ZRES13 - tables: # etldoc: imposm3 -> osm_landcover_polygon landcover_polygon: - fields: + columns: - name: osm_id type: id - name: geometry @@ -71,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 @@ -80,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 2f693dc71..b738d9aa7 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 000000000..6b3c567be --- /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 000000000..80d20529a --- /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 6762ce010..74be17994 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 252247fce..ed76bc836 100644 --- a/layers/landuse/landuse.sql +++ b/layers/landuse/landuse.sql @@ -1,111 +1,378 @@ --- etldoc: ne_50m_urban_areas -> landuse_z4 -CREATE OR REPLACE VIEW landuse_z4 AS ( - SELECT NULL::bigint AS osm_id, geometry, 'residential'::text AS landuse, NULL::text AS amenity, NULL::text AS leisure, NULL::text AS tourism, NULL::text AS place, NULL::text AS waterway, scalerank - FROM ne_50m_urban_areas - WHERE scalerank <= 2 -); - --- etldoc: ne_50m_urban_areas -> landuse_z5 -CREATE OR REPLACE VIEW landuse_z5 AS ( - SELECT NULL::bigint AS osm_id, geometry, 'residential'::text AS landuse, NULL::text AS amenity, NULL::text AS leisure, NULL::text AS tourism, NULL::text AS place, NULL::text AS waterway, scalerank - FROM ne_50m_urban_areas -); +-- ne_50m_urban_areas +-- etldoc: ne_50m_urban_areas -> ne_50m_urban_areas_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_urban_areas_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_urban_areas_gen_z5 AS +( +SELECT + NULL::bigint AS osm_id, + ST_Simplify(geometry, ZRes(7)) as geometry, + 'residential'::text AS landuse, + NULL::text AS amenity, + NULL::text AS leisure, + NULL::text AS tourism, + NULL::text AS place, + NULL::text AS waterway, + scalerank +FROM ne_50m_urban_areas + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_urban_areas_gen_z5_idx ON ne_50m_urban_areas_gen_z5 USING gist (geometry); --- etldoc: osm_landuse_polygon_gen7 -> landuse_z6 -CREATE OR REPLACE VIEW landuse_z6 AS ( - SELECT osm_id, geometry, landuse, amenity, leisure, tourism, place, waterway, NULL::int as scalerank - FROM osm_landuse_polygon_gen7 -); +-- etldoc: ne_50m_urban_areas_gen_z5 -> ne_50m_urban_areas_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_urban_areas_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_urban_areas_gen_z4 AS +( +SELECT + osm_id, + ST_Simplify(geometry, ZRes(6)) as geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway +FROM ne_50m_urban_areas_gen_z5 +WHERE scalerank <= 2 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_urban_areas_gen_z4_idx ON ne_50m_urban_areas_gen_z4 USING gist (geometry); --- etldoc: osm_landuse_polygon_gen6 -> landuse_z8 -CREATE OR REPLACE VIEW landuse_z8 AS ( - SELECT osm_id, geometry, landuse, amenity, leisure, tourism, place, waterway, NULL::int as scalerank - FROM osm_landuse_polygon_gen6 +-- 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_gen5 -> landuse_z9 -CREATE OR REPLACE VIEW landuse_z9 AS ( - SELECT osm_id, geometry, landuse, amenity, leisure, tourism, place, waterway, NULL::int as scalerank - FROM osm_landuse_polygon_gen5 +-- 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_gen4 -> landuse_z10 -CREATE OR REPLACE VIEW landuse_z10 AS ( - SELECT osm_id, geometry, landuse, amenity, leisure, tourism, place, waterway, NULL::int as scalerank - FROM osm_landuse_polygon_gen4 +-- 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_gen3 -> landuse_z11 -CREATE OR REPLACE VIEW landuse_z11 AS ( - SELECT osm_id, geometry, landuse, amenity, leisure, tourism, place, waterway, NULL::int as scalerank - FROM osm_landuse_polygon_gen3 +-- 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_gen2 -> landuse_z12 -CREATE OR REPLACE VIEW landuse_z12 AS ( - SELECT osm_id, geometry, landuse, amenity, leisure, tourism, place, waterway, NULL::int as scalerank - FROM osm_landuse_polygon_gen2 +-- 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_gen1 -> landuse_z13 -CREATE OR REPLACE VIEW landuse_z13 AS ( - SELECT osm_id, geometry, landuse, amenity, leisure, tourism, place, waterway, NULL::int as scalerank - FROM osm_landuse_polygon_gen1 +-- 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 -> landuse_z14 -CREATE OR REPLACE VIEW landuse_z14 AS ( - SELECT osm_id, geometry, landuse, amenity, leisure, tourism, place, waterway, NULL::int as scalerank - FROM osm_landuse_polygon +-- 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+" ] ; +-- etldoc: label="layer_landuse | z4| z5| z6| z7| z8| z9| z10| z11| z12| z13| z14+" ] ; CREATE OR REPLACE FUNCTION layer_landuse(bbox geometry, zoom_level int) -RETURNS TABLE(osm_id bigint, geometry geometry, class text) AS $$ - SELECT osm_id, geometry, - COALESCE( - NULLIF(landuse, ''), - NULLIF(amenity, ''), - NULLIF(leisure, ''), - NULLIF(tourism, ''), - NULLIF(place, ''), - NULLIF(waterway, '') - ) AS class - FROM ( - -- etldoc: landuse_z4 -> layer_landuse:z4 - SELECT * FROM landuse_z4 - WHERE zoom_level = 4 - UNION ALL - -- etldoc: landuse_z5 -> layer_landuse:z5 - SELECT * FROM landuse_z5 - WHERE zoom_level = 5 - UNION ALL - -- etldoc: landuse_z6 -> layer_landuse:z6 - -- etldoc: landuse_z6 -> layer_landuse:z7 - SELECT * FROM landuse_z6 WHERE zoom_level BETWEEN 6 AND 7 - UNION ALL - -- etldoc: landuse_z8 -> layer_landuse:z8 - SELECT * FROM landuse_z8 WHERE zoom_level = 8 - UNION ALL - -- etldoc: landuse_z9 -> layer_landuse:z9 - SELECT * FROM landuse_z9 WHERE zoom_level = 9 - UNION ALL - -- etldoc: landuse_z10 -> layer_landuse:z10 - SELECT * FROM landuse_z10 WHERE zoom_level = 10 - UNION ALL - -- etldoc: landuse_z11 -> layer_landuse:z11 - SELECT * FROM landuse_z11 WHERE zoom_level = 11 - UNION ALL - -- etldoc: landuse_z12 -> layer_landuse:z12 - SELECT * FROM landuse_z12 WHERE zoom_level = 12 - UNION ALL - -- etldoc: landuse_z13 -> layer_landuse:z13 - SELECT * FROM landuse_z13 WHERE zoom_level = 13 - UNION ALL - -- etldoc: landuse_z14 -> layer_landuse:z14 - SELECT * FROM landuse_z14 WHERE zoom_level >= 14 - ) AS zoom_levels - WHERE geometry && bbox; -$$ LANGUAGE SQL IMMUTABLE; + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + class text + ) +AS +$$ +SELECT osm_id, + geometry, + landuse_unify(COALESCE( + NULLIF(landuse, ''), + NULLIF(amenity, ''), + NULLIF(leisure, ''), + NULLIF(tourism, ''), + NULLIF(place, ''), + NULLIF(waterway, '') + )) AS class +FROM ( + -- etldoc: ne_50m_urban_areas_gen_z4 -> layer_landuse:z4 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM ne_50m_urban_areas_gen_z4 + WHERE zoom_level = 4 + UNION ALL + -- etldoc: ne_50m_urban_areas_gen_z5 -> layer_landuse:z5 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM ne_50m_urban_areas_gen_z5 + WHERE zoom_level = 5 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z6_union -> layer_landuse:z6 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z6_union + WHERE zoom_level = 6 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z7_union -> layer_landuse:z7 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z7_union + WHERE zoom_level = 7 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z8_union -> layer_landuse:z8 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z8_union + WHERE zoom_level = 8 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z9_union -> layer_landuse:z9 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z9_union + WHERE zoom_level = 9 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z10_union -> layer_landuse:z10 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z10_union + WHERE zoom_level = 10 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z11_union -> layer_landuse:z11 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z11_union + WHERE zoom_level = 11 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z12_union -> layer_landuse:z12 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z12_union + WHERE zoom_level = 12 + UNION ALL + -- etldoc: osm_landuse_polygon_gen_z13 -> layer_landuse:z13 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon_gen_z13 + WHERE zoom_level = 13 + UNION ALL + -- etldoc: osm_landuse_polygon -> layer_landuse:z14 + SELECT osm_id, + geometry, + landuse, + amenity, + leisure, + tourism, + place, + waterway + FROM osm_landuse_polygon + WHERE zoom_level >= 14 + ) AS zoom_levels +WHERE geometry && bbox; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/landuse/landuse.yaml b/layers/landuse/landuse.yaml index 5d6d92a81..2d6f642e2 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. @@ -22,6 +25,7 @@ layer: - residential - commercial - industrial + - garages - retail - bus_station - school @@ -37,12 +41,16 @@ layer: - theme_park - zoo - suburb + - quarter - neighbourhood - dam + - quarry datasource: geometry_field: geometry query: (SELECT geometry, class FROM layer_landuse(!bbox!, z(!scale_denominator!))) AS t schema: + - ./class.sql + - ./prep_landuse.sql - ./landuse.sql datasources: - type: imposm3 diff --git a/layers/landuse/mapping.yaml b/layers/landuse/mapping.yaml index 038313e72..581258c30 100644 --- a/layers/landuse/mapping.yaml +++ b/layers/landuse/mapping.yaml @@ -1,35 +1,41 @@ generalized_tables: - # etldoc: imposm3 -> osm_landuse_polygon_gen7 - landuse_polygon_gen7: - source: landuse_polygon_gen6 + # etldoc: osm_landuse_polygon_gen_z7 -> osm_landuse_polygon_gen_z6 + landuse_polygon_gen_z6: + source: landuse_polygon_gen_z7 + tolerance: ZRES6 + sql_filter: area>power(ZRES6,2) + # etldoc: osm_landuse_polygon_gen_z8 -> osm_landuse_polygon_gen_z7 + landuse_polygon_gen_z7: + source: landuse_polygon_gen_z8 tolerance: ZRES7 - # etldoc: imposm3 -> osm_landuse_polygon_gen6 - landuse_polygon_gen6: - source: landuse_polygon_gen5 - sql_filter: area>power(ZRES6,2) AND (landuse='residential' OR place='suburb' OR place='neighbourhood') + sql_filter: area>power(ZRES6,2) + # etldoc: osm_landuse_polygon_gen_z9 -> osm_landuse_polygon_gen_z8 + landuse_polygon_gen_z8: + source: landuse_polygon_gen_z9 + sql_filter: area>power(ZRES6,2) AND (landuse='residential' OR place='suburb' OR place='quarter' OR place='neighbourhood') tolerance: ZRES8 - # etldoc: imposm3 -> osm_landuse_polygon_gen5 - landuse_polygon_gen5: - source: landuse_polygon_gen4 + # etldoc: osm_landuse_polygon_gen_z10 -> osm_landuse_polygon_gen_z9 + landuse_polygon_gen_z9: + source: landuse_polygon_gen_z10 sql_filter: area>power(ZRES7,2) tolerance: ZRES9 - # etldoc: imposm3 -> osm_landuse_polygon_gen4 - landuse_polygon_gen4: - source: landuse_polygon_gen3 + # etldoc: osm_landuse_polygon_gen_z11 -> osm_landuse_polygon_gen_z10 + landuse_polygon_gen_z10: + source: landuse_polygon_gen_z11 sql_filter: area>power(ZRES8,2) tolerance: ZRES10 - # etldoc: imposm3 -> osm_landuse_polygon_gen3 - landuse_polygon_gen3: - source: landuse_polygon_gen2 + # etldoc: osm_landuse_polygon_gen_z12 -> osm_landuse_polygon_gen_z11 + landuse_polygon_gen_z11: + source: landuse_polygon_gen_z12 sql_filter: area>power(ZRES9,2) tolerance: ZRES11 - # etldoc: imposm3 -> osm_landuse_polygon_gen2 - landuse_polygon_gen2: - source: landuse_polygon_gen1 + # etldoc: osm_landuse_polygon_gen_z13 -> osm_landuse_polygon_gen_z12 + landuse_polygon_gen_z12: + source: landuse_polygon_gen_z13 sql_filter: area>power(ZRES10,2) tolerance: ZRES12 - # etldoc: imposm3 -> osm_landuse_polygon_gen1 - landuse_polygon_gen1: + # etldoc: osm_landuse_polygon -> osm_landuse_polygon_gen_z13 + landuse_polygon_gen_z13: source: landuse_polygon sql_filter: area>power(ZRES11,2) AND ST_IsValid(geometry) tolerance: ZRES13 @@ -38,7 +44,7 @@ tables: # etldoc: imposm3 -> osm_landuse_polygon landuse_polygon: type: polygon - fields: + columns: - name: osm_id type: id - name: geometry @@ -68,6 +74,7 @@ tables: - railway - cemetery - military + - quarry # zoning - residential - commercial @@ -82,6 +89,7 @@ tables: - college - library - hospital + - grave_yard leisure: - stadium - pitch @@ -92,6 +100,7 @@ tables: - zoo place: - suburb + - quarter - neighbourhood waterway: - dam diff --git a/layers/landuse/mapping_diagram.png b/layers/landuse/mapping_diagram.png index 1139c4818..f9a82451d 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 000000000..e737e6bc7 --- /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 000000000..a32e70b87 --- /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 e9a5bd783..0f5c6fdd0 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/layer.sql b/layers/mountain_peak/layer.sql deleted file mode 100644 index 6002bd1bd..000000000 --- a/layers/mountain_peak/layer.sql +++ /dev/null @@ -1,55 +0,0 @@ - --- etldoc: layer_mountain_peak[shape=record fillcolor=lightpink, --- etldoc: style="rounded,filled", label="layer_mountain_peak | z7+" ] ; - -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) AS -$$ - -- etldoc: osm_peak_point -> layer_mountain_peak:z7_ - SELECT - osm_id, - geometry, - name, - name_en, - name_de, - tags -> 'natural' AS class, - tags, - ele::int, - ele_ft::int, - 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, - substring(ele from E'^(-?\\d+)(\\D|$)')::int AS ele, - round(substring(ele from E'^(-?\\d+)(\\D|$)')::int*3.2808399)::int AS ele_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) - ) DESC - )::int AS "rank" - FROM osm_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) - ORDER BY "rank" ASC; - -$$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/mountain_peak/mapping.yaml b/layers/mountain_peak/mapping.yaml index 7b9cc688e..21952d17a 100644 --- a/layers/mountain_peak/mapping.yaml +++ b/layers/mountain_peak/mapping.yaml @@ -3,7 +3,7 @@ tables: # etldoc: imposm3 -> osm_peak_point peak_point: type: point - fields: + columns: - name: osm_id type: id - name: geometry @@ -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 e0c5ceb3c..9e8a3e210 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 new file mode 100644 index 000000000..cf91e018f --- /dev/null +++ b/layers/mountain_peak/mountain_peak.sql @@ -0,0 +1,118 @@ +-- etldoc: osm_peak_point -> peak_point +-- etldoc: ne_10m_admin_0_countries -> peak_point +CREATE OR REPLACE VIEW peak_point AS +( +SELECT pp.osm_id, + pp.geometry, + pp.name, + pp.name_en, + pp.name_de, + pp.tags, + pp.ele, + ne.iso_a2, + pp.wikipedia +FROM osm_peak_point pp, ne_10m_admin_0_countries ne +WHERE ST_Intersects(pp.geometry, ne.geometry) + ); + + + +-- etldoc: layer_mountain_peak[shape=record fillcolor=lightpink, +-- etldoc: style="rounded,filled", label="layer_mountain_peak | z7+ | z13+" ] ; + +CREATE OR REPLACE FUNCTION layer_mountain_peak(bbox geometry, + zoom_level integer, + pixel_width numeric) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + name text, + name_en text, + name_de text, + class text, + tags hstore, + ele int, + ele_ft int, + customary_ft int, + "rank" int + ) +AS +$$ +SELECT + -- etldoc: peak_point -> layer_mountain_peak:z7_ + osm_id, + geometry, + name, + name_en, + name_de, + tags->'natural' AS class, + tags, + ele::int, + ele_ft::int, + customary_ft, + rank::int +FROM ( + SELECT osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + substring(ele FROM E'^(-?\\d+)(\\D|$)')::int AS ele, + round(substring(ele FROM E'^(-?\\d+)(\\D|$)')::int * 3.2808399)::int AS ele_ft, + CASE WHEN iso_a2 = 'US' THEN 1 END AS customary_ft, + row_number() OVER ( + PARTITION BY LabelGrid(geometry, 100 * pixel_width) + ORDER BY ( + substring(ele FROM E'^(-?\\d+)(\\D|$)')::int + + (CASE WHEN wikipedia <> '' THEN 10000 ELSE 0 END) + + (CASE WHEN name <> '' THEN 10000 ELSE 0 END) + ) DESC + )::int AS "rank" + FROM peak_point + WHERE geometry && bbox + AND ele 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 + PARALLEL SAFE; +-- TODO: Check if the above can be made STRICT -- i.e. if pixel_width could be NULL diff --git a/layers/mountain_peak/mountain_peak.yaml b/layers/mountain_peak/mountain_peak.yaml index a25212010..563934067 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,22 +13,33 @@ 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!), zres(z(!scale_denominator!))::numeric)) 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!), zres(z(!scale_denominator!))::numeric)) AS t schema: - ./update_peak_point.sql - - ./layer.sql + - ./update_mountain_linestring.sql + - ./mountain_peak.sql datasources: - type: imposm3 mapping_file: ./mapping.yaml diff --git a/layers/mountain_peak/style.json b/layers/mountain_peak/style.json new file mode 100644 index 000000000..8de0648b1 --- /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": 188 + } + ] +} \ 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 000000000..760c2742a --- /dev/null +++ b/layers/mountain_peak/update_mountain_linestring.sql @@ -0,0 +1,86 @@ +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 +); + +-- 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 + IF (tg_op = 'DELETE') THEN + INSERT INTO mountain_linestring.osm_ids VALUES (OLD.osm_id); + ELSE + INSERT INTO mountain_linestring.osm_ids VALUES (NEW.osm_id); + END IF; + 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'; + 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 OR DELETE + ON osm_mountain_linestring + FOR EACH ROW +EXECUTE PROCEDURE mountain_linestring.store(); + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE + 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 666bf9717..cda77f3da 100644 --- a/layers/mountain_peak/update_peak_point.sql +++ b/layers/mountain_peak/update_peak_point.sql @@ -1,48 +1,86 @@ DROP TRIGGER IF EXISTS trigger_flag ON osm_peak_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_peak_point; DROP TRIGGER IF EXISTS trigger_refresh ON mountain_peak_point.updates; --- etldoc: osm_peak_point -> osm_peak_point -CREATE OR REPLACE FUNCTION update_osm_peak_point() RETURNS VOID AS $$ -BEGIN - UPDATE osm_peak_point - SET tags = update_tags(tags, geometry) - WHERE COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL; +CREATE SCHEMA IF NOT EXISTS mountain_peak_point; -END; -$$ LANGUAGE plpgsql; +CREATE TABLE IF NOT EXISTS mountain_peak_point.osm_ids +( + osm_id bigint +); -SELECT update_osm_peak_point(); +-- etldoc: osm_peak_point -> osm_peak_point +CREATE OR REPLACE FUNCTION update_osm_peak_point(full_update boolean) RETURNS void AS +$$ + UPDATE osm_peak_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM mountain_peak_point.osm_ids)) + AND COALESCE(tags -> 'name:latin', tags -> 'name:nonlatin', tags -> 'name_int') IS NULL + AND tags != update_tags(tags, geometry) +$$ LANGUAGE SQL; + +SELECT update_osm_peak_point(true); -- Handle updates -CREATE SCHEMA IF NOT EXISTS mountain_peak_point; +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; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; -CREATE TABLE IF NOT EXISTS mountain_peak_point.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION mountain_peak_point.flag() RETURNS trigger AS $$ +CREATE TABLE IF NOT EXISTS mountain_peak_point.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION mountain_peak_point.flag() RETURNS trigger AS +$$ BEGIN - INSERT INTO mountain_peak_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + INSERT INTO mountain_peak_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; END; -$$ language plpgsql; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION mountain_peak_point.refresh() RETURNS trigger AS - $BODY$ - BEGIN +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN RAISE LOG 'Refresh mountain_peak_point'; - PERFORM update_osm_peak_point(); + PERFORM update_osm_peak_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM mountain_peak_point.osm_ids; + -- noinspection SqlWithoutWhere DELETE FROM mountain_peak_point.updates; - RETURN null; - END; - $BODY$ -language plpgsql; + + RAISE LOG 'Refresh mountain_peak_point done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_peak_point + FOR EACH ROW +EXECUTE PROCEDURE mountain_peak_point.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE ON osm_peak_point + AFTER INSERT OR UPDATE OR DELETE + ON osm_peak_point FOR EACH STATEMENT - EXECUTE PROCEDURE mountain_peak_point.flag(); +EXECUTE PROCEDURE mountain_peak_point.flag(); CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON mountain_peak_point.updates + AFTER INSERT + ON mountain_peak_point.updates INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE mountain_peak_point.refresh(); +EXECUTE PROCEDURE mountain_peak_point.refresh(); diff --git a/layers/park/etl_diagram.png b/layers/park/etl_diagram.png index 2ee424239..3a2735258 100644 Binary files a/layers/park/etl_diagram.png and b/layers/park/etl_diagram.png differ diff --git a/layers/park/layer.sql b/layers/park/layer.sql deleted file mode 100644 index f3da111c9..000000000 --- a/layers/park/layer.sql +++ /dev/null @@ -1,125 +0,0 @@ --- etldoc: layer_park[shape=record fillcolor=lightpink, style="rounded,filled", --- etldoc: label="layer_park | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; - -CREATE OR REPLACE FUNCTION layer_park(bbox geometry, zoom_level int, pixel_width numeric) -RETURNS TABLE(osm_id bigint, geometry geometry, class text, name text, name_en text, name_de text, tags hstore, rank int) AS $$ - SELECT osm_id, geometry, class, name, name_en, name_de, tags, rank - FROM ( - SELECT osm_id, geometry, - COALESCE(NULLIF(boundary, ''), NULLIF(leisure, '')) AS class, - name, name_en, name_de, tags, - NULL::int as rank - FROM ( - -- etldoc: osm_park_polygon_gen8 -> layer_park:z6 - SELECT osm_id, geometry, name, name_en, name_de, tags, leisure, boundary, NULL::int as scalerank - FROM osm_park_polygon_gen8 - WHERE zoom_level = 6 AND geometry && bbox - UNION ALL - -- etldoc: osm_park_polygon_gen7 -> layer_park:z7 - SELECT osm_id, geometry, name, name_en, name_de, tags, leisure, boundary, NULL::int as scalerank - FROM osm_park_polygon_gen7 - WHERE zoom_level = 7 AND geometry && bbox - UNION ALL - -- etldoc: osm_park_polygon_gen6 -> layer_park:z8 - SELECT osm_id, geometry, name, name_en, name_de, tags, leisure, boundary, NULL::int as scalerank - FROM osm_park_polygon_gen6 - WHERE zoom_level = 8 AND geometry && bbox - UNION ALL - -- etldoc: osm_park_polygon_gen5 -> layer_park:z9 - SELECT osm_id, geometry, name, name_en, name_de, tags, leisure, boundary, NULL::int as scalerank - FROM osm_park_polygon_gen5 - WHERE zoom_level = 9 AND geometry && bbox - UNION ALL - -- etldoc: osm_park_polygon_gen4 -> layer_park:z10 - SELECT osm_id, geometry, name, name_en, name_de, tags, leisure, boundary, NULL::int as scalerank - FROM osm_park_polygon_gen4 - WHERE zoom_level = 10 AND geometry && bbox - UNION ALL - -- etldoc: osm_park_polygon_gen3 -> layer_park:z11 - SELECT osm_id, geometry, name, name_en, name_de, tags, leisure, boundary, NULL::int as scalerank - FROM osm_park_polygon_gen3 - WHERE zoom_level = 11 AND geometry && bbox - UNION ALL - -- etldoc: osm_park_polygon_gen2 -> layer_park:z12 - SELECT osm_id, geometry, name, name_en, name_de, tags, leisure, boundary, NULL::int as scalerank - FROM osm_park_polygon_gen2 - WHERE zoom_level = 12 AND geometry && bbox - UNION ALL - -- etldoc: osm_park_polygon_gen1 -> layer_park:z13 - SELECT osm_id, geometry, name, name_en, name_de, tags, leisure, boundary, NULL::int as scalerank - FROM osm_park_polygon_gen1 - WHERE zoom_level = 13 AND geometry && bbox - UNION ALL - -- etldoc: osm_park_polygon -> layer_park:z14 - SELECT osm_id, geometry, name, name_en, name_de, tags, leisure, boundary, NULL::int as scalerank - FROM osm_park_polygon - WHERE zoom_level >= 14 AND geometry && bbox - ) AS park_polygon - - UNION ALL - SELECT osm_id, geometry_point AS geometry, - COALESCE(NULLIF(boundary, ''), NULLIF(leisure, '')) AS class, - name, name_en, name_de, tags, - row_number() OVER ( - PARTITION BY LabelGrid(geometry_point, 100 * pixel_width) - ORDER BY - (CASE WHEN boundary = 'national_park' THEN true ELSE false END) DESC, - (COALESCE(NULLIF(tags->'wikipedia', ''), NULLIF(tags->'wikidata', '')) IS NOT NULL) DESC, - area DESC - )::int AS "rank" - FROM ( - -- etldoc: osm_park_polygon_gen8 -> layer_park:z6 - SELECT osm_id, geometry_point, name, name_en, name_de, tags, leisure, boundary, area - FROM osm_park_polygon_gen8 - WHERE zoom_level = 6 AND geometry_point && bbox - UNION ALL - - -- etldoc: osm_park_polygon_gen7 -> layer_park:z7 - SELECT osm_id, geometry_point, name, name_en, name_de, tags, leisure, boundary, area - FROM osm_park_polygon_gen7 - WHERE zoom_level = 7 AND geometry_point && bbox - UNION ALL - - -- etldoc: osm_park_polygon_gen6 -> layer_park:z8 - SELECT osm_id, geometry_point, name, name_en, name_de, tags, leisure, boundary, area - FROM osm_park_polygon_gen6 - WHERE zoom_level = 8 AND geometry_point && bbox - UNION ALL - - -- etldoc: osm_park_polygon_gen5 -> layer_park:z9 - SELECT osm_id, geometry_point, name, name_en, name_de, tags, leisure, boundary, area - FROM osm_park_polygon_gen5 - WHERE zoom_level = 9 AND geometry_point && bbox - UNION ALL - - -- etldoc: osm_park_polygon_gen4 -> layer_park:z10 - SELECT osm_id, geometry_point, name, name_en, name_de, tags, leisure, boundary, area - FROM osm_park_polygon_gen4 - WHERE zoom_level = 10 AND geometry_point && bbox - UNION ALL - - -- etldoc: osm_park_polygon_gen3 -> layer_park:z11 - SELECT osm_id, geometry_point, name, name_en, name_de, tags, leisure, boundary, area - FROM osm_park_polygon_gen3 - WHERE zoom_level = 11 AND geometry_point && bbox - UNION ALL - - -- etldoc: osm_park_polygon_gen2 -> layer_park:z12 - SELECT osm_id, geometry_point, name, name_en, name_de, tags, leisure, boundary, area - FROM osm_park_polygon_gen2 - WHERE zoom_level = 12 AND geometry_point && bbox - UNION ALL - - -- etldoc: osm_park_polygon_gen1 -> layer_park:z13 - SELECT osm_id, geometry_point, name, name_en, name_de, tags, leisure, boundary, area - FROM osm_park_polygon_gen1 - WHERE zoom_level = 13 AND geometry_point && bbox - UNION ALL - - -- etldoc: osm_park_polygon -> layer_park:z14 - SELECT osm_id, geometry_point, name, name_en, name_de, tags, leisure, boundary, area - FROM osm_park_polygon - WHERE zoom_level >= 14 AND geometry_point && bbox - ) AS park_point - ) AS park_all; -$$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/park/mapping.yaml b/layers/park/mapping.yaml index 42372a75a..746803c2d 100644 --- a/layers/park/mapping.yaml +++ b/layers/park/mapping.yaml @@ -1,48 +1,60 @@ generalized_tables: - # etldoc: imposm3 -> osm_park_polygon_gen8 - park_polygon_gen8: - source: park_polygon_gen7 + # 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: imposm3 -> osm_park_polygon_gen7 - park_polygon_gen7: - source: park_polygon_gen6 + # 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: imposm3 -> osm_park_polygon_gen6 - park_polygon_gen6: - source: park_polygon_gen5 + # 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: imposm3 -> osm_park_polygon_gen5 - park_polygon_gen5: - source: park_polygon_gen4 + # 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: imposm3 -> osm_park_polygon_gen4 - park_polygon_gen4: - source: park_polygon_gen3 + # 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: imposm3 -> osm_park_polygon_gen3 - park_polygon_gen3: - source: park_polygon_gen2 + # etldoc: osm_park_polygon_gen_z12 -> osm_park_polygon_gen_z11 + park_polygon_gen_z11: + source: park_polygon_gen_z12 sql_filter: area>power(ZRES10,2) tolerance: ZRES11 - # etldoc: imposm3 -> osm_park_polygon_gen2 - park_polygon_gen2: - source: park_polygon_gen1 + # etldoc: osm_park_polygon_gen_z13 -> osm_park_polygon_gen_z12 + park_polygon_gen_z12: + source: park_polygon_gen_z13 sql_filter: area>power(ZRES11,2) tolerance: ZRES12 - # etldoc: imposm3 -> osm_park_polygon_gen1 - park_polygon_gen1: + # etldoc: osm_park_polygon -> osm_park_polygon_gen_z13 + park_polygon_gen_z13: source: park_polygon sql_filter: area>power(ZRES12,2) AND ST_IsValid(geometry) tolerance: ZRES13 @@ -52,7 +64,8 @@ tables: # etldoc: imposm3 -> osm_park_polygon park_polygon: type: polygon - fields: + _resolve_wikidata: false + columns: - name: osm_id type: id - name: geometry @@ -77,6 +90,9 @@ tables: - name: boundary key: boundary type: string + - name: protection_title + key: protection_title + type: string - name: area type: area mapping: @@ -84,3 +100,4 @@ tables: - nature_reserve boundary: - national_park + - protected_area diff --git a/layers/park/mapping_diagram.png b/layers/park/mapping_diagram.png index 0f1a4efa7..72d2edb8d 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 new file mode 100644 index 000000000..1d8f5b351 --- /dev/null +++ b/layers/park/park.sql @@ -0,0 +1,386 @@ +-- etldoc: layer_park[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_park | z4 | z5 | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; + +CREATE OR REPLACE FUNCTION layer_park(bbox geometry, zoom_level int, pixel_width numeric) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + class text, + name text, + name_en text, + name_de text, + tags hstore, + rank int + ) +AS +$$ +SELECT osm_id, + geometry, + class, + NULLIF(name, '') AS name, + NULLIF(name_en, '') AS name_en, + NULLIF(name_de, '') AS name_de, + tags, + rank +FROM ( + SELECT osm_id, + geometry, + COALESCE( + LOWER(REPLACE(NULLIF(protection_title, ''), ' ', '_')), + NULLIF(boundary, ''), + NULLIF(leisure, '') + ) AS class, + name, + name_en, + name_de, + tags, + NULL::int AS rank + FROM ( + -- etldoc: osm_park_polygon_dissolve_z4 -> layer_park:z4 + SELECT NULL::int AS osm_id, + geometry, + NULL AS name, + NULL AS name_en, + NULL AS name_de, + NULL AS tags, + NULL AS leisure, + NULL AS boundary, + NULL AS protection_title + FROM osm_park_polygon_dissolve_z4 + WHERE zoom_level = 4 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z5 -> layer_park:z5 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z5 + WHERE zoom_level = 5 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z6 -> layer_park:z6 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z6 + WHERE zoom_level = 6 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z7 -> layer_park:z7 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z7 + WHERE zoom_level = 7 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z8 -> layer_park:z8 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z8 + WHERE zoom_level = 8 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z9 -> layer_park:z9 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z9 + WHERE zoom_level = 9 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z10 -> layer_park:z10 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z10 + WHERE zoom_level = 10 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z11 -> layer_park:z11 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z11 + WHERE zoom_level = 11 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z12 -> layer_park:z12 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z12 + WHERE zoom_level = 12 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon_gen_z13 -> layer_park:z13 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon_gen_z13 + WHERE zoom_level = 13 + AND geometry && bbox + UNION ALL + -- etldoc: osm_park_polygon -> layer_park:z14 + SELECT osm_id, + geometry, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title + FROM osm_park_polygon + WHERE zoom_level >= 14 + AND geometry && bbox + ) AS park_polygon + + UNION ALL + SELECT osm_id, + geometry_point AS geometry, + COALESCE( + LOWER(REPLACE(NULLIF(protection_title, ''), ' ', '_')), + NULLIF(boundary, ''), + NULLIF(leisure, '') + ) AS class, + name, + name_en, + name_de, + tags, + row_number() OVER ( + PARTITION BY LabelGrid(geometry_point, 100 * pixel_width) + ORDER BY + (CASE WHEN boundary = 'national_park' THEN TRUE ELSE FALSE END) DESC, + (COALESCE(NULLIF(tags->'wikipedia', ''), NULLIF(tags->'wikidata', '')) IS NOT NULL) DESC, + area DESC + )::int AS "rank" + FROM ( + -- etldoc: osm_park_polygon_gen_z5 -> layer_park:z5 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z5 + WHERE zoom_level = 5 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z6 -> layer_park:z6 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z6 + WHERE zoom_level = 6 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z7 -> layer_park:z7 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z7 + WHERE zoom_level = 7 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z8 -> layer_park:z8 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z8 + WHERE zoom_level = 8 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z9 -> layer_park:z9 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z9 + WHERE zoom_level = 9 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z10 -> layer_park:z10 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z10 + WHERE zoom_level = 10 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z11 -> layer_park:z11 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z11 + WHERE zoom_level = 11 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z12 -> layer_park:z12 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z12 + WHERE zoom_level = 12 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon_gen_z13 -> layer_park:z13 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon_gen_z13 + WHERE zoom_level = 13 + AND geometry_point && bbox + AND area > 70000*2^(20-zoom_level) + UNION ALL + + -- etldoc: osm_park_polygon -> layer_park:z14 + SELECT osm_id, + geometry_point, + name, + name_en, + name_de, + tags, + leisure, + boundary, + protection_title, + area + FROM osm_park_polygon + WHERE zoom_level >= 14 + AND geometry_point && bbox + ) AS park_point + ) AS park_all; +$$ LANGUAGE SQL STABLE + PARALLEL SAFE; +-- TODO: Check if the above can be made STRICT -- i.e. if pixel_width could be NULL diff --git a/layers/park/park.yaml b/layers/park/park.yaml index 05c2cec7b..bd38b6510 100644 --- a/layers/park/park.yaml +++ b/layers/park/park.yaml @@ -1,15 +1,22 @@ layer: id: "park" description: | - The park layer contains parks from OpenStreetMap tagged with either [`boundary=national_park`](http://wiki.openstreetmap.org/wiki/Tag:boundary%3Dnational_park) or [`leisure=nature_reserve`](http://wiki.openstreetmap.org/wiki/Tag:leisure%3Dnature_reserve). + The park layer contains parks from OpenStreetMap tagged with + [`boundary=national_park`](http://wiki.openstreetmap.org/wiki/Tag:boundary%3Dnational_park), + [`boundary=protected_area`](http://wiki.openstreetmap.org/wiki/Tag:boundary%3Dprotected_area), + or [`leisure=nature_reserve`](http://wiki.openstreetmap.org/wiki/Tag:leisure%3Dnature_reserve). buffer_size: 4 fields: class: description: | Use the **class** to differentiate between different parks. - values: - - national_park - - nature_reserve + The class for `boundary=protected_area` parks is the lower-case of the + [`protection_title`](http://wiki.openstreetmap.org/wiki/key:protection_title) + value with blanks replaced by `_`. + `national_park` is the class of `protection_title=National Park` and `boundary=national_park`. + `nature_reserve` is the class of `protection_title=Nature Reserve` and `leisure=nature_reserve`. + The class for other [`protection_title`](http://wiki.openstreetmap.org/wiki/key:protection_title) + values is similarly assigned. name: The OSM [`name`](http://wiki.openstreetmap.org/wiki/Key:name) value of the park (point features only). 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). @@ -19,7 +26,7 @@ layer: query: (SELECT geometry, class, name, name_en, name_de, {name_languages}, rank FROM layer_park(!bbox!, z(!scale_denominator!), zres(z(!scale_denominator!))::numeric)) AS t schema: - ./update_park_polygon.sql - - ./layer.sql + - ./park.sql datasources: - type: imposm3 mapping_file: ./mapping.yaml diff --git a/layers/park/style.json b/layers/park/style.json new file mode 100644 index 000000000..528228b8a --- /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": 186 + } + ] +} \ No newline at end of file diff --git a/layers/park/update_park_polygon.sql b/layers/park/update_park_polygon.sql index 1dabc001c..69492692f 100644 --- a/layers/park/update_park_polygon.sql +++ b/layers/park/update_park_polygon.sql @@ -1,140 +1,208 @@ -ALTER TABLE osm_park_polygon ADD COLUMN IF NOT EXISTS geometry_point geometry; -ALTER TABLE osm_park_polygon_gen1 ADD COLUMN IF NOT EXISTS geometry_point geometry; -ALTER TABLE osm_park_polygon_gen2 ADD COLUMN IF NOT EXISTS geometry_point geometry; -ALTER TABLE osm_park_polygon_gen3 ADD COLUMN IF NOT EXISTS geometry_point geometry; -ALTER TABLE osm_park_polygon_gen4 ADD COLUMN IF NOT EXISTS geometry_point geometry; -ALTER TABLE osm_park_polygon_gen5 ADD COLUMN IF NOT EXISTS geometry_point geometry; -ALTER TABLE osm_park_polygon_gen6 ADD COLUMN IF NOT EXISTS geometry_point geometry; -ALTER TABLE osm_park_polygon_gen7 ADD COLUMN IF NOT EXISTS geometry_point geometry; -ALTER TABLE osm_park_polygon_gen8 ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z13 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z12 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z11 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z10 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z9 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z8 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z7 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z6 + ADD COLUMN IF NOT EXISTS geometry_point geometry; +ALTER TABLE osm_park_polygon_gen_z5 + ADD COLUMN IF NOT EXISTS geometry_point geometry; + +-- etldoc: osm_park_polygon_gen_z4 -> osm_park_polygon_dissolve_z4 +DROP MATERIALIZED VIEW IF EXISTS osm_park_polygon_dissolve_z4 CASCADE; +CREATE MATERIALIZED VIEW osm_park_polygon_dissolve_z4 AS +( + SELECT min(osm_id) AS osm_id, + ST_Union(geometry) AS geometry + FROM ( + SELECT ST_ClusterDBSCAN(geometry, 0, 1) OVER() AS cluster, + osm_id, + geometry + FROM osm_park_polygon_gen_z4 + ) park_cluster + GROUP BY 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_gen1; -DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen2; -DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen3; -DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen4; -DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen5; -DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen6; -DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen7; -DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen8; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z13; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z12; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z11; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z10; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z9; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z8; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z7; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z6; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z5; +DROP TRIGGER IF EXISTS update_row ON osm_park_polygon_gen_z4; -- etldoc: osm_park_polygon -> osm_park_polygon --- etldoc: osm_park_polygon_gen1 -> osm_park_polygon_gen1 --- etldoc: osm_park_polygon_gen2 -> osm_park_polygon_gen2 --- etldoc: osm_park_polygon_gen3 -> osm_park_polygon_gen3 --- etldoc: osm_park_polygon_gen4 -> osm_park_polygon_gen4 --- etldoc: osm_park_polygon_gen5 -> osm_park_polygon_gen5 --- etldoc: osm_park_polygon_gen6 -> osm_park_polygon_gen6 --- etldoc: osm_park_polygon_gen7 -> osm_park_polygon_gen7 --- etldoc: osm_park_polygon_gen8 -> osm_park_polygon_gen8 -CREATE OR REPLACE FUNCTION update_osm_park_polygon() RETURNS VOID AS $$ +-- etldoc: osm_park_polygon_gen_z13 -> osm_park_polygon_gen_z13 +-- etldoc: osm_park_polygon_gen_z12 -> osm_park_polygon_gen_z12 +-- etldoc: osm_park_polygon_gen_z11 -> osm_park_polygon_gen_z11 +-- etldoc: osm_park_polygon_gen_z10 -> osm_park_polygon_gen_z10 +-- etldoc: osm_park_polygon_gen_z9 -> osm_park_polygon_gen_z9 +-- etldoc: osm_park_polygon_gen_z8 -> osm_park_polygon_gen_z8 +-- etldoc: osm_park_polygon_gen_z7 -> osm_park_polygon_gen_z7 +-- etldoc: osm_park_polygon_gen_z6 -> osm_park_polygon_gen_z6 +-- etldoc: osm_park_polygon_gen_z5 -> osm_park_polygon_gen_z5 +-- etldoc: osm_park_polygon_gen_z4 -> osm_park_polygon_gen_z4 +CREATE OR REPLACE FUNCTION update_osm_park_polygon() RETURNS void AS +$$ BEGIN - UPDATE osm_park_polygon - SET tags = update_tags(tags, geometry), - geometry_point = st_centroid(geometry); + UPDATE osm_park_polygon + SET tags = update_tags(tags, geometry), + geometry_point = st_centroid(geometry); + + UPDATE osm_park_polygon_gen_z13 + SET tags = update_tags(tags, geometry), + geometry_point = st_centroid(geometry); - UPDATE osm_park_polygon_gen1 - SET tags = update_tags(tags, geometry), - geometry_point = st_centroid(geometry); + UPDATE osm_park_polygon_gen_z12 + SET tags = update_tags(tags, geometry), + geometry_point = st_centroid(geometry); - UPDATE osm_park_polygon_gen2 - SET tags = update_tags(tags, geometry), - geometry_point = st_centroid(geometry); + UPDATE osm_park_polygon_gen_z11 + SET tags = update_tags(tags, geometry), + geometry_point = st_centroid(geometry); - UPDATE osm_park_polygon_gen3 - SET tags = update_tags(tags, geometry), - geometry_point = st_centroid(geometry); + UPDATE osm_park_polygon_gen_z10 + SET tags = update_tags(tags, geometry), + geometry_point = st_centroid(geometry); - UPDATE osm_park_polygon_gen4 - SET tags = update_tags(tags, geometry), - geometry_point = st_centroid(geometry); + UPDATE osm_park_polygon_gen_z9 + SET tags = update_tags(tags, geometry), + geometry_point = st_centroid(geometry); - UPDATE osm_park_polygon_gen5 - SET tags = update_tags(tags, geometry), - geometry_point = st_centroid(geometry); + UPDATE osm_park_polygon_gen_z8 + SET tags = update_tags(tags, geometry), + geometry_point = st_centroid(geometry); - UPDATE osm_park_polygon_gen6 - SET tags = update_tags(tags, geometry), - geometry_point = st_centroid(geometry); + UPDATE osm_park_polygon_gen_z7 + SET tags = update_tags(tags, geometry), + geometry_point = st_centroid(geometry); - UPDATE osm_park_polygon_gen7 - SET tags = update_tags(tags, geometry), - geometry_point = st_centroid(geometry); + UPDATE osm_park_polygon_gen_z6 + SET tags = update_tags(tags, geometry), + geometry_point = st_centroid(geometry); - UPDATE osm_park_polygon_gen8 - 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; SELECT update_osm_park_polygon(); -CREATE INDEX IF NOT EXISTS osm_park_polygon_point_geom_idx ON osm_park_polygon USING gist(geometry_point); -CREATE INDEX IF NOT EXISTS osm_park_polygon_gen1_point_geom_idx ON osm_park_polygon_gen1 USING gist(geometry_point); -CREATE INDEX IF NOT EXISTS osm_park_polygon_gen2_point_geom_idx ON osm_park_polygon_gen2 USING gist(geometry_point); -CREATE INDEX IF NOT EXISTS osm_park_polygon_gen3_point_geom_idx ON osm_park_polygon_gen3 USING gist(geometry_point); -CREATE INDEX IF NOT EXISTS osm_park_polygon_gen4_point_geom_idx ON osm_park_polygon_gen4 USING gist(geometry_point); -CREATE INDEX IF NOT EXISTS osm_park_polygon_gen5_point_geom_idx ON osm_park_polygon_gen5 USING gist(geometry_point); -CREATE INDEX IF NOT EXISTS osm_park_polygon_gen6_point_geom_idx ON osm_park_polygon_gen6 USING gist(geometry_point); -CREATE INDEX IF NOT EXISTS osm_park_polygon_gen7_point_geom_idx ON osm_park_polygon_gen7 USING gist(geometry_point); -CREATE INDEX IF NOT EXISTS osm_park_polygon_gen8_point_geom_idx ON osm_park_polygon_gen8 USING gist(geometry_point); - +CREATE INDEX IF NOT EXISTS osm_park_polygon_point_geom_idx ON osm_park_polygon USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z13_point_geom_idx ON osm_park_polygon_gen_z13 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z12_point_geom_idx ON osm_park_polygon_gen_z12 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z11_point_geom_idx ON osm_park_polygon_gen_z11 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z10_point_geom_idx ON osm_park_polygon_gen_z10 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z9_point_geom_idx ON osm_park_polygon_gen_z9 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z8_point_geom_idx ON osm_park_polygon_gen_z8 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z7_point_geom_idx ON osm_park_polygon_gen_z7 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z6_point_geom_idx ON osm_park_polygon_gen_z6 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z5_point_geom_idx ON osm_park_polygon_gen_z5 USING gist (geometry_point); +CREATE INDEX IF NOT EXISTS osm_park_polygon_gen_z4_polygon_geom_idx ON osm_park_polygon_gen_z4 USING gist (geometry); +CREATE INDEX IF NOT EXISTS osm_park_polygon_dissolve_z4_polygon_geom_idx ON osm_park_polygon_dissolve_z4 USING gist (geometry); CREATE OR REPLACE FUNCTION update_osm_park_polygon_row() - RETURNS TRIGGER + RETURNS trigger +AS +$$ +BEGIN + NEW.tags = update_tags(NEW.tags, NEW.geometry); + NEW.geometry_point = st_centroid(NEW.geometry); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION update_osm_park_dissolved_polygon_row() + RETURNS trigger AS -$BODY$ +$$ BEGIN - NEW.tags = update_tags(NEW.tags, NEW.geometry); - NEW.geometry_point = st_centroid(NEW.geometry); - RETURN NEW; + NEW.tags = update_tags(NEW.tags, NEW.geometry); + RETURN NEW; END; -$BODY$ -LANGUAGE plpgsql; +$$ LANGUAGE plpgsql; CREATE TRIGGER update_row -BEFORE INSERT OR UPDATE ON osm_park_polygon -FOR EACH ROW + BEFORE INSERT OR UPDATE + ON osm_park_polygon + FOR EACH ROW EXECUTE PROCEDURE update_osm_park_polygon_row(); CREATE TRIGGER update_row -BEFORE INSERT OR UPDATE ON osm_park_polygon_gen1 -FOR EACH ROW + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z13 + FOR EACH ROW EXECUTE PROCEDURE update_osm_park_polygon_row(); CREATE TRIGGER update_row -BEFORE INSERT OR UPDATE ON osm_park_polygon_gen2 -FOR EACH ROW + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z12 + FOR EACH ROW EXECUTE PROCEDURE update_osm_park_polygon_row(); CREATE TRIGGER update_row -BEFORE INSERT OR UPDATE ON osm_park_polygon_gen3 -FOR EACH ROW + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z11 + FOR EACH ROW EXECUTE PROCEDURE update_osm_park_polygon_row(); CREATE TRIGGER update_row -BEFORE INSERT OR UPDATE ON osm_park_polygon_gen4 -FOR EACH ROW + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z10 + FOR EACH ROW EXECUTE PROCEDURE update_osm_park_polygon_row(); CREATE TRIGGER update_row -BEFORE INSERT OR UPDATE ON osm_park_polygon_gen5 -FOR EACH ROW + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z9 + FOR EACH ROW EXECUTE PROCEDURE update_osm_park_polygon_row(); CREATE TRIGGER update_row -BEFORE INSERT OR UPDATE ON osm_park_polygon_gen6 -FOR EACH ROW + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z8 + FOR EACH ROW EXECUTE PROCEDURE update_osm_park_polygon_row(); CREATE TRIGGER update_row -BEFORE INSERT OR UPDATE ON osm_park_polygon_gen7 -FOR EACH ROW + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z7 + FOR EACH ROW EXECUTE PROCEDURE update_osm_park_polygon_row(); CREATE TRIGGER update_row -BEFORE INSERT OR UPDATE ON osm_park_polygon_gen8 -FOR EACH ROW + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z6 + FOR EACH ROW EXECUTE PROCEDURE update_osm_park_polygon_row(); +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z5 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_park_polygon_row(); +CREATE TRIGGER update_row + BEFORE INSERT OR UPDATE + ON osm_park_polygon_gen_z4 + FOR EACH ROW +EXECUTE PROCEDURE update_osm_park_dissolved_polygon_row(); diff --git a/layers/place/capital.sql b/layers/place/capital.sql index e132530cc..112210942 100644 --- a/layers/place/capital.sql +++ b/layers/place/capital.sql @@ -1,8 +1,10 @@ -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 - ELSE NULL - END; -$$ LANGUAGE SQL IMMUTABLE STRICT; +CREATE OR REPLACE FUNCTION normalize_capital_level(capital text) + RETURNS int AS +$$ +SELECT CASE + WHEN capital = 'yes' THEN 2 + WHEN capital IN ('2', '3', '4', '5', '6') THEN capital::int + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; diff --git a/layers/place/city.sql b/layers/place/city.sql index 59b6286d6..41438d5cf 100644 --- a/layers/place/city.sql +++ b/layers/place/city.sql @@ -1,55 +1,79 @@ - -- etldoc: layer_city[shape=record fillcolor=lightpink, style="rounded,filled", -- etldoc: label="layer_city | z2-z14+" ] ; -- etldoc: osm_city_point -> layer_city:z2_14 CREATE OR REPLACE FUNCTION layer_city(bbox geometry, zoom_level int, pixel_width numeric) -RETURNS TABLE(osm_id bigint, geometry geometry, name text, name_en text, name_de text, tags hstore, place city_place, "rank" int, capital int) AS $$ - SELECT * FROM ( - SELECT osm_id, geometry, name, - COALESCE(NULLIF(name_en, ''), name) AS name_en, - COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, - tags, - place, "rank", normalize_capital_level(capital) AS capital - FROM osm_city_point - WHERE geometry && bbox - AND ((zoom_level = 2 AND "rank" = 1) - OR (zoom_level BETWEEN 3 AND 7 AND "rank" <= zoom_level + 1) - ) - UNION ALL - SELECT osm_id, geometry, name, - COALESCE(NULLIF(name_en, ''), name) AS name_en, - COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, - tags, - place, - COALESCE("rank", gridrank + 10), - normalize_capital_level(capital) AS capital - FROM ( - SELECT osm_id, geometry, name, - COALESCE(NULLIF(name_en, ''), name) AS name_en, - COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, - tags, - place, "rank", capital, - row_number() OVER ( - PARTITION BY LabelGrid(geometry, 128 * pixel_width) - ORDER BY "rank" ASC NULLS LAST, - place ASC NULLS LAST, - population DESC NULLS LAST, - length(name) ASC - )::int AS gridrank - FROM osm_city_point - WHERE geometry && bbox - AND ((zoom_level = 7 AND place <= 'town'::city_place - OR (zoom_level BETWEEN 8 AND 10 AND place <= 'village'::city_place) - - OR (zoom_level BETWEEN 11 AND 13 AND place <= 'suburb'::city_place) - OR (zoom_level >= 14) - )) - ) AS ranked_places - WHERE (zoom_level BETWEEN 7 AND 8 AND (gridrank <= 4 OR "rank" IS NOT NULL)) - OR (zoom_level = 9 AND (gridrank <= 8 OR "rank" IS NOT NULL)) - OR (zoom_level = 10 AND (gridrank <= 12 OR "rank" IS NOT NULL)) - OR (zoom_level BETWEEN 11 AND 12 AND (gridrank <= 14 OR "rank" IS NOT NULL)) - OR (zoom_level >= 13) - ) as city_all; -$$ LANGUAGE SQL IMMUTABLE; + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + name text, + name_en text, + name_de text, + tags hstore, + place city_place, + "rank" int, + capital int + ) +AS +$$ +SELECT * +FROM ( + SELECT osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + place, + "rank", + normalize_capital_level(capital) AS capital + FROM osm_city_point + WHERE geometry && bbox + AND ((zoom_level = 2 AND "rank" = 1) + OR (zoom_level BETWEEN 3 AND 7 AND "rank" <= zoom_level + 1) + ) + UNION ALL + SELECT osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + place, + COALESCE("rank", gridrank + 10), + normalize_capital_level(capital) AS capital + FROM ( + SELECT osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + place, + "rank", + capital, + row_number() OVER ( + PARTITION BY LabelGrid(geometry, 128 * pixel_width) + ORDER BY "rank" ASC NULLS LAST, + place ASC NULLS LAST, + population DESC NULLS LAST, + length(name) ASC + )::int AS gridrank + FROM osm_city_point + WHERE geometry && bbox + AND ((zoom_level = 7 AND place <= 'town'::city_place + OR (zoom_level BETWEEN 8 AND 10 AND place <= 'village'::city_place) + OR (zoom_level BETWEEN 11 AND 13 AND place <= 'suburb'::city_place) + OR (zoom_level >= 14) + )) + ) AS ranked_places + WHERE (zoom_level BETWEEN 7 AND 8 AND (gridrank <= 4 OR "rank" IS NOT NULL)) + OR (zoom_level = 9 AND (gridrank <= 8 OR "rank" IS NOT NULL)) + OR (zoom_level = 10 AND (gridrank <= 12 OR "rank" IS NOT NULL)) + OR (zoom_level BETWEEN 11 AND 12 AND (gridrank <= 14 OR "rank" IS NOT NULL)) + OR (zoom_level >= 13) + ) AS city_all; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/place/etl_diagram.png b/layers/place/etl_diagram.png index f63eb6ca9..de2cb08b6 100644 Binary files a/layers/place/etl_diagram.png and b/layers/place/etl_diagram.png differ diff --git a/layers/place/island_rank.sql b/layers/place/island_rank.sql index 9d631248a..f7ef3efa0 100644 --- a/layers/place/island_rank.sql +++ b/layers/place/island_rank.sql @@ -1,9 +1,12 @@ -CREATE OR REPLACE FUNCTION island_rank(area REAL) RETURNS INT AS $$ - SELECT CASE - WHEN area < 10000000 THEN 6 - WHEN area BETWEEN 1000000 AND 15000000 THEN 5 - WHEN area BETWEEN 15000000 AND 40000000 THEN 4 - WHEN area > 40000000 THEN 3 - ELSE 7 - END; -$$ LANGUAGE SQL IMMUTABLE STRICT; +CREATE OR REPLACE FUNCTION island_rank(area real) RETURNS int AS +$$ +SELECT CASE + WHEN area < 10000000 THEN 6 + WHEN area BETWEEN 1000000 AND 15000000 THEN 5 + WHEN area BETWEEN 15000000 AND 40000000 THEN 4 + WHEN area > 40000000 THEN 3 + ELSE 7 + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; diff --git a/layers/place/layer.sql b/layers/place/layer.sql deleted file mode 100644 index 251454ef2..000000000 --- a/layers/place/layer.sql +++ /dev/null @@ -1,111 +0,0 @@ - --- etldoc: layer_place[shape=record fillcolor=lightpink, style="rounded,filled", --- etldoc: label="layer_place | z0-3| z4-7| z8-11| z12-z14+" ] ; - -CREATE OR REPLACE FUNCTION layer_place(bbox geometry, zoom_level int, pixel_width numeric) -RETURNS TABLE(osm_id bigint, geometry geometry, name text, name_en text, - name_de text, tags hstore, class text, "rank" int, capital INT, iso_a2 - TEXT) AS $$ - SELECT * FROM ( - - -- etldoc: osm_continent_point -> layer_place:z0_3 - SELECT - osm_id*10, geometry, name, - COALESCE(NULLIF(name_en, ''), name) AS name_en, - COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, - tags, - 'continent' AS class, 1 AS "rank", NULL::int AS capital, - NULL::text AS iso_a2 - FROM osm_continent_point - WHERE geometry && bbox AND zoom_level < 4 - UNION ALL - - -- etldoc: osm_country_point -> layer_place:z0_3 - -- etldoc: osm_country_point -> layer_place:z4_7 - -- etldoc: osm_country_point -> layer_place:z8_11 - -- etldoc: osm_country_point -> layer_place:z12_14 - SELECT - osm_id*10, geometry, name, - COALESCE(NULLIF(name_en, ''), name) AS name_en, - COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, - tags, - 'country' AS class, "rank", NULL::int AS capital, - iso3166_1_alpha_2 AS iso_a2 - FROM osm_country_point - WHERE geometry && bbox AND "rank" <= zoom_level + 1 AND name <> '' - UNION ALL - - -- etldoc: osm_state_point -> layer_place:z0_3 - -- etldoc: osm_state_point -> layer_place:z4_7 - -- etldoc: osm_state_point -> layer_place:z8_11 - -- etldoc: osm_state_point -> layer_place:z12_14 - SELECT - osm_id*10, geometry, name, - COALESCE(NULLIF(name_en, ''), name) AS name_en, - COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, - tags, - 'state' AS class, "rank", NULL::int AS capital, - NULL::text AS iso_a2 - FROM osm_state_point - WHERE geometry && bbox AND - name <> '' AND - ("rank" + 2 <= zoom_level) AND ( - zoom_level >= 5 OR - is_in_country IN ('United Kingdom', 'USA', 'Россия', 'Brasil', 'China', 'India') OR - is_in_country_code IN ('AU', 'CN', 'IN', 'BR', 'US')) - UNION ALL - - -- etldoc: osm_island_point -> layer_place:z12_14 - SELECT - osm_id*10, geometry, name, - COALESCE(NULLIF(name_en, ''), name) AS name_en, - COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, - tags, - 'island' AS class, 7 AS "rank", NULL::int AS capital, - NULL::text AS iso_a2 - FROM osm_island_point - WHERE zoom_level >= 12 - AND geometry && bbox - UNION ALL - - -- etldoc: osm_island_polygon -> layer_place:z8_11 - -- etldoc: osm_island_polygon -> layer_place:z12_14 - SELECT - osm_id*10, geometry, name, - COALESCE(NULLIF(name_en, ''), name) AS name_en, - COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, - tags, - 'island' AS class, island_rank(area) AS "rank", NULL::int AS capital, - NULL::text AS iso_a2 - FROM osm_island_polygon - WHERE geometry && bbox AND - ((zoom_level = 8 AND island_rank(area) <= 3) - OR (zoom_level = 9 AND island_rank(area) <= 4) - OR (zoom_level >= 10)) - UNION ALL - - -- etldoc: layer_city -> layer_place:z0_3 - -- etldoc: layer_city -> layer_place:z4_7 - -- etldoc: layer_city -> layer_place:z8_11 - -- etldoc: layer_city -> layer_place:z12_14 - SELECT - osm_id*10, geometry, name, name_en, name_de, - tags, - place::text AS class, "rank", capital, - NULL::text AS iso_a2 - FROM layer_city(bbox, zoom_level, pixel_width) - ORDER BY "rank" ASC - ) AS place_all -$$ LANGUAGE SQL IMMUTABLE; - - -CREATE OR REPLACE FUNCTION layer_place_lite(bbox geometry, zoom_level int, pixel_width numeric) -RETURNS TABLE(osm_id bigint, geometry geometry, name text, name_en text, - name_de text, tags hstore, class text, "rank" int, capital INT, iso_a2 - TEXT) AS $$ - SELECT * FROM layer_place(bbox, zoom_level, pixel_width) - WHERE zoom_level < 13 - UNION ALL - SELECT * FROM layer_place(bbox, 14, pixel_width) - WHERE zoom_level >= 13 -$$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/place/mapping.yaml b/layers/place/mapping.yaml index 15d7ef5a0..c7c4e5093 100644 --- a/layers/place/mapping.yaml +++ b/layers/place/mapping.yaml @@ -20,7 +20,7 @@ tables: # etldoc: imposm3 -> osm_continent_point continent_point: type: point - fields: + columns: - name: osm_id type: id - name: geometry @@ -40,7 +40,7 @@ tables: # etldoc: imposm3 -> osm_country_point country_point: type: point - fields: + columns: - name: osm_id type: id - name: geometry @@ -70,7 +70,7 @@ tables: # etldoc: imposm3 -> osm_island_polygon island_polygon: type: polygon - fields: + columns: - name: osm_id type: id - name: geometry @@ -93,7 +93,7 @@ tables: # etldoc: imposm3 -> osm_island_point island_point: type: point - fields: + columns: - name: osm_id type: id - name: geometry @@ -114,7 +114,7 @@ tables: # etldoc: imposm3 -> osm_state_point state_point: type: point - fields: + columns: - name: osm_id type: id - name: geometry @@ -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,11 +143,12 @@ tables: mapping: place: - state + - province # etldoc: imposm3 -> osm_city_point city_point: type: point - fields: + columns: - name: osm_id type: id - name: geometry @@ -174,5 +178,6 @@ tables: - village - hamlet - suburb + - quarter - neighbourhood - isolated_dwelling diff --git a/layers/place/mapping_diagram.png b/layers/place/mapping_diagram.png index 6a43d749e..ae3581369 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 new file mode 100644 index 000000000..c14b78f85 --- /dev/null +++ b/layers/place/place.sql @@ -0,0 +1,155 @@ +-- etldoc: layer_place[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_place | z0-3| z4-7| z8-11| z12-z14+" ] ; + +CREATE OR REPLACE FUNCTION layer_place(bbox geometry, zoom_level int, pixel_width numeric) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + name text, + name_en text, + name_de text, + tags hstore, + class text, + "rank" int, + capital int, + iso_a2 text + ) +AS +$$ +SELECT * +FROM ( + SELECT + -- etldoc: osm_continent_point -> layer_place:z0_3 + osm_id * 10 AS osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + 'continent' AS class, + 1 AS "rank", + NULL::int AS capital, + NULL::text AS iso_a2 + FROM osm_continent_point + WHERE geometry && bbox + AND zoom_level < 4 + + UNION ALL + + SELECT + -- etldoc: osm_country_point -> layer_place:z0_3 + -- etldoc: osm_country_point -> layer_place:z4_7 + -- etldoc: osm_country_point -> layer_place:z8_11 + -- etldoc: osm_country_point -> layer_place:z12_14 + osm_id * 10 AS osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + 'country' AS class, + "rank", + NULL::int AS capital, + iso3166_1_alpha_2 AS iso_a2 + FROM osm_country_point + WHERE geometry && bbox + AND "rank" <= zoom_level + 1 + AND name <> '' + + UNION ALL + + SELECT + -- etldoc: osm_state_point -> layer_place:z0_3 + -- etldoc: osm_state_point -> layer_place:z4_7 + -- etldoc: osm_state_point -> layer_place:z8_11 + -- etldoc: osm_state_point -> layer_place:z12_14 + osm_id * 10 AS osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + place::text AS class, + "rank", + NULL::int AS capital, + NULL::text AS iso_a2 + FROM osm_state_point + WHERE geometry && bbox + AND name <> '' + AND zoom_level > 1 + + UNION ALL + + SELECT + -- etldoc: osm_island_point -> layer_place:z12_14 + osm_id * 10 AS osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + 'island' AS class, + 7 AS "rank", + NULL::int AS capital, + NULL::text AS iso_a2 + FROM osm_island_point + WHERE zoom_level >= 12 + AND geometry && bbox + + UNION ALL + + SELECT + -- etldoc: osm_island_polygon -> layer_place:z8_11 + -- etldoc: osm_island_polygon -> layer_place:z12_14 + osm_id * 10 AS osm_id, + geometry, + name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + 'island' AS class, + island_rank(area) AS "rank", + NULL::int AS capital, + NULL::text AS iso_a2 + FROM osm_island_polygon + WHERE geometry && bbox + AND ((zoom_level = 8 AND island_rank(area) <= 3) + OR (zoom_level = 9 AND island_rank(area) <= 4) + OR (zoom_level >= 10)) + + UNION ALL + + SELECT + -- etldoc: layer_city -> layer_place:z0_3 + -- etldoc: layer_city -> layer_place:z4_7 + -- etldoc: layer_city -> layer_place:z8_11 + -- etldoc: layer_city -> layer_place:z12_14 + osm_id * 10 AS osm_id, + geometry, + name, + name_en, + name_de, + tags, + place::text AS class, + "rank", + capital, + NULL::text AS iso_a2 + FROM layer_city(bbox, zoom_level, pixel_width) + ORDER BY "rank" ASC + ) AS place_all +$$ LANGUAGE SQL STABLE + PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION layer_place_lite(bbox geometry, zoom_level int, pixel_width numeric) +RETURNS TABLE(osm_id bigint, geometry geometry, name text, name_en text, + name_de text, tags hstore, class text, "rank" int, capital INT, iso_a2 + TEXT) AS $$ +SELECT * FROM layer_place(bbox, zoom_level, pixel_width) +WHERE zoom_level < 13 +UNION ALL +SELECT * FROM layer_place(bbox, 14, pixel_width) +WHERE zoom_level >= 13 +$$ LANGUAGE SQL STABLE + PARALLEL SAFE; +-- TODO: Check if the above can be made STRICT -- i.e. if pixel_width could be NULL diff --git a/layers/place/place.yaml b/layers/place/place.yaml index 8e4b6d0df..45de229b2 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,23 +33,26 @@ layer: - continent - country - state + - province - city - town - village - hamlet - 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). + Two-letter country code [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). Available only for `class=country`. Original value of the [`country_code_iso3166_1_alpha_2`](http://wiki.openstreetmap.org/wiki/Tag:place%3Dcountry) tag. rank: description: | Countries, states and the most important cities all have a **rank** to boost their importance on the map. - The **rank** field for 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 @@ -72,7 +81,7 @@ schema: - ./update_island_point.sql - ./update_state_point.sql - ./update_city_point.sql - - ./layer.sql + - ./place.sql datasources: - type: imposm3 mapping_file: ./mapping.yaml diff --git a/layers/place/style.json b/layers/place/style.json new file mode 100644 index 000000000..7a0fad1b4 --- /dev/null +++ b/layers/place/style.json @@ -0,0 +1,661 @@ +{ + "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" + ] + ], + "order": 184 + }, + { + "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": 189 + }, + { + "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": 190 + }, + { + "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": 191 + }, + { + "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": 192 + }, + { + "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": 193 + }, + { + "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": 194 + }, + { + "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": 195 + }, + { + "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": 196 + }, + { + "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": 197 + } + ] +} \ No newline at end of file diff --git a/layers/place/types.sql b/layers/place/types.sql index f7fdedfcd..eea6c8661 100644 --- a/layers/place/types.sql +++ b/layers/place/types.sql @@ -1,9 +1,12 @@ -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', 'neighbourhood', 'isolated_dwelling'); - END IF; -END +DO +$$ + BEGIN + PERFORM 'city_place'::regtype; + EXCEPTION + WHEN undefined_object THEN + CREATE TYPE city_place AS enum ('city', 'town', 'village', 'hamlet', 'suburb', 'quarter', 'neighbourhood', 'isolated_dwelling'); + END $$; -ALTER TABLE osm_city_point ALTER COLUMN place TYPE city_place USING place::city_place; +ALTER TABLE osm_city_point + ALTER COLUMN place TYPE city_place USING place::city_place; diff --git a/layers/place/update_city_point.sql b/layers/place/update_city_point.sql index 9087c62c1..3d43fcee4 100644 --- a/layers/place/update_city_point.sql +++ b/layers/place/update_city_point.sql @@ -1,87 +1,116 @@ DROP TRIGGER IF EXISTS trigger_flag ON osm_city_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_city_point; DROP TRIGGER IF EXISTS trigger_refresh ON place_city.updates; CREATE EXTENSION IF NOT EXISTS unaccent; -CREATE OR REPLACE FUNCTION update_osm_city_point() RETURNS VOID AS $$ -BEGIN +CREATE SCHEMA IF NOT EXISTS place_city; - -- Clear OSM key:rank ( https://github.com/openmaptiles/openmaptiles/issues/108 ) - -- etldoc: osm_city_point -> osm_city_point - UPDATE osm_city_point AS osm SET "rank" = NULL WHERE "rank" IS NOT NULL; - - -- etldoc: ne_10m_populated_places -> osm_city_point - -- etldoc: osm_city_point -> osm_city_point - - WITH important_city_point AS ( - SELECT osm.geometry, osm.osm_id, osm.name, osm.name_en, ne.scalerank, ne.labelrank - FROM ne_10m_populated_places AS ne, osm_city_point AS osm - WHERE - ( - (osm.tags ? 'wikidata' AND osm.tags->'wikidata' = ne.wikidataid) OR - ne.name ILIKE osm.name OR - ne.name ILIKE osm.name_en OR - ne.namealt ILIKE osm.name OR - ne.namealt ILIKE osm.name_en OR - ne.name_en ILIKE osm.name OR - ne.name_en ILIKE osm.name_en OR - ne.meganame ILIKE osm.name OR - ne.meganame ILIKE osm.name_en OR - ne.nameascii ILIKE osm.name OR - ne.nameascii ILIKE osm.name_en OR - ne.name = unaccent(osm.name) - ) - AND osm.place IN ('city', 'town', 'village') - AND ST_DWithin(ne.geometry, osm.geometry, 50000) - ) - UPDATE osm_city_point AS osm - -- Move scalerank to range 1 to 10 and merge scalerank 5 with 6 since not enough cities - -- are in the scalerank 5 bucket - SET "rank" = CASE WHEN scalerank <= 5 THEN scalerank + 1 ELSE scalerank END - FROM important_city_point AS ne - WHERE osm.osm_id = ne.osm_id; - - UPDATE osm_city_point - SET tags = update_tags(tags, geometry) - WHERE COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL; +CREATE TABLE IF NOT EXISTS place_city.osm_ids +( + osm_id bigint +); -END; -$$ LANGUAGE plpgsql; +CREATE OR REPLACE FUNCTION update_osm_city_point(full_update boolean) RETURNS void AS +$$ + -- etldoc: ne_10m_populated_places -> osm_city_point + -- etldoc: osm_city_point -> osm_city_point + + WITH important_city_point AS ( + SELECT osm.osm_id, ne.scalerank + FROM osm_city_point AS osm + -- Clear OSM key:rank ( https://github.com/openmaptiles/openmaptiles/issues/108 ) + LEFT JOIN ne_10m_populated_places AS ne ON + ( + (osm.tags ? 'wikidata' AND osm.tags->'wikidata' = ne.wikidataid) OR + lower(osm.name) IN (lower(ne.name), lower(ne.namealt), lower(ne.meganame), lower(ne.name_en), lower(ne.nameascii)) OR + lower(osm.name_en) IN (lower(ne.name), lower(ne.namealt), lower(ne.meganame), lower(ne.name_en), lower(ne.nameascii)) OR + ne.name = unaccent(osm.name) + ) + AND osm.place IN ('city', 'town', 'village') + AND ST_DWithin(ne.geometry, osm.geometry, 50000) + ) + UPDATE osm_city_point AS osm + -- Move scalerank to range 1 to 10 and merge scalerank 5 with 6 since not enough cities + -- are in the scalerank 5 bucket + SET "rank" = CASE WHEN scalerank <= 5 THEN scalerank + 1 ELSE scalerank END + FROM important_city_point AS ne + WHERE (full_update OR osm.osm_id IN (SELECT osm_id FROM place_city.osm_ids)) + AND rank IS DISTINCT FROM CASE WHEN scalerank <= 5 THEN scalerank + 1 ELSE scalerank END + AND osm.osm_id = ne.osm_id; -SELECT update_osm_city_point(); + UPDATE osm_city_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_city.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); -CREATE INDEX IF NOT EXISTS osm_city_point_rank_idx ON osm_city_point("rank"); +$$ LANGUAGE SQL; + +SELECT update_osm_city_point(true); + +CREATE INDEX IF NOT EXISTS osm_city_point_rank_idx ON osm_city_point ("rank"); -- Handle updates -CREATE SCHEMA IF NOT EXISTS place_city; +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; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; -CREATE TABLE IF NOT EXISTS place_city.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION place_city.flag() RETURNS trigger AS $$ +CREATE TABLE IF NOT EXISTS place_city.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION place_city.flag() RETURNS trigger AS +$$ BEGIN - INSERT INTO place_city.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + INSERT INTO place_city.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; END; -$$ language plpgsql; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION place_city.refresh() RETURNS trigger AS - $BODY$ - BEGIN +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN RAISE LOG 'Refresh place_city rank'; - PERFORM update_osm_city_point(); + PERFORM update_osm_city_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM place_city.osm_ids; + -- noinspection SqlWithoutWhere DELETE FROM place_city.updates; - RETURN null; - END; - $BODY$ -language plpgsql; + + RAISE LOG 'Refresh place_city done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_city_point + FOR EACH ROW +EXECUTE PROCEDURE place_city.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE ON osm_city_point + AFTER INSERT OR UPDATE OR DELETE + ON osm_city_point FOR EACH STATEMENT - EXECUTE PROCEDURE place_city.flag(); +EXECUTE PROCEDURE place_city.flag(); CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON place_city.updates + AFTER INSERT + ON place_city.updates INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE place_city.refresh(); +EXECUTE PROCEDURE place_city.refresh(); \ No newline at end of file diff --git a/layers/place/update_continent_point.sql b/layers/place/update_continent_point.sql index 9e48c54e7..5e944a2a7 100644 --- a/layers/place/update_continent_point.sql +++ b/layers/place/update_continent_point.sql @@ -1,48 +1,86 @@ DROP TRIGGER IF EXISTS trigger_flag ON osm_continent_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_continent_point; DROP TRIGGER IF EXISTS trigger_refresh ON place_continent_point.updates; --- etldoc: osm_continent_point -> osm_continent_point -CREATE OR REPLACE FUNCTION update_osm_continent_point() RETURNS VOID AS $$ -BEGIN - UPDATE osm_continent_point - SET tags = update_tags(tags, geometry) - WHERE COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL; +CREATE SCHEMA IF NOT EXISTS place_continent_point; -END; -$$ LANGUAGE plpgsql; +CREATE TABLE IF NOT EXISTS place_continent_point.osm_ids +( + osm_id bigint +); -SELECT update_osm_continent_point(); +-- etldoc: osm_continent_point -> osm_continent_point +CREATE OR REPLACE FUNCTION update_osm_continent_point(full_update boolean) RETURNS void AS +$$ + UPDATE osm_continent_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_continent_point.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); +$$ LANGUAGE SQL; + +SELECT update_osm_continent_point(true); -- Handle updates -CREATE SCHEMA IF NOT EXISTS place_continent_point; +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; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; -CREATE TABLE IF NOT EXISTS place_continent_point.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION place_continent_point.flag() RETURNS trigger AS $$ +CREATE TABLE IF NOT EXISTS place_continent_point.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION place_continent_point.flag() RETURNS trigger AS +$$ BEGIN - INSERT INTO place_continent_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + INSERT INTO place_continent_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; END; -$$ language plpgsql; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION place_continent_point.refresh() RETURNS trigger AS - $BODY$ - BEGIN +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN RAISE LOG 'Refresh place_continent_point'; - PERFORM update_osm_continent_point(); + PERFORM update_osm_continent_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM place_continent_point.osm_ids; + -- noinspection SqlWithoutWhere DELETE FROM place_continent_point.updates; - RETURN null; - END; - $BODY$ -language plpgsql; + + RAISE LOG 'Refresh place_continent_point done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_continent_point + FOR EACH ROW +EXECUTE PROCEDURE place_continent_point.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE ON osm_continent_point + AFTER INSERT OR UPDATE OR DELETE + ON osm_continent_point FOR EACH STATEMENT - EXECUTE PROCEDURE place_continent_point.flag(); +EXECUTE PROCEDURE place_continent_point.flag(); CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON place_continent_point.updates + AFTER INSERT + ON place_continent_point.updates INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE place_continent_point.refresh(); +EXECUTE PROCEDURE place_continent_point.refresh(); diff --git a/layers/place/update_country_point.sql b/layers/place/update_country_point.sql index 175a7226c..a4cc79df1 100644 --- a/layers/place/update_country_point.sql +++ b/layers/place/update_country_point.sql @@ -1,119 +1,165 @@ DROP TRIGGER IF EXISTS trigger_flag ON osm_country_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_country_point; DROP TRIGGER IF EXISTS trigger_refresh ON place_country.updates; -ALTER TABLE osm_country_point DROP CONSTRAINT IF EXISTS osm_country_point_rank_constraint; +CREATE SCHEMA IF NOT EXISTS place_country; + +CREATE TABLE IF NOT EXISTS place_country.osm_ids +( + osm_id bigint +); -- etldoc: ne_10m_admin_0_countries -> osm_country_point -- etldoc: osm_country_point -> osm_country_point -CREATE OR REPLACE FUNCTION update_osm_country_point() RETURNS VOID AS $$ -BEGIN - - UPDATE osm_country_point AS osm - SET - "rank" = 7, - iso3166_1_alpha_2 = COALESCE( - NULLIF(osm.country_code_iso3166_1_alpha_2, ''), - NULLIF(osm.iso3166_1_alpha_2, ''), - NULLIF(osm.iso3166_1, '') +CREATE OR REPLACE FUNCTION update_osm_country_point(full_update boolean) RETURNS void AS +$$ + UPDATE osm_country_point AS osm + SET "rank" = 7, + iso3166_1_alpha_2 = COALESCE( + NULLIF(osm.country_code_iso3166_1_alpha_2, ''), + NULLIF(osm.iso3166_1_alpha_2, ''), + NULLIF(osm.iso3166_1, '') + ) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_country.osm_ids)) + AND rank IS NULL; + + WITH important_country_point AS ( + SELECT osm.geometry, + osm.osm_id, + osm.name, + COALESCE(NULLIF(osm.name_en, ''), ne.name) AS name_en, + ne.scalerank, + ne.labelrank + FROM ne_10m_admin_0_countries AS ne, + osm_country_point AS osm + WHERE + -- We match only countries with ISO codes to eliminate disputed countries + iso3166_1_alpha_2 IS NOT NULL + -- that lies inside polygon of sovereign country + AND ST_Within(osm.geometry, ne.geometry) ) - ; - - WITH important_country_point AS ( - SELECT osm.geometry, osm.osm_id, osm.name, COALESCE(NULLIF(osm.name_en, ''), ne.name) AS name_en, ne.scalerank, ne.labelrank - FROM ne_10m_admin_0_countries AS ne, osm_country_point AS osm - WHERE - -- We match only countries with ISO codes to eliminate disputed countries - iso3166_1_alpha_2 IS NOT NULL - -- that lies inside polygon of sovereign country - AND ST_Within(osm.geometry, ne.geometry) - ) - UPDATE osm_country_point AS osm - -- Normalize both scalerank and labelrank into a ranking system from 1 to 6 - -- where the ranks are still distributed uniform enough across all countries - SET "rank" = LEAST(6, CEILING((scalerank + labelrank)/2.0)) - FROM important_country_point AS ne - WHERE osm.osm_id = ne.osm_id; - - -- Repeat the step for archipelago countries like Philippines or Indonesia - -- whose label point is not within country's polygon - WITH important_country_point AS ( - SELECT - osm.osm_id, + UPDATE osm_country_point AS osm + -- Normalize both scalerank and labelrank into a ranking system from 1 to 6 + -- where the ranks are still distributed uniform enough across all countries + SET "rank" = LEAST(6, CEILING((scalerank + labelrank) / 2.0)) + FROM important_country_point AS ne + WHERE (full_update OR osm.osm_id IN (SELECT osm_id FROM place_country.osm_ids)) + AND rank = 7 + AND osm.osm_id = ne.osm_id; + + -- Repeat the step for archipelago countries like Philippines or Indonesia + -- whose label point is not within country's polygon + WITH important_country_point AS ( + SELECT osm.osm_id, -- osm.name, - ne.scalerank, - ne.labelrank, + ne.scalerank, + ne.labelrank, -- ST_Distance(osm.geometry, ne.geometry) AS distance, - ROW_NUMBER() - OVER ( - PARTITION BY osm.osm_id - ORDER BY - ST_Distance(osm.geometry, ne.geometry) - ) AS rk - FROM osm_country_point osm, - ne_10m_admin_0_countries AS ne - WHERE - iso3166_1_alpha_2 IS NOT NULL - AND NOT (osm."rank" BETWEEN 1 AND 6) - ) - UPDATE osm_country_point AS osm - -- Normalize both scalerank and labelrank into a ranking system from 1 to 6 - -- where the ranks are still distributed uniform enough across all countries - SET "rank" = LEAST(6, CEILING((ne.scalerank + ne.labelrank)/2.0)) - FROM important_country_point AS ne - WHERE osm.osm_id = ne.osm_id AND ne.rk = 1; - - UPDATE osm_country_point AS osm - SET "rank" = 6 - WHERE "rank" = 7; - - -- TODO: This shouldn't be necessary? The rank function makes something wrong... - UPDATE osm_country_point AS osm - SET "rank" = 1 - WHERE "rank" = 0; - - UPDATE osm_country_point - SET tags = update_tags(tags, geometry) - WHERE COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL; - -END; -$$ LANGUAGE plpgsql; - -SELECT update_osm_country_point(); - --- ALTER TABLE osm_country_point ADD CONSTRAINT osm_country_point_rank_constraint CHECK("rank" BETWEEN 1 AND 6); -CREATE INDEX IF NOT EXISTS osm_country_point_rank_idx ON osm_country_point("rank"); + ROW_NUMBER() + OVER ( + PARTITION BY osm.osm_id + ORDER BY + ST_Distance(osm.geometry, ne.geometry) + ) AS rk + FROM osm_country_point osm, + ne_10m_admin_0_countries AS ne + WHERE iso3166_1_alpha_2 IS NOT NULL + AND NOT (osm."rank" BETWEEN 1 AND 6) + ) + UPDATE osm_country_point AS osm + -- Normalize both scalerank and labelrank into a ranking system from 1 to 6 + -- where the ranks are still distributed uniform enough across all countries + SET "rank" = LEAST(6, CEILING((ne.scalerank + ne.labelrank) / 2.0)) + FROM important_country_point AS ne + WHERE (full_update OR osm.osm_id IN (SELECT osm_id FROM place_country.osm_ids)) + AND rank = 7 + AND osm.osm_id = ne.osm_id + AND ne.rk = 1; + + UPDATE osm_country_point AS osm + SET "rank" = 6 + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_country.osm_ids)) + AND "rank" = 7; + + -- TODO: This shouldn't be necessary? The rank function makes something wrong... + UPDATE osm_country_point AS osm + SET "rank" = 1 + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_country.osm_ids)) + AND "rank" = 0; + + UPDATE osm_country_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_country.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); + +$$ LANGUAGE SQL; + +SELECT update_osm_country_point(true); + +CREATE INDEX IF NOT EXISTS osm_country_point_rank_idx ON osm_country_point ("rank"); -- Handle updates -CREATE SCHEMA IF NOT EXISTS place_country; +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; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; -CREATE TABLE IF NOT EXISTS place_country.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION place_country.flag() RETURNS trigger AS $$ +CREATE TABLE IF NOT EXISTS place_country.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION place_country.flag() RETURNS trigger AS +$$ BEGIN - INSERT INTO place_country.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + INSERT INTO place_country.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; END; -$$ language plpgsql; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION place_country.refresh() RETURNS trigger AS - $BODY$ - BEGIN +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN RAISE LOG 'Refresh place_country rank'; - PERFORM update_osm_country_point(); + PERFORM update_osm_country_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM place_country.osm_ids; + -- noinspection SqlWithoutWhere DELETE FROM place_country.updates; - RETURN null; - END; - $BODY$ -language plpgsql; + + RAISE LOG 'Refresh place_country done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_country_point + FOR EACH ROW +EXECUTE PROCEDURE place_country.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE ON osm_country_point + AFTER INSERT OR UPDATE OR DELETE + ON osm_country_point FOR EACH STATEMENT - EXECUTE PROCEDURE place_country.flag(); +EXECUTE PROCEDURE place_country.flag(); CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON place_country.updates + AFTER INSERT + ON place_country.updates INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE place_country.refresh(); +EXECUTE PROCEDURE place_country.refresh(); diff --git a/layers/place/update_island_point.sql b/layers/place/update_island_point.sql index cedb64687..7f57d3232 100644 --- a/layers/place/update_island_point.sql +++ b/layers/place/update_island_point.sql @@ -1,48 +1,86 @@ DROP TRIGGER IF EXISTS trigger_flag ON osm_island_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_island_point; DROP TRIGGER IF EXISTS trigger_refresh ON place_island_point.updates; --- etldoc: osm_island_point -> osm_island_point -CREATE OR REPLACE FUNCTION update_osm_island_point() RETURNS VOID AS $$ -BEGIN - UPDATE osm_island_point - SET tags = update_tags(tags, geometry) - WHERE COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL; +CREATE SCHEMA IF NOT EXISTS place_island_point; -END; -$$ LANGUAGE plpgsql; +CREATE TABLE IF NOT EXISTS place_island_point.osm_ids +( + osm_id bigint +); -SELECT update_osm_island_point(); +-- etldoc: osm_island_point -> osm_island_point +CREATE OR REPLACE FUNCTION update_osm_island_point(full_update boolean) RETURNS void AS +$$ + UPDATE osm_island_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_island_point.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); +$$ LANGUAGE SQL; + +SELECT update_osm_island_point(true); -- Handle updates -CREATE SCHEMA IF NOT EXISTS place_island_point; +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; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; -CREATE TABLE IF NOT EXISTS place_island_point.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION place_island_point.flag() RETURNS trigger AS $$ +CREATE TABLE IF NOT EXISTS place_island_point.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION place_island_point.flag() RETURNS trigger AS +$$ BEGIN - INSERT INTO place_island_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + INSERT INTO place_island_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; END; -$$ language plpgsql; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION place_island_point.refresh() RETURNS trigger AS - $BODY$ - BEGIN +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN RAISE LOG 'Refresh place_island_point'; - PERFORM update_osm_island_point(); + PERFORM update_osm_island_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM place_island_point.osm_ids; + -- noinspection SqlWithoutWhere DELETE FROM place_island_point.updates; - RETURN null; - END; - $BODY$ -language plpgsql; + + RAISE LOG 'Refresh place_island_point done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_island_point + FOR EACH ROW +EXECUTE PROCEDURE place_island_point.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE ON osm_island_point + AFTER INSERT OR UPDATE OR DELETE + ON osm_island_point FOR EACH STATEMENT - EXECUTE PROCEDURE place_island_point.flag(); +EXECUTE PROCEDURE place_island_point.flag(); CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON place_island_point.updates + AFTER INSERT + ON place_island_point.updates INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE place_island_point.refresh(); +EXECUTE PROCEDURE place_island_point.refresh(); diff --git a/layers/place/update_island_polygon.sql b/layers/place/update_island_polygon.sql index 883e9b36d..2644f3558 100644 --- a/layers/place/update_island_polygon.sql +++ b/layers/place/update_island_polygon.sql @@ -1,51 +1,93 @@ DROP TRIGGER IF EXISTS trigger_flag ON osm_island_polygon; +DROP TRIGGER IF EXISTS trigger_store ON osm_island_polygon; DROP TRIGGER IF EXISTS trigger_refresh ON place_island_polygon.updates; +CREATE SCHEMA IF NOT EXISTS place_island_polygon; + +CREATE TABLE IF NOT EXISTS place_island_polygon.osm_ids +( + osm_id bigint +); + -- etldoc: osm_island_polygon -> osm_island_polygon -CREATE OR REPLACE FUNCTION update_osm_island_polygon() RETURNS VOID AS $$ -BEGIN - UPDATE osm_island_polygon SET geometry=ST_PointOnSurface(ST_MakeValid(geometry)) WHERE ST_GeometryType(geometry) <> 'ST_Point'; +CREATE OR REPLACE FUNCTION update_osm_island_polygon(full_update boolean) RETURNS void AS +$$ + UPDATE osm_island_polygon + SET geometry = ST_PointOnSurface(geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_island_polygon.osm_ids)) + AND ST_GeometryType(geometry) <> 'ST_Point' + AND ST_IsValid(geometry); - UPDATE osm_island_polygon - SET tags = update_tags(tags, geometry) - WHERE COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL; + UPDATE osm_island_polygon + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_island_polygon.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); - ANALYZE osm_island_polygon; -END; -$$ LANGUAGE plpgsql; +$$ LANGUAGE SQL; -SELECT update_osm_island_polygon(); +SELECT update_osm_island_polygon(true); -- Handle updates -CREATE SCHEMA IF NOT EXISTS place_island_polygon; +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; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; -CREATE TABLE IF NOT EXISTS place_island_polygon.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION place_island_polygon.flag() RETURNS trigger AS $$ +CREATE TABLE IF NOT EXISTS place_island_polygon.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION place_island_polygon.flag() RETURNS trigger AS +$$ BEGIN - INSERT INTO place_island_polygon.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + INSERT INTO place_island_polygon.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; END; -$$ language plpgsql; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION place_island_polygon.refresh() RETURNS trigger AS - $BODY$ - BEGIN +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN RAISE LOG 'Refresh place_island_polygon'; - PERFORM update_osm_island_polygon(); + PERFORM update_osm_island_polygon(false); + -- noinspection SqlWithoutWhere + DELETE FROM place_island_polygon.osm_ids; + -- noinspection SqlWithoutWhere DELETE FROM place_island_polygon.updates; - RETURN null; - END; - $BODY$ -language plpgsql; + + RAISE LOG 'Refresh place_island_polygon done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_island_polygon + FOR EACH ROW +EXECUTE PROCEDURE place_island_polygon.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE ON osm_island_polygon + AFTER INSERT OR UPDATE OR DELETE + ON osm_island_polygon FOR EACH STATEMENT - EXECUTE PROCEDURE place_island_polygon.flag(); +EXECUTE PROCEDURE place_island_polygon.flag(); CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON place_island_polygon.updates + AFTER INSERT + ON place_island_polygon.updates INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE place_island_polygon.refresh(); +EXECUTE PROCEDURE place_island_polygon.refresh(); diff --git a/layers/place/update_state_point.sql b/layers/place/update_state_point.sql index 878d6adbf..fa1558022 100644 --- a/layers/place/update_state_point.sql +++ b/layers/place/update_state_point.sql @@ -1,79 +1,127 @@ DROP TRIGGER IF EXISTS trigger_flag ON osm_state_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_state_point; DROP TRIGGER IF EXISTS trigger_refresh ON place_state.updates; -ALTER TABLE osm_state_point DROP CONSTRAINT IF EXISTS osm_state_point_rank_constraint; +CREATE SCHEMA IF NOT EXISTS place_state; + +CREATE TABLE IF NOT EXISTS place_state.osm_ids +( + osm_id bigint +); -- etldoc: ne_10m_admin_1_states_provinces -> osm_state_point -- etldoc: osm_state_point -> osm_state_point -CREATE OR REPLACE FUNCTION update_osm_state_point() RETURNS VOID AS $$ -BEGIN +CREATE OR REPLACE FUNCTION update_osm_state_point(full_update boolean) RETURNS void AS +$$ + WITH important_state_point AS ( + SELECT osm.geometry, + osm.osm_id, + osm.name, + COALESCE(NULLIF(osm.name_en, ''), ne.name) AS name_en, + ne.scalerank, + ne.labelrank, + ne.datarank + FROM ne_10m_admin_1_states_provinces AS ne, + osm_state_point AS osm + WHERE + -- We only match whether the point is within the Natural Earth polygon + -- because name matching is difficult + ST_Within(osm.geometry, ne.geometry) + -- We leave out leess important states + AND ne.scalerank <= 6 + AND ne.labelrank <= 7 + ) + UPDATE osm_state_point AS osm + -- Normalize both scalerank and labelrank into a ranking system from 1 to 6. + SET "rank" = LEAST(6, CEILING((scalerank + labelrank + datarank) / 3.0)) + FROM important_state_point AS ne + WHERE (full_update OR osm.osm_id IN (SELECT osm_id FROM place_state.osm_ids)) + AND rank IS NULL + AND osm.osm_id = ne.osm_id; + + -- TODO: This shouldn't be necessary? The rank function makes something wrong... + UPDATE osm_state_point AS osm + SET "rank" = 1 + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_state.osm_ids)) + AND "rank" = 0; + + DELETE FROM osm_state_point + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_state.osm_ids)) + AND "rank" IS NULL; + + UPDATE osm_state_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM place_state.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); + +$$ LANGUAGE SQL; + +SELECT update_osm_state_point(true); + +CREATE INDEX IF NOT EXISTS osm_state_point_rank_idx ON osm_state_point ("rank"); - WITH important_state_point AS ( - SELECT osm.geometry, osm.osm_id, osm.name, COALESCE(NULLIF(osm.name_en, ''), ne.name) AS name_en, ne.scalerank, ne.labelrank, ne.datarank - FROM ne_10m_admin_1_states_provinces AS ne, osm_state_point AS osm - WHERE - -- We only match whether the point is within the Natural Earth polygon - -- because name matching is difficult - ST_Within(osm.geometry, ne.geometry) - -- We leave out leess important states - AND ne.scalerank <= 3 AND ne.labelrank <= 2 - ) - UPDATE osm_state_point AS osm - -- Normalize both scalerank and labelrank into a ranking system from 1 to 6. - SET "rank" = LEAST(6, CEILING((scalerank + labelrank + datarank)/3.0)) - FROM important_state_point AS ne - WHERE osm.osm_id = ne.osm_id; - - -- TODO: This shouldn't be necessary? The rank function makes something wrong... - UPDATE osm_state_point AS osm - SET "rank" = 1 - WHERE "rank" = 0; - - DELETE FROM osm_state_point WHERE "rank" IS NULL; - - UPDATE osm_state_point - SET tags = update_tags(tags, geometry) - WHERE COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL; +-- Handle updates +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; + RETURN NULL; END; $$ LANGUAGE plpgsql; -SELECT update_osm_state_point(); - --- ALTER TABLE osm_state_point ADD CONSTRAINT osm_state_point_rank_constraint CHECK("rank" BETWEEN 1 AND 6); -CREATE INDEX IF NOT EXISTS osm_state_point_rank_idx ON osm_state_point("rank"); - --- Handle updates - -CREATE SCHEMA IF NOT EXISTS place_state; - -CREATE TABLE IF NOT EXISTS place_state.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION place_state.flag() RETURNS trigger AS $$ +CREATE TABLE IF NOT EXISTS place_state.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION place_state.flag() RETURNS trigger AS +$$ BEGIN - INSERT INTO place_state.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + INSERT INTO place_state.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; END; -$$ language plpgsql; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION place_state.refresh() RETURNS trigger AS - $BODY$ - BEGIN +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN RAISE LOG 'Refresh place_state rank'; - PERFORM update_osm_state_point(); + PERFORM update_osm_state_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM place_state.osm_ids; + -- noinspection SqlWithoutWhere DELETE FROM place_state.updates; - RETURN null; - END; - $BODY$ -language plpgsql; + + RAISE LOG 'Refresh place_state done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_state_point + FOR EACH ROW +EXECUTE PROCEDURE place_state.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE ON osm_state_point + AFTER INSERT OR UPDATE OR DELETE + ON osm_state_point FOR EACH STATEMENT - EXECUTE PROCEDURE place_state.flag(); +EXECUTE PROCEDURE place_state.flag(); CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON place_state.updates + AFTER INSERT + ON place_state.updates INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE place_state.refresh(); +EXECUTE PROCEDURE place_state.refresh(); diff --git a/layers/poi/class.sql b/layers/poi/class.sql index c668e0034..7834e85d2 100644 --- a/layers/poi/class.sql +++ b/layers/poi/class.sql @@ -1,67 +1,41 @@ -CREATE OR REPLACE FUNCTION poi_class_rank(class TEXT) -RETURNS INT AS $$ - SELECT CASE class - WHEN 'hospital' THEN 20 - WHEN 'railway' THEN 40 - WHEN 'bus' THEN 50 - WHEN 'attraction' THEN 70 - WHEN 'harbor' THEN 75 - WHEN 'college' THEN 80 - WHEN 'school' THEN 85 - WHEN 'stadium' THEN 90 - WHEN 'zoo' THEN 95 - WHEN 'town_hall' THEN 100 - WHEN 'campsite' THEN 110 - WHEN 'cemetery' THEN 115 - WHEN 'park' THEN 120 - WHEN 'library' THEN 130 - WHEN 'police' THEN 135 - WHEN 'post' THEN 140 - WHEN 'golf' THEN 150 - WHEN 'shop' THEN 400 - WHEN 'grocery' THEN 500 - WHEN 'fast_food' THEN 600 - WHEN 'clothing_store' THEN 700 - WHEN 'bar' THEN 800 - ELSE 1000 - END; -$$ LANGUAGE SQL IMMUTABLE; +CREATE OR REPLACE FUNCTION poi_class_rank(class text) + RETURNS int AS +$$ +SELECT CASE class + WHEN 'hospital' THEN 20 + WHEN 'railway' THEN 40 + WHEN 'bus' THEN 50 + WHEN 'attraction' THEN 70 + WHEN 'harbor' THEN 75 + WHEN 'college' THEN 80 + WHEN 'school' THEN 85 + WHEN 'stadium' THEN 90 + WHEN 'zoo' THEN 95 + WHEN 'town_hall' THEN 100 + WHEN 'campsite' THEN 110 + WHEN 'cemetery' THEN 115 + WHEN 'park' THEN 120 + WHEN 'library' THEN 130 + WHEN 'police' THEN 135 + WHEN 'post' THEN 140 + WHEN 'golf' THEN 150 + WHEN 'shop' THEN 400 + WHEN 'grocery' THEN 500 + WHEN 'fast_food' THEN 600 + WHEN 'clothing_store' THEN 700 + WHEN 'bar' THEN 800 + ELSE 1000 + END; +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; -CREATE OR REPLACE FUNCTION poi_class(subclass TEXT, mapping_key TEXT) -RETURNS TEXT AS $$ - SELECT CASE - WHEN subclass IN ('townhall','public_building','courthouse','community_centre') THEN 'town_hall' - WHEN subclass IN ('golf','golf_course','miniature_golf') THEN 'golf' - WHEN subclass IN ('fast_food','food_court') THEN 'fast_food' - WHEN subclass IN ('park','bbq') THEN 'park' - WHEN subclass IN ('bus_stop','bus_station') THEN 'bus' - WHEN (subclass='station' AND mapping_key = 'railway') OR subclass IN ('halt', 'tram_stop', 'subway') THEN 'railway' - WHEN (subclass='station' AND mapping_key = 'aerialway') THEN 'aerialway' - WHEN subclass IN ('subway_entrance','train_station_entrance') THEN 'entrance' - WHEN subclass IN ('camp_site','caravan_site') THEN 'campsite' - WHEN subclass IN ('laundry','dry_cleaning') THEN 'laundry' - WHEN subclass IN ('supermarket','deli','delicatessen','department_store','greengrocer','marketplace') THEN 'grocery' - WHEN subclass IN ('books','library') THEN 'library' - WHEN subclass IN ('university','college') THEN 'college' - WHEN subclass IN ('hotel','motel','bed_and_breakfast','guest_house','hostel','chalet','alpine_hut','dormitory', 'apartment') THEN 'lodging' - WHEN subclass IN ('post_box','post_office') THEN 'post' - WHEN subclass IN ('cafe') THEN 'cafe' - WHEN subclass IN ('school','kindergarten') THEN 'school' - WHEN subclass IN ('alcohol','beverages','wine') THEN 'alcohol_shop' - WHEN subclass IN ('bar','nightclub') THEN 'bar' - WHEN subclass IN ('marina','dock') THEN 'harbor' - WHEN subclass IN ('car','car_repair','taxi') THEN 'car' - WHEN subclass IN ('hospital','nursing_home', 'clinic') THEN 'hospital' - WHEN subclass IN ('grave_yard','cemetery') THEN 'cemetery' - WHEN subclass IN ('attraction','viewpoint') THEN 'attraction' - WHEN subclass IN ('biergarten','pub') THEN 'beer' - WHEN subclass IN ('music','musical_instrument') THEN 'music' - WHEN subclass IN ('american_football','stadium','soccer') THEN 'stadium' - WHEN subclass IN ('art','artwork','gallery','arts_centre') THEN 'art_gallery' - WHEN subclass IN ('bag','clothes') THEN 'clothing_store' - WHEN subclass IN ('swimming_area','swimming') THEN 'swimming' - WHEN subclass IN ('castle','ruins') THEN 'castle' - WHEN mapping_key IN ('barrier', 'craft', 'shop') THEN mapping_key - ELSE subclass - END; -$$ LANGUAGE SQL IMMUTABLE; +CREATE OR REPLACE FUNCTION poi_class(subclass text, mapping_key text) + RETURNS text AS +$$ +SELECT CASE + %%FIELD_MAPPING: class %% + WHEN mapping_key IN ('barrier', 'craft', 'shop') THEN mapping_key + ELSE subclass + END; +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; diff --git a/layers/poi/etl_diagram.png b/layers/poi/etl_diagram.png index 94638760b..db51621de 100644 Binary files a/layers/poi/etl_diagram.png and b/layers/poi/etl_diagram.png differ diff --git a/layers/poi/layer.sql b/layers/poi/layer.sql deleted file mode 100644 index 176f9281a..000000000 --- a/layers/poi/layer.sql +++ /dev/null @@ -1,166 +0,0 @@ --- Filter the imported POIs, mostly by blacklisting some subclasses. -CREATE OR REPLACE FUNCTION poi_filter( - name varchar, - subclass varchar, - mapping_key varchar -) -RETURNS BOOLEAN AS $$ -SELECT - CASE - WHEN mapping_key = 'amenity' THEN - LOWER(subclass) NOT IN ( - 'yes', 'no', 'none', 'bench', 'clock', 'drinking_water', - 'fountain', 'parking_entrance', 'parking_space', 'photo_booth', - 'reception_desk', 'ticket_validator', 'vending_machine', - 'waste_disposal', 'water_point' - ) - WHEN mapping_key = 'healthcare' THEN - name <> '' - WHEN mapping_key = 'landuse' THEN - name <> '' AND LOWER(subclass) NOT IN ('yes', 'no', 'none') - WHEN mapping_key = 'leisure' THEN - LOWER(subclass) NOT IN ( - 'yes', 'no', 'none', 'common', 'nature_reserve', - 'picnic_table', 'slipway', 'swimming_pool', 'track' - ) - WHEN mapping_key = 'office' THEN - name <> '' AND LOWER(subclass) NOT IN ('no', 'none') - WHEN mapping_key = 'shop' THEN - LOWER(subclass) NOT IN ('yes', 'no', 'none', 'vacant') - ELSE - LOWER(subclass) NOT IN ('yes', 'no', 'none') - END; -$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; - --- Compute the weight of an OSM POI, primarily this function relies on the --- count of page views for the Wikipedia pages of this POI. -CREATE OR REPLACE FUNCTION poi_display_weight( - name varchar, - subclass varchar, - mapping_key varchar, - tags hstore -) -RETURNS REAL AS $$ - BEGIN - RETURN CASE - WHEN name <> '' THEN - 1 - poi_class_rank(poi_class(subclass, mapping_key))::real / 1000 - ELSE - 0.0 - END; - END -$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; - -CREATE OR REPLACE FUNCTION osm_hash_from_imposm(imposm_id bigint) -RETURNS bigint AS $$ - SELECT CASE - WHEN imposm_id < -1e17 THEN (-imposm_id-1e17) * 10 + 4 -- Relation - WHEN imposm_id < 0 THEN (-imposm_id) * 10 + 1 -- Way - ELSE imposm_id * 10 -- Node - END::bigint; -$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; - -CREATE OR REPLACE FUNCTION global_id_from_imposm(imposm_id bigint) -RETURNS TEXT AS $$ - SELECT CONCAT( - 'osm:', - CASE WHEN imposm_id < -1e17 THEN CONCAT('relation:', -imposm_id-1e17) - WHEN imposm_id < 0 THEN CONCAT('way:', -imposm_id) - ELSE CONCAT('node:', imposm_id) - END - ); -$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; - - -CREATE OR REPLACE FUNCTION all_pois(zoom_level integer) -RETURNS TABLE(osm_id bigint, global_id text, geometry geometry, name text, name_en text, - name_de text, tags hstore, class text, subclass text, agg_stop integer, layer integer, - level integer, indoor integer, mapping_key text) -AS $$ - SELECT osm_id_hash AS osm_id, global_id, - geometry, NULLIF(name, '') AS name, - COALESCE(NULLIF(name_en, ''), name) AS name_en, - COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, - tags, - poi_class(subclass, mapping_key) AS class, - CASE - WHEN subclass = 'information' - THEN NULLIF(information, '') - WHEN subclass = 'place_of_worship' - THEN NULLIF(religion, '') - WHEN subclass IN ('pitch', 'sports_centre') - THEN NULLIF(sport, '') - WHEN subclass = 'recycling' - THEN COALESCE(tags->'recycling_type', 'recycling') - ELSE subclass - END AS subclass, - agg_stop, - NULLIF(layer, 0) AS layer, - "level", - CASE WHEN indoor=TRUE THEN 1 ELSE NULL END as indoor, - mapping_key - FROM ( - -- etldoc: osm_poi_point -> layer_poi:z12 - -- etldoc: osm_poi_point -> layer_poi:z13 - SELECT *, - osm_hash_from_imposm(osm_id) AS osm_id_hash, - global_id_from_imposm(osm_id) as global_id - FROM osm_poi_point - WHERE zoom_level BETWEEN 12 AND 13 - AND ((subclass='station' AND mapping_key = 'railway') - OR subclass IN ('halt', 'ferry_terminal')) - UNION ALL - - -- etldoc: osm_poi_point -> layer_poi:z14_ - SELECT *, - osm_hash_from_imposm(osm_id) AS osm_id_hash, - global_id_from_imposm(osm_id) as global_id - FROM osm_poi_point - WHERE zoom_level >= 14 - AND (name <> '' OR (subclass <> 'garden' AND subclass <> 'park')) - - UNION ALL - -- etldoc: osm_poi_polygon -> layer_poi:z12 - -- etldoc: osm_poi_polygon -> layer_poi:z13 - SELECT *, - NULL::INTEGER AS agg_stop, - osm_hash_from_imposm(osm_id) AS osm_id_hash, - global_id_from_imposm(osm_id) as global_id - FROM osm_poi_polygon - WHERE 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, - osm_hash_from_imposm(osm_id) AS osm_id_hash, - global_id_from_imposm(osm_id) as global_id - FROM osm_poi_polygon - WHERE zoom_level >= 14 - AND (name <> '' OR (subclass <> 'garden' AND subclass <> 'park')) - ) as poi_union - WHERE poi_filter(name, subclass, mapping_key); -$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; - --- etldoc: layer_poi[shape=record fillcolor=lightpink, style="rounded,filled", --- etldoc: label="layer_poi | z12 | z13 | z14+" ] ; -CREATE OR REPLACE FUNCTION layer_poi(bbox geometry, zoom_level integer, pixel_width numeric) -RETURNS TABLE(osm_id bigint, global_id text, geometry geometry, name text, name_en text, - name_de text, tags hstore, class text, subclass text, agg_stop integer, layer integer, - level integer, indoor integer, mapping_key text, "rank" int) -AS $$ - SELECT *, - row_number() OVER ( - PARTITION BY LabelGrid(geometry, 100 * pixel_width) - ORDER BY poi_display_weight(name, subclass, mapping_key, tags) DESC - )::int AS "rank" - FROM ( - -- etldoc: osm_poi_point -> layer_poi:z12 - -- etldoc: osm_poi_point -> layer_poi:z13 - SELECT * - FROM all_pois(zoom_level) - WHERE geometry && bbox - ) as all_pois; -$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; diff --git a/layers/poi/mapping.yaml b/layers/poi/mapping.yaml index 50b2d2843..af6dc700a 100644 --- a/layers/poi/mapping.yaml +++ b/layers/poi/mapping.yaml @@ -6,6 +6,64 @@ def_poi_mapping_aerialway: &poi_mapping_aerialway - station +# amenity values , see http://taginfo.openstreetmap.org/keys/amenity#values +def_poi_mapping_amenity: &poi_mapping_amenity + - arts_centre + - atm + - bank + - bar + - bbq + - bicycle_parking + - bicycle_rental + - biergarten + - bus_station + - cafe + - cinema + - clinic + - college + - community_centre + - courthouse + - dentist + - doctors + - embassy + - fast_food + - ferry_terminal + - fire_station + - food_court + - fuel + - grave_yard + - hospital + - ice_cream + - kindergarten + - library + - marketplace + - motorcycle_parking + - nightclub + - nursing_home + - parking + - pharmacy + - place_of_worship + - police + - parcel_locker + - post_box + - post_office + - prison + - pub + - public_building + - recycling + - restaurant + - school + - shelter + - swimming_pool + - taxi + - telephone + - theatre + - toilets + - townhall + - university + - veterinary + - waste_basket + # barrier values , see http://taginfo.openstreetmap.org/keys/barrier#values def_poi_mapping_barrier: &poi_mapping_barrier - bollard @@ -45,6 +103,29 @@ def_poi_mapping_landuse: &poi_mapping_landuse - retail - winter_sports +# leisure values , see http://taginfo.openstreetmap.org/keys/leisure#values +def_poi_mapping_leisure: &poi_mapping_leisure + - dog_park + - escape_game + - garden + - golf_course + - ice_rink + - hackerspace + - marina + - miniature_golf + - park + - pitch + - playground + - sports_centre + - stadium + - swimming_area + - swimming_pool + - water_park + +# office values , see http://taginfo.openstreetmap.org/keys/office#values +def_poi_mapping_office: &poi_mapping_office + - diplomatic + # railway values , see http://taginfo.openstreetmap.org/keys/railway#values def_poi_mapping_railway: &poi_mapping_railway - halt @@ -53,6 +134,194 @@ def_poi_mapping_railway: &poi_mapping_railway - train_station_entrance - tram_stop +# shop values , see http://taginfo.openstreetmap.org/keys/shop#values +def_poi_mapping_shop: &poi_mapping_shop + - accessories + - alcohol + - antiques + - art + - bag + - bakery + - beauty + - bed + - beverages + - bicycle + - books + - boutique + - butcher + - camera + - car + - car_repair + - carpet + - charity + - chemist + - chocolate + - clothes + - coffee + - computer + - confectionery + - convenience + - copyshop + - cosmetics + - deli + - delicatessen + - department_store + - doityourself + - dry_cleaning + - electronics + - erotic + - fabric + - florist + - frozen_food + - furniture + - garden_centre + - general + - gift + - greengrocer + - hairdresser + - hardware + - hearing_aids + - hifi + - ice_cream + - interior_decoration + - jewelry + - kiosk + - lamps + - laundry + - mall + - massage + - mobile_phone + - motorcycle + - music + - musical_instrument + - newsagent + - optician + - outdoor + - perfume + - perfumery + - pet + - photo + - second_hand + - shoes + - sports + - stationery + - supermarket + - tailor + - tattoo + - ticket + - tobacco + - toys + - travel_agency + - video + - video_games + - watches + - weapons + - wholesale + - wine + +# sport values , see http://taginfo.openstreetmap.org/keys/sport#values +def_poi_mapping_sport: &poi_mapping_sport + - american_football + - archery + - athletics + - australian_football + - badminton + - baseball + - basketball + - beachvolleyball + - billiards + - bmx + - boules + - bowls + - boxing + - canadian_football + - canoe + - chess + - climbing + - climbing_adventure + - cricket + - cricket_nets + - croquet + - curling + - cycling + - disc_golf + - diving + - dog_racing + - equestrian + - fatsal + - field_hockey + - free_flying + - gaelic_games + - golf + - gymnastics + - handball + - hockey + - horse_racing + - horseshoes + - ice_hockey + - ice_stock + - judo + - karting + - korfball + - long_jump + - model_aerodrome + - motocross + - motor + - multi + - netball + - orienteering + - paddle_tennis + - paintball + - paragliding + - pelota + - racquet + - rc_car + - rowing + - rugby + - rugby_league + - rugby_union + - running + - sailing + - scuba_diving + - shooting + - shooting_range + - skateboard + - skating + - skiing + - soccer + - surfing + - swimming + - table_soccer + - table_tennis + - team_handball + - tennis + - toboggan + - volleyball + - water_ski + - yoga + +# tourism values , see http://taginfo.openstreetmap.org/keys/tourism#values +def_poi_mapping_tourism: &poi_mapping_tourism + - alpine_hut + - aquarium + - artwork + - attraction + - bed_and_breakfast + - camp_site + - caravan_site + - chalet + - gallery + - guest_house + - hostel + - hotel + - information + - motel + - museum + - picnic_site + - theme_park + - viewpoint + - zoo + # waterway values , see http://taginfo.openstreetmap.org/keys/waterway#values def_poi_mapping_waterway: &poi_mapping_waterway - dock @@ -89,6 +358,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 @@ -104,6 +376,15 @@ def_poi_fields: &poi_fields - name: sport key: sport 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 @@ -115,8 +396,8 @@ def_poi_mapping: &poi_mapping highway: *poi_mapping_highway historic: *poi_mapping_historic landuse: *poi_mapping_landuse - leisure: [__any__] - office: [__any__] + leisure: *poi_mapping_leisure + office: *poi_mapping_office railway: *poi_mapping_railway shop: [__any__] sport: [__any__] @@ -127,14 +408,11 @@ tables: # etldoc: imposm3 -> osm_poi_point poi_point: type: point - fields: *poi_fields + columns: *poi_fields mapping: *poi_mapping # etldoc: imposm3 -> osm_poi_polygon poi_polygon: type: polygon - fields: *poi_fields + columns: *poi_fields mapping: *poi_mapping - -tags: - load_all: true diff --git a/layers/poi/mapping_diagram.png b/layers/poi/mapping_diagram.png index b16fae2bd..089aded3b 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 new file mode 100644 index 000000000..843668af4 --- /dev/null +++ b/layers/poi/poi.sql @@ -0,0 +1,169 @@ +-- Filter the imported POIs, mostly by blacklisting some subclasses. +CREATE OR REPLACE FUNCTION poi_filter( + name varchar, + subclass varchar, + mapping_key varchar +) +RETURNS BOOLEAN AS $$ +SELECT + CASE + WHEN mapping_key = 'amenity' THEN + LOWER(subclass) NOT IN ( + 'yes', 'no', 'none', 'bench', 'clock', 'drinking_water', + 'fountain', 'parking_entrance', 'parking_space', 'photo_booth', + 'reception_desk', 'ticket_validator', 'vending_machine', + 'waste_disposal', 'water_point' + ) + WHEN mapping_key = 'healthcare' THEN + name <> '' + WHEN mapping_key = 'landuse' THEN + name <> '' AND LOWER(subclass) NOT IN ('yes', 'no', 'none') + WHEN mapping_key = 'leisure' THEN + LOWER(subclass) NOT IN ( + 'yes', 'no', 'none', 'common', 'nature_reserve', + 'picnic_table', 'slipway', 'swimming_pool', 'track' + ) + WHEN mapping_key = 'office' THEN + name <> '' AND LOWER(subclass) NOT IN ('no', 'none') + WHEN mapping_key = 'shop' THEN + LOWER(subclass) NOT IN ('yes', 'no', 'none', 'vacant') + ELSE + LOWER(subclass) NOT IN ('yes', 'no', 'none') + END; +$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; + +-- Compute the weight of an OSM POI, primarily this function relies on the +-- count of page views for the Wikipedia pages of this POI. +CREATE OR REPLACE FUNCTION poi_display_weight( + name varchar, + subclass varchar, + mapping_key varchar, + tags hstore +) +RETURNS REAL AS $$ +BEGIN +RETURN CASE + WHEN name <> '' THEN + 1 - poi_class_rank(poi_class(subclass, mapping_key))::real / 1000 + ELSE + 0.0 +END; +END +$$ LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION osm_hash_from_imposm(imposm_id bigint) +RETURNS bigint AS $$ +SELECT CASE + WHEN imposm_id < -1e17 THEN (-imposm_id-1e17) * 10 + 4 -- Relation + WHEN imposm_id < 0 THEN (-imposm_id) * 10 + 1 -- Way + ELSE imposm_id * 10 -- Node + END::bigint; +$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION global_id_from_imposm(imposm_id bigint) +RETURNS TEXT AS $$ +SELECT CONCAT( + 'osm:', + CASE WHEN imposm_id < -1e17 THEN CONCAT('relation:', -imposm_id-1e17) + WHEN imposm_id < 0 THEN CONCAT('way:', -imposm_id) + ELSE CONCAT('node:', imposm_id) + END + ); +$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; + + +CREATE OR REPLACE FUNCTION all_pois(zoom_level integer) +RETURNS TABLE(osm_id bigint, global_id text, geometry geometry, name text, name_en text, + name_de text, tags hstore, class text, subclass text, agg_stop integer, layer integer, + level integer, indoor integer, mapping_key text) +AS $$ +SELECT osm_id_hash AS osm_id, global_id, + geometry, NULLIF(name, '') AS name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + poi_class(subclass, mapping_key) AS class, + CASE + WHEN subclass = 'information' + THEN NULLIF(information, '') + WHEN subclass = 'place_of_worship' + THEN NULLIF(religion, '') + WHEN subclass IN ('pitch', 'sports_centre') + THEN NULLIF(sport, '') + WHEN subclass = 'recycling' + THEN COALESCE(tags->'recycling_type', 'recycling') + ELSE subclass + END AS subclass, + agg_stop, + NULLIF(layer, 0) AS layer, + "level", + CASE WHEN indoor=TRUE THEN 1 ELSE NULL END as indoor, + mapping_key +FROM ( + -- etldoc: osm_poi_point -> layer_poi:z12 + -- etldoc: osm_poi_point -> layer_poi:z13 + SELECT *, + osm_hash_from_imposm(osm_id) AS osm_id_hash, + global_id_from_imposm(osm_id) as global_id + FROM osm_poi_point + WHERE zoom_level BETWEEN 12 AND 13 + AND ((subclass='station' AND mapping_key = 'railway') + OR subclass IN ('halt', 'ferry_terminal')) + UNION ALL + + -- etldoc: osm_poi_point -> layer_poi:z14_ + SELECT *, + osm_hash_from_imposm(osm_id) AS osm_id_hash, + global_id_from_imposm(osm_id) as global_id + FROM osm_poi_point + WHERE zoom_level >= 14 + AND (name <> '' OR (subclass <> 'garden' AND subclass <> 'park')) + + UNION ALL + -- etldoc: osm_poi_polygon -> layer_poi:z12 + -- etldoc: osm_poi_polygon -> layer_poi:z13 + SELECT *, + NULL::INTEGER AS agg_stop, + osm_hash_from_imposm(osm_id) AS osm_id_hash, + global_id_from_imposm(osm_id) as global_id + FROM osm_poi_polygon + WHERE 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, + osm_hash_from_imposm(osm_id) AS osm_id_hash, + global_id_from_imposm(osm_id) as global_id + FROM osm_poi_polygon + WHERE zoom_level >= 14 + AND (name <> '' OR (subclass <> 'garden' AND subclass <> 'park')) + ) as poi_union +WHERE poi_filter(name, subclass, mapping_key); +$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; + +-- etldoc: layer_poi[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_poi | z12 | z13 | z14+" ] ; +CREATE OR REPLACE FUNCTION layer_poi(bbox geometry, zoom_level integer, pixel_width numeric) +RETURNS TABLE(osm_id bigint, global_id text, geometry geometry, name text, name_en text, + name_de text, tags hstore, class text, subclass text, agg_stop integer, layer integer, + level integer, indoor integer, mapping_key text, "rank" int) +AS $$ +SELECT *, + row_number() OVER ( + PARTITION BY LabelGrid(geometry, 100 * pixel_width) + ORDER BY poi_display_weight(name, subclass, mapping_key, tags) DESC + )::int AS "rank" +FROM ( + -- etldoc: osm_poi_point -> layer_poi:z12 + -- etldoc: osm_poi_point -> layer_poi:z13 + SELECT * + FROM all_pois(zoom_level) + WHERE geometry && bbox + ) as all_pois; +$$ LANGUAGE SQL STABLE + PARALLEL SAFE; + +-- TODO: Check if the above can be made STRICT -- i.e. if pixel_width could be NULL diff --git a/layers/poi/poi.yaml b/layers/poi/poi.yaml index 2e770ced6..f148f93d6 100644 --- a/layers/poi/poi.yaml +++ b/layers/poi/poi.yaml @@ -10,11 +10,93 @@ layer: name: The OSM [`name`](http://wiki.openstreetmap.org/wiki/Key:name) value of the POI. name_en: English name `name:en` if available, otherwise `name`. name_de: German name `name:de` if available, otherwise `name` or `name:en`. - class: | + class: + description: | More general classes of POIs. If there is no more general `class` for the `subclass` this field will contain the same value as `subclass`. But for example for schools you only need to style the class `school` to filter the subclasses `school` and `kindergarten`. Or use the class `shop` to style all shops. + values: + shop: + subclass: ['accessories', 'antiques', 'beauty', 'bed', 'boutique', 'camera', 'carpet', 'charity', 'chemist', + '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'] + town_hall: + subclass: ['townhall', 'public_building', 'courthouse', 'community_centre'] + golf: + subclass: ['golf', 'golf_course', 'miniature_golf'] + fast_food: + subclass: ['fast_food', 'food_court'] + park: + subclass: ['park', 'bbq'] + bus: + subclass: ['bus_stop', 'bus_station'] + railway: + - __AND__: + subclass: 'station' + mapping_key: 'railway' + - subclass: ['halt', 'tram_stop', 'subway'] + aerialway: + __AND__: + subclass: 'station' + mapping_key: 'aerialway' + entrance: + subclass: ['subway_entrance', 'train_station_entrance'] + campsite: + subclass: ['camp_site', 'caravan_site'] + laundry: + subclass: ['laundry', 'dry_cleaning'] + grocery: + subclass: ['supermarket', 'deli', 'delicatessen', 'department_store', 'greengrocer', 'marketplace'] + library: + subclass: ['books', 'library'] + college: + subclass: ['university', 'college'] + lodging: + subclass: ['hotel', 'motel', 'bed_and_breakfast', 'guest_house', 'hostel', 'chalet', 'alpine_hut', 'dormitory'] + ice_cream: + subclass: ['chocolate', 'confectionery'] + post: + subclass: ['post_box', 'post_office', 'parcel_locker'] + cafe: + subclass: ['cafe'] + school: + subclass: ['school', 'kindergarten'] + alcohol_shop: + subclass: ['alcohol', 'beverages', 'wine'] + bar: + subclass: ['bar', 'nightclub'] + harbor: + subclass: ['marina', 'dock'] + car: + subclass: ['car', 'car_repair', 'car_parts', 'taxi'] + hospital: + subclass: ['hospital', 'nursing_home', 'clinic'] + cemetery: + subclass: ['grave_yard', 'cemetery'] + attraction: + subclass: ['attraction', 'viewpoint'] + beer: + subclass: ['biergarten', 'pub'] + music: + subclass: ['music', 'musical_instrument'] + stadium: + subclass: ['american_football', 'stadium', 'soccer'] + art_gallery: + subclass: ['art', 'artwork', 'gallery', 'arts_centre'] + clothing_store: + subclass: ['bag', 'clothes'] + swimming: + subclass: ['swimming_area', 'swimming'] + castle: + subclass: ['castle', 'ruins'] + atm: + subclass: ['atm'] subclass: description: | Original value of either the @@ -32,7 +114,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: | @@ -67,11 +150,11 @@ layer: query: (SELECT osm_id, geometry, global_id, name, name_en, name_de, {name_languages}, class, subclass, agg_stop, layer, level, indoor, rank FROM layer_poi(!bbox!, z(!scale_denominator!), zres(z(!scale_denominator!))::numeric) ORDER BY rank) AS t schema: - ./public_transport_stop_type.sql - - ./update_poi_polygon.sql - - ./update_poi_point.sql - ./class.sql - ./poi_stop_agg.sql - - ./layer.sql + - ./update_poi_polygon.sql + - ./update_poi_point.sql + - ./poi.sql datasources: - type: imposm3 mapping_file: ./mapping.yaml diff --git a/layers/poi/poi_stop_agg.sql b/layers/poi/poi_stop_agg.sql index bffb65d09..39fb170e1 100644 --- a/layers/poi/poi_stop_agg.sql +++ b/layers/poi/poi_stop_agg.sql @@ -1,39 +1,34 @@ +-- etldoc: osm_poi_point -> osm_poi_stop_centroid DROP MATERIALIZED VIEW IF EXISTS osm_poi_stop_centroid CASCADE; -CREATE MATERIALIZED VIEW osm_poi_stop_centroid AS ( - SELECT - uic_ref, - count(*) as count, - CASE WHEN count(*) > 2 THEN ST_Centroid(ST_UNION(geometry)) - ELSE NULL END AS centroid - FROM osm_poi_point - WHERE - nullif(uic_ref, '') IS NOT NULL - AND subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') - GROUP BY - uic_ref - HAVING - count(*) > 1 -); +CREATE MATERIALIZED VIEW osm_poi_stop_centroid AS +( +SELECT uic_ref, + count(*) AS count, + CASE WHEN count(*) > 2 THEN ST_Centroid(ST_UNION(geometry)) END AS centroid +FROM osm_poi_point +WHERE uic_ref <> '' + AND subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') +GROUP BY uic_ref +HAVING count(*) > 1 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */; +-- etldoc: osm_poi_stop_centroid -> osm_poi_stop_rank +-- etldoc: osm_poi_point -> osm_poi_stop_rank DROP MATERIALIZED VIEW IF EXISTS osm_poi_stop_rank CASCADE; -CREATE MATERIALIZED VIEW osm_poi_stop_rank AS ( - SELECT - p.osm_id, +CREATE MATERIALIZED VIEW osm_poi_stop_rank AS +( +SELECT p.osm_id, -- p.uic_ref, -- p.subclass, - ROW_NUMBER() - OVER ( - PARTITION BY p.uic_ref - ORDER BY - p.subclass :: public_transport_stop_type NULLS LAST, - ST_Distance(c.centroid, p.geometry) - ) AS rk - FROM osm_poi_point p - INNER JOIN osm_poi_stop_centroid c ON (p.uic_ref = c.uic_ref) - WHERE - subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') - ORDER BY p.uic_ref, rk -); - -ALTER TABLE osm_poi_point ADD COLUMN IF NOT EXISTS agg_stop INTEGER DEFAULT NULL; -SELECT update_osm_poi_point_agg(); \ No newline at end of file + ROW_NUMBER() + OVER ( + PARTITION BY p.uic_ref + ORDER BY + p.subclass :: public_transport_stop_type NULLS LAST, + ST_Distance(c.centroid, p.geometry) + ) AS rk +FROM osm_poi_point p + INNER JOIN osm_poi_stop_centroid c ON (p.uic_ref = c.uic_ref) +WHERE subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') +ORDER BY p.uic_ref, rk + ) /* DELAY_MATERIALIZED_VIEW_CREATION */; diff --git a/layers/poi/public_transport_stop_type.sql b/layers/poi/public_transport_stop_type.sql index 433aa179a..bb3239dea 100644 --- a/layers/poi/public_transport_stop_type.sql +++ b/layers/poi/public_transport_stop_type.sql @@ -1,11 +1,11 @@ -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 - FROM pg_type - WHERE typname = 'public_transport_stop_type') THEN - CREATE TYPE public_transport_stop_type AS ENUM ( - 'subway', 'tram_stop', 'bus_station', 'bus_stop' - ); - END IF; -END +DO +$$ + BEGIN + PERFORM 'public_transport_stop_type'::regtype; + EXCEPTION + WHEN undefined_object THEN + CREATE TYPE public_transport_stop_type AS enum ( + 'subway', 'tram_stop', 'bus_station', 'bus_stop' + ); + END $$; diff --git a/layers/poi/style.json b/layers/poi/style.json new file mode 100644 index 000000000..bc167cbc6 --- /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": 146 + }, + { + "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": 147 + }, + { + "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": 148 + }, + { + "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": 149 + }, + { + "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": 150 + }, + { + "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": 151 + }, + { + "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": 152 + }, + { + "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": 153 + }, + { + "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": 154 + }, + { + "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": 155 + }, + { + "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": 156 + }, + { + "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": 157 + }, + { + "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": 158 + }, + { + "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": 159 + }, + { + "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": 160 + }, + { + "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": 161 + }, + { + "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": 162 + }, + { + "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": 163 + }, + { + "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": 164 + }, + { + "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": 165 + }, + { + "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": 166 + }, + { + "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": 167 + }, + { + "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": 168 + }, + { + "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": 169 + }, + { + "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": 170 + }, + { + "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": 171 + }, + { + "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": 185 + }, + { + "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": 187 + } + ] +} \ No newline at end of file diff --git a/layers/poi/update_poi_point.sql b/layers/poi/update_poi_point.sql index c7e1543dd..d48e44307 100644 --- a/layers/poi/update_poi_point.sql +++ b/layers/poi/update_poi_point.sql @@ -2,82 +2,138 @@ DROP TRIGGER IF EXISTS trigger_flag ON osm_poi_point; DROP TRIGGER IF EXISTS trigger_refresh ON poi_point.updates; -- 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() RETURNS void AS +$$ BEGIN - UPDATE osm_poi_point + UPDATE osm_poi_point SET subclass = 'subway' - WHERE station = 'subway' and subclass='station'; + WHERE station = 'subway' + AND subclass = 'station'; - UPDATE osm_poi_point + UPDATE osm_poi_point SET subclass = 'halt' - WHERE funicular = 'yes' and subclass='station'; + WHERE funicular = 'yes' + AND subclass = 'station'; - UPDATE osm_poi_point - SET tags = update_tags(tags, geometry) - WHERE COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL; + -- 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 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 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 + AND tags != update_tags(tags, geometry); END; $$ LANGUAGE plpgsql; SELECT update_osm_poi_point(); -CREATE OR REPLACE FUNCTION update_osm_poi_point_agg() RETURNS VOID AS $$ +-- 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 - ELSE NULL - END; - - UPDATE osm_poi_point p - SET agg_stop = ( - CASE - WHEN p.subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') - AND r.rk IS NULL OR r.rk = 1 - THEN 1 - ELSE NULL - END) + UPDATE osm_poi_point p + SET + agg_stop = CASE + WHEN p.subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') + THEN 1 + END + WHERE + agg_stop != CASE + WHEN p.subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') + THEN 1 + END; + + UPDATE osm_poi_point p + SET + agg_stop = ( + CASE + WHEN p.subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') + AND r.rk IS NULL OR r.rk = 1 + THEN 1 + END) FROM osm_poi_stop_rank r - WHERE p.osm_id = r.osm_id - ; + WHERE p.osm_id = r.osm_id AND + agg_stop != ( + CASE + WHEN p.subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway') + AND r.rk IS NULL OR r.rk = 1 + THEN 1 + END); END; $$ LANGUAGE plpgsql; +ALTER TABLE osm_poi_point + ADD COLUMN IF NOT EXISTS agg_stop integer DEFAULT NULL; +SELECT update_osm_poi_point_agg(); + -- Handle updates CREATE SCHEMA IF NOT EXISTS poi_point; -CREATE TABLE IF NOT EXISTS poi_point.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION poi_point.flag() RETURNS trigger AS $$ +CREATE TABLE IF NOT EXISTS poi_point.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION poi_point.flag() RETURNS trigger AS +$$ BEGIN - INSERT INTO poi_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + INSERT INTO poi_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; END; -$$ language plpgsql; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION poi_point.refresh() RETURNS trigger AS - $BODY$ - BEGIN +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN RAISE LOG 'Refresh poi_point'; PERFORM update_osm_poi_point(); 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.updates; - RETURN null; - END; - $BODY$ -language plpgsql; + + RAISE LOG 'Refresh poi_point done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE ON osm_poi_point + AFTER INSERT OR UPDATE OR DELETE + ON osm_poi_point FOR EACH STATEMENT - EXECUTE PROCEDURE poi_point.flag(); +EXECUTE PROCEDURE poi_point.flag(); CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON poi_point.updates + AFTER INSERT + ON poi_point.updates INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE poi_point.refresh(); +EXECUTE PROCEDURE poi_point.refresh(); diff --git a/layers/poi/update_poi_polygon.sql b/layers/poi/update_poi_polygon.sql index 84de5f418..cf6c2238d 100644 --- a/layers/poi/update_poi_polygon.sql +++ b/layers/poi/update_poi_polygon.sql @@ -1,66 +1,111 @@ DROP TRIGGER IF EXISTS trigger_flag ON osm_poi_polygon; +DROP TRIGGER IF EXISTS trigger_store ON osm_poi_polygon; DROP TRIGGER IF EXISTS trigger_refresh ON poi_polygon.updates; +CREATE SCHEMA IF NOT EXISTS poi_polygon; + +CREATE TABLE IF NOT EXISTS poi_polygon.osm_ids +( + osm_id bigint +); + -- etldoc: osm_poi_polygon -> osm_poi_polygon -CREATE OR REPLACE FUNCTION update_poi_polygon() RETURNS VOID AS $$ -BEGIN - UPDATE osm_poi_polygon - SET geometry = - CASE WHEN ST_NPoints(ST_ConvexHull(geometry))=ST_NPoints(geometry) - THEN ST_Centroid(geometry) - ELSE ST_PointOnSurface(ST_MakeValid(geometry)) - END - WHERE ST_GeometryType(geometry) <> 'ST_Point'; - - UPDATE osm_poi_polygon - SET subclass = 'subway' - WHERE station = 'subway' and subclass='station'; - - UPDATE osm_poi_polygon +CREATE OR REPLACE FUNCTION update_poi_polygon(full_update boolean) RETURNS void AS +$$ + UPDATE osm_poi_polygon + SET geometry = + CASE + WHEN ST_NPoints(ST_ConvexHull(geometry)) = ST_NPoints(geometry) + THEN ST_Centroid(geometry) + ELSE ST_PointOnSurface(geometry) + END + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_polygon.osm_ids)) + AND ST_GeometryType(geometry) <> 'ST_Point' + AND ST_IsValid(geometry); + + UPDATE osm_poi_polygon + SET subclass = 'subway' + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_polygon.osm_ids)) + AND station = 'subway' + AND subclass = 'station'; + + UPDATE osm_poi_polygon SET subclass = 'halt' - WHERE funicular = 'yes' and subclass='station'; + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_polygon.osm_ids)) + AND funicular = 'yes' + AND subclass = 'station'; - UPDATE osm_poi_polygon - SET tags = update_tags(tags, geometry) - WHERE COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL; + UPDATE osm_poi_polygon + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM poi_polygon.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); - ANALYZE osm_poi_polygon; -END; -$$ LANGUAGE plpgsql; +$$ LANGUAGE SQL; -SELECT update_poi_polygon(); +SELECT update_poi_polygon(true); -- Handle updates -CREATE SCHEMA IF NOT EXISTS poi_polygon; +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; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; -CREATE TABLE IF NOT EXISTS poi_polygon.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION poi_polygon.flag() RETURNS trigger AS $$ +CREATE TABLE IF NOT EXISTS poi_polygon.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION poi_polygon.flag() RETURNS trigger AS +$$ BEGIN - INSERT INTO poi_polygon.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + INSERT INTO poi_polygon.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; END; -$$ language plpgsql; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION poi_polygon.refresh() RETURNS trigger AS - $BODY$ - BEGIN +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN RAISE LOG 'Refresh poi_polygon'; - PERFORM update_poi_polygon(); + PERFORM update_poi_polygon(false); + -- noinspection SqlWithoutWhere + DELETE FROM poi_polygon.osm_ids; + -- noinspection SqlWithoutWhere DELETE FROM poi_polygon.updates; - RETURN null; - END; - $BODY$ -language plpgsql; + + RAISE LOG 'Refresh poi_polygon done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_poi_polygon + FOR EACH ROW +EXECUTE PROCEDURE poi_polygon.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE ON osm_poi_polygon + AFTER INSERT OR UPDATE OR DELETE + ON osm_poi_polygon FOR EACH STATEMENT - EXECUTE PROCEDURE poi_polygon.flag(); +EXECUTE PROCEDURE poi_polygon.flag(); CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON poi_polygon.updates + AFTER INSERT + ON poi_polygon.updates INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE poi_polygon.refresh(); +EXECUTE PROCEDURE poi_polygon.refresh(); diff --git a/layers/transportation/class.sql b/layers/transportation/class.sql index 187b5f812..c73ce817e 100644 --- a/layers/transportation/class.sql +++ b/layers/transportation/class.sql @@ -1,64 +1,93 @@ -CREATE OR REPLACE FUNCTION brunnel(is_bridge BOOL, is_tunnel BOOL, is_ford BOOL) RETURNS TEXT AS $$ - SELECT CASE - WHEN is_bridge THEN 'bridge' - WHEN is_tunnel THEN 'tunnel' - WHEN is_ford THEN 'ford' - ELSE NULL - END; -$$ LANGUAGE SQL IMMUTABLE STRICT; +CREATE OR REPLACE FUNCTION brunnel(is_bridge bool, is_tunnel bool, is_ford bool) RETURNS text AS +$$ +SELECT CASE + WHEN is_bridge THEN 'bridge' + WHEN is_tunnel THEN 'tunnel' + WHEN is_ford THEN 'ford' + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; -- The classes for highways are derived from the classes used in ClearTables -- https://github.com/ClearTables/ClearTables/blob/master/transportation.lua -CREATE OR REPLACE FUNCTION highway_class(highway TEXT, public_transport TEXT, construction TEXT) RETURNS TEXT AS $$ - SELECT CASE - WHEN highway IN ('motorway', 'motorway_link') THEN 'motorway' - WHEN highway IN ('trunk', 'trunk_link') THEN 'trunk' - WHEN highway IN ('primary', 'primary_link') THEN 'primary' - WHEN highway IN ('secondary', 'secondary_link') THEN 'secondary' - WHEN highway IN ('tertiary', 'tertiary_link') THEN 'tertiary' - WHEN highway IN ('unclassified', 'residential', 'living_street', 'road') THEN 'minor' - WHEN highway IN ('pedestrian', 'path', 'footway', 'cycleway', 'steps', 'bridleway', 'corridor') OR public_transport IN ('platform') THEN 'path' - WHEN highway IN ('service', 'track', 'raceway') THEN highway - WHEN highway = 'construction' THEN CASE - WHEN construction IN ('motorway', 'motorway_link') THEN 'motorway_construction' - WHEN construction IN ('trunk', 'trunk_link') THEN 'trunk_construction' - WHEN construction IN ('primary', 'primary_link') THEN 'primary_construction' - WHEN construction IN ('secondary', 'secondary_link') THEN 'secondary_construction' - WHEN construction IN ('tertiary', 'tertiary_link') THEN 'tertiary_construction' - WHEN construction = '' OR construction IN ('unclassified', 'residential', 'living_street', 'road') THEN 'minor_construction' - WHEN construction IN ('pedestrian', 'path', 'footway', 'cycleway', 'steps', 'bridleway', 'corridor') OR public_transport IN ('platform') THEN 'path_construction' - WHEN construction IN ('service', 'track', 'raceway') THEN CONCAT(highway, '_construction') - ELSE NULL - END - ELSE NULL - END; -$$ LANGUAGE SQL IMMUTABLE; +CREATE OR REPLACE FUNCTION highway_class(highway text, public_transport text, construction text) RETURNS text AS +$$ +SELECT CASE + %%FIELD_MAPPING: class %% + END; +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; -- The classes for railways are derived from the classes used in ClearTables -- https://github.com/ClearTables/ClearTables/blob/master/transportation.lua -CREATE OR REPLACE FUNCTION railway_class(railway TEXT) RETURNS TEXT AS $$ - SELECT CASE - WHEN railway IN ('rail', 'narrow_gauge', 'preserved', 'funicular') THEN 'rail' - WHEN railway IN ('subway', 'light_rail', 'monorail', 'tram') THEN 'transit' - ELSE NULL - END; -$$ LANGUAGE SQL IMMUTABLE STRICT; +CREATE OR REPLACE FUNCTION railway_class(railway text) RETURNS text AS +$$ +SELECT CASE + WHEN railway IN ('rail', 'narrow_gauge', 'preserved', 'funicular') THEN 'rail' + WHEN railway IN ('subway', 'light_rail', 'monorail', 'tram') THEN 'transit' + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; -- Limit service to only the most important values to ensure -- we always know the values of service -CREATE OR REPLACE FUNCTION service_value(service TEXT) RETURNS TEXT AS $$ - SELECT CASE - WHEN service IN ('spur', 'yard', 'siding', 'crossover', 'driveway', 'alley', 'parking_aisle') THEN service - ELSE NULL - END; -$$ LANGUAGE SQL IMMUTABLE STRICT; +CREATE OR REPLACE FUNCTION service_value(service text) RETURNS text AS +$$ +SELECT CASE + WHEN service IN ('spur', 'yard', 'siding', 'crossover', 'driveway', 'alley', 'parking_aisle') THEN service + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; -- Limit surface to only the most important values to ensure -- we always know the values of surface -CREATE OR REPLACE FUNCTION surface_value(surface TEXT) RETURNS TEXT AS $$ - SELECT CASE - WHEN surface IN ('paved', 'asphalt', 'cobblestone', 'concrete', 'concrete:lanes', 'concrete:plates', 'metal', 'paving_stones', 'sett', 'unhewn_cobblestone', 'wood') 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' - ELSE NULL - END; -$$ LANGUAGE SQL IMMUTABLE STRICT; +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' + WHEN surface IN ('unpaved', 'compacted', 'dirt', 'earth', 'fine_gravel', 'grass', 'grass_paver', 'gravel', + 'gravel_turf', 'ground', 'ice', 'mud', 'pebblestone', 'salt', 'sand', 'snow', 'woodchips') + THEN 'unpaved' + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; + +-- Determine which transportation features are shown at zoom 12 +CREATE OR REPLACE FUNCTION transportation_filter_z12(highway text, construction text) RETURNS boolean AS +$$ +SELECT CASE + WHEN highway IN ('unclassified', 'residential') THEN TRUE + WHEN highway_class(highway, '', construction) IN + ( + 'motorway', 'trunk', 'primary', 'secondary', 'tertiary', 'raceway', + 'motorway_construction', 'trunk_construction', 'primary_construction', + 'secondary_construction', 'tertiary_construction', 'raceway_construction', + 'busway', 'bus_guideway' + ) THEN TRUE --includes ramps + ELSE FALSE + END +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; + +-- Determine which transportation features are shown at zoom 13 +-- Assumes that piers have already been excluded +CREATE OR REPLACE FUNCTION transportation_filter_z13(highway text, + public_transport text, + construction text, + service text) RETURNS boolean AS +$$ +SELECT CASE + WHEN transportation_filter_z12(highway, construction) THEN TRUE + WHEN highway = 'service' OR construction = 'service' THEN service NOT IN ('driveway', 'parking_aisle') + WHEN highway_class(highway, public_transport, construction) IN ('minor', 'minor_construction') THEN TRUE + ELSE FALSE + END +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; diff --git a/layers/transportation/etl_diagram.png b/layers/transportation/etl_diagram.png index ba57d14d3..c59403224 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 000000000..058864521 --- /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/layer.sql b/layers/transportation/layer.sql deleted file mode 100644 index b36dbc0b9..000000000 --- a/layers/transportation/layer.sql +++ /dev/null @@ -1,390 +0,0 @@ -CREATE OR REPLACE FUNCTION highway_is_link(highway TEXT) RETURNS BOOLEAN AS $$ - SELECT highway LIKE '%_link'; -$$ LANGUAGE SQL IMMUTABLE STRICT; - - --- etldoc: layer_transportation[shape=record fillcolor=lightpink, style="rounded,filled", --- etldoc: label=" layer_transportation | z4 | z5 | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; -CREATE OR REPLACE FUNCTION layer_transportation(bbox geometry, zoom_level int) -RETURNS TABLE(osm_id bigint, geometry geometry, class text, subclass text, -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) AS $$ - SELECT - osm_id, geometry, - CASE - WHEN NULLIF(highway, '') IS NOT NULL OR NULLIF(public_transport, '') IS NOT NULL 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 - END AS class, - CASE - WHEN railway IS NOT NULL THEN railway - WHEN (highway IS NOT NULL OR public_transport IS NOT NULL) - AND highway_class(highway, public_transport, construction) = 'path' - THEN COALESCE(NULLIF(public_transport, ''), highway) - ELSE NULL - END AS subclass, - -- 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, - brunnel(is_bridge, is_tunnel, is_ford) AS brunnel, - NULLIF(service, '') AS service, - NULLIF(layer, 0) AS layer, - "level", - CASE WHEN indoor=TRUE THEN 1 ELSE NULL END as indoor, - NULLIF(bicycle, '') AS bicycle, - NULLIF(foot, '') AS foot, - NULLIF(horse, '') AS horse, - NULLIF(mtb_scale, '') AS mtb_scale, - NULLIF(surface, '') AS surface - FROM ( - -- etldoc: osm_transportation_merge_linestring_gen7 -> layer_transportation:z4 - SELECT - osm_id, geometry, - highway, construction, NULL AS railway, NULL AS aerialway, NULL AS shipway, - NULL AS public_transport, NULL AS service, - NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, - NULL::boolean AS is_ford, - NULL::boolean AS is_ramp, NULL::int AS is_oneway, NULL as man_made, - NULL::int AS layer, NULL::int AS level, NULL::boolean AS indoor, - NULL as bicycle, NULL as foot, NULL as horse, NULL as mtb_scale, - NULL AS surface, z_order - FROM osm_transportation_merge_linestring_gen7 - WHERE zoom_level = 4 - UNION ALL - - -- etldoc: osm_transportation_merge_linestring_gen6 -> layer_transportation:z5 - SELECT - osm_id, geometry, - highway, construction, NULL AS railway, NULL AS aerialway, NULL AS shipway, - NULL AS public_transport, NULL AS service, - NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, - NULL::boolean AS is_ford, - NULL::boolean AS is_ramp, NULL::int AS is_oneway, NULL as man_made, - NULL::int AS layer, NULL::int AS level, NULL::boolean AS indoor, - NULL as bicycle, NULL as foot, NULL as horse, NULL as mtb_scale, - NULL AS surface, z_order - FROM osm_transportation_merge_linestring_gen6 - WHERE zoom_level = 5 - UNION ALL - - -- etldoc: osm_transportation_merge_linestring_gen5 -> layer_transportation:z6 - SELECT - osm_id, geometry, - highway, construction, NULL AS railway, NULL AS aerialway, NULL AS shipway, - NULL AS public_transport, NULL AS service, - NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, - NULL::boolean AS is_ford, - NULL::boolean AS is_ramp, NULL::int AS is_oneway, NULL as man_made, - NULL::int AS layer, NULL::int AS level, NULL::boolean AS indoor, - NULL as bicycle, NULL as foot, NULL as horse, NULL as mtb_scale, - NULL AS surface, z_order - FROM osm_transportation_merge_linestring_gen5 - WHERE zoom_level = 6 - UNION ALL - - -- etldoc: osm_transportation_merge_linestring_gen4 -> layer_transportation:z7 - SELECT - osm_id, geometry, - highway, construction, NULL AS railway, NULL AS aerialway, NULL AS shipway, - NULL AS public_transport, NULL AS service, - NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, - NULL::boolean AS is_ford, - NULL::boolean AS is_ramp, NULL::int AS is_oneway, NULL as man_made, - NULL::int AS layer, NULL::int AS level, NULL::boolean AS indoor, - NULL as bicycle, NULL as foot, NULL as horse, NULL as mtb_scale, - NULL AS surface, z_order - FROM osm_transportation_merge_linestring_gen4 - WHERE zoom_level = 7 - UNION ALL - - -- etldoc: osm_transportation_merge_linestring_gen3 -> layer_transportation:z8 - SELECT - osm_id, geometry, - highway, construction, NULL AS railway, NULL AS aerialway, NULL AS shipway, - NULL AS public_transport, NULL AS service, - NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, - NULL::boolean AS is_ford, - NULL::boolean AS is_ramp, NULL::int AS is_oneway, NULL as man_made, - NULL::int AS layer, NULL::int AS level, NULL::boolean AS indoor, - NULL as bicycle, NULL as foot, NULL as horse, NULL as mtb_scale, - NULL AS surface, z_order - FROM osm_transportation_merge_linestring_gen3 - WHERE zoom_level = 8 - UNION ALL - - -- etldoc: osm_highway_linestring_gen2 -> layer_transportation:z9 - -- etldoc: osm_highway_linestring_gen2 -> layer_transportation:z10 - SELECT - osm_id, geometry, - highway, construction, NULL AS railway, NULL AS aerialway, NULL AS shipway, - NULL AS public_transport, NULL AS service, - NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, - NULL::boolean AS is_ford, - NULL::boolean AS is_ramp, NULL::int AS is_oneway, NULL as man_made, - layer, NULL::int AS level, NULL::boolean AS indoor, - bicycle, foot, horse, mtb_scale, - NULL AS surface, z_order - FROM osm_highway_linestring_gen2 - WHERE zoom_level BETWEEN 9 AND 10 - AND st_length(geometry)>zres(11) - UNION ALL - - -- etldoc: osm_highway_linestring_gen1 -> layer_transportation:z11 - SELECT - osm_id, geometry, - highway, construction, NULL AS railway, NULL AS aerialway, NULL AS shipway, - NULL AS public_transport, NULL AS service, - NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, - NULL::boolean AS is_ford, - NULL::boolean AS is_ramp, NULL::int AS is_oneway, NULL as man_made, - layer, NULL::int AS level, NULL::boolean AS indoor, - bicycle, foot, horse, mtb_scale, - NULL AS surface, z_order - FROM osm_highway_linestring_gen1 - 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, construction, NULL AS railway, NULL AS aerialway, NULL AS shipway, - public_transport, service_value(service) AS service, - is_bridge, is_tunnel, is_ford, is_ramp, is_oneway, man_made, - layer, - CASE WHEN highway IN ('footway', 'steps') THEN "level" - ELSE NULL::int - END AS "level", - CASE WHEN highway IN ('footway', 'steps') THEN indoor - ELSE NULL::boolean - END AS indoor, - bicycle, foot, horse, mtb_scale, - surface_value(surface) AS "surface", - z_order - FROM osm_highway_linestring - 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) - ) - ) - UNION ALL - - -- etldoc: osm_railway_linestring_gen5 -> layer_transportation:z8 - SELECT - osm_id, geometry, - NULL AS highway, NULL AS construction, railway, NULL AS aerialway, NULL AS shipway, - NULL AS public_transport, service_value(service) AS service, - NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, - NULL::boolean AS is_ford, - NULL::boolean AS is_ramp, NULL::int AS is_oneway, NULL as man_made, - NULL::int AS layer, NULL::int AS level, NULL::boolean AS indoor, - NULL as bicycle, NULL as foot, NULL as horse, NULL as mtb_scale, - NULL as surface, z_order - FROM osm_railway_linestring_gen5 - WHERE zoom_level = 8 - AND railway='rail' AND service = '' and usage='main' - UNION ALL - - -- etldoc: osm_railway_linestring_gen4 -> layer_transportation:z9 - SELECT - osm_id, geometry, - NULL AS highway, NULL AS construction, railway, NULL AS aerialway, NULL AS shipway, - NULL AS public_transport, service_value(service) AS service, - NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, - NULL::boolean AS is_ford, - NULL::boolean AS is_ramp, NULL::int AS is_oneway, NULL as man_made, - layer, NULL::int AS level, NULL::boolean AS indoor, - NULL as bicycle, NULL as foot, NULL as horse, NULL as mtb_scale, - NULL AS surface, z_order - FROM osm_railway_linestring_gen4 - WHERE zoom_level = 9 - AND railway='rail' AND service = '' and usage='main' - UNION ALL - - -- etldoc: osm_railway_linestring_gen3 -> layer_transportation:z10 - SELECT - osm_id, geometry, - NULL AS highway, NULL AS construction, railway, NULL AS aerialway, NULL AS shipway, - NULL AS public_transport, service_value(service) AS service, - is_bridge, is_tunnel, is_ford, 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_railway_linestring_gen3 - WHERE zoom_level = 10 - AND railway IN ('rail', 'narrow_gauge') AND service = '' - UNION ALL - - -- etldoc: osm_railway_linestring_gen2 -> layer_transportation:z11 - SELECT - osm_id, geometry, - NULL AS highway, NULL AS construction, railway, NULL AS aerialway, NULL AS shipway, - NULL AS public_transport, service_value(service) AS service, - is_bridge, is_tunnel, is_ford, 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_railway_linestring_gen2 - WHERE zoom_level = 11 - AND railway IN ('rail', 'narrow_gauge', 'light_rail') AND service = '' - UNION ALL - - -- etldoc: osm_railway_linestring_gen1 -> layer_transportation:z12 - SELECT - osm_id, geometry, - NULL AS highway, NULL AS construction, railway, NULL AS aerialway, NULL AS shipway, - NULL AS public_transport, service_value(service) AS service, - is_bridge, is_tunnel, is_ford, 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_railway_linestring_gen1 - WHERE zoom_level = 12 - AND railway IN ('rail', 'narrow_gauge', 'light_rail') AND service = '' - UNION ALL - - -- etldoc: osm_railway_linestring -> layer_transportation:z13 - -- etldoc: osm_railway_linestring -> layer_transportation:z14_ - SELECT - osm_id, geometry, - NULL AS highway, NULL AS construction, railway, NULL AS aerialway, NULL AS shipway, - NULL AS public_transport, service_value(service) AS service, - is_bridge, is_tunnel, is_ford, 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_railway_linestring - WHERE zoom_level = 13 - AND railway IN ('rail', 'narrow_gauge', 'light_rail') AND service = '' - OR zoom_Level >= 14 - UNION ALL - - -- etldoc: osm_aerialway_linestring_gen1 -> layer_transportation:z12 - SELECT - osm_id, geometry, - NULL AS highway, NULL AS construction, NULL as railway, aerialway, NULL AS shipway, - NULL AS public_transport, service_value(service) AS service, - is_bridge, is_tunnel, is_ford, 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_aerialway_linestring_gen1 - WHERE zoom_level = 12 - UNION ALL - - -- etldoc: osm_aerialway_linestring -> layer_transportation:z13 - -- etldoc: osm_aerialway_linestring -> layer_transportation:z14_ - SELECT - osm_id, geometry, - NULL AS highway, NULL AS construction, NULL as railway, aerialway, NULL AS shipway, - NULL AS public_transport, service_value(service) AS service, - is_bridge, is_tunnel, is_ford, 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_aerialway_linestring - WHERE zoom_level >= 13 - UNION ALL - - -- etldoc: osm_shipway_linestring_gen2 -> layer_transportation:z11 - SELECT - osm_id, geometry, - NULL AS highway, NULL AS construction, NULL AS railway, NULL AS aerialway, shipway, - NULL AS public_transport, service_value(service) AS service, - is_bridge, is_tunnel, is_ford, 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_gen2 - WHERE zoom_level = 11 - UNION ALL - - -- etldoc: osm_shipway_linestring_gen1 -> layer_transportation:z12 - SELECT - osm_id, geometry, - NULL AS highway, NULL AS construction, NULL AS railway, NULL AS aerialway, shipway, - NULL AS public_transport, service_value(service) AS service, - is_bridge, is_tunnel, is_ford, 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_gen1 - WHERE zoom_level = 12 - UNION ALL - - -- etldoc: osm_shipway_linestring -> layer_transportation:z13 - -- etldoc: osm_shipway_linestring -> layer_transportation:z14_ - SELECT - osm_id, geometry, - NULL AS highway, NULL AS construction, NULL AS railway, NULL AS aerialway, shipway, - NULL AS public_transport, service_value(service) AS service, - is_bridge, is_tunnel, is_ford, 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 - WHERE zoom_level >= 13 - UNION ALL - - -- NOTE: We limit the selection of polys because we need to be - -- careful to net get false positives here because - -- it is possible that closed linestrings appear both as - -- highway linestrings and as polygon - -- etldoc: osm_highway_polygon -> layer_transportation:z13 - -- etldoc: osm_highway_polygon -> layer_transportation:z14_ - SELECT - osm_id, geometry, - highway, NULL AS construction, NULL AS railway, NULL AS aerialway, NULL AS shipway, - public_transport, NULL AS service, - CASE WHEN man_made IN ('bridge') THEN TRUE - ELSE FALSE - END AS is_bridge, FALSE AS is_tunnel, FALSE AS is_ford, - FALSE AS is_ramp, FALSE::int AS is_oneway, man_made, - layer, NULL::int AS level, NULL::boolean AS indoor, - NULL as bicycle, NULL as foot, NULL as horse, NULL as mtb_scale, - NULL AS surface, z_order - FROM osm_highway_polygon - -- We do not want underground pedestrian areas for now - WHERE zoom_level >= 13 - AND ( - man_made IN ('bridge', 'pier') - OR (is_area AND COALESCE(layer, 0) >= 0) - ) - ) AS zoom_levels - WHERE geometry && bbox - ORDER BY z_order ASC; -$$ LANGUAGE SQL IMMUTABLE; - - -CREATE OR REPLACE FUNCTION layer_transportation_lite(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, surface TEXT) AS $$ - SELECT - osm_id, geometry, class, subclass, ramp, oneway, brunnel, service, layer, level, indoor, surface - FROM layer_transportation(bbox, zoom_level) - WHERE zoom_level < 13 - UNION ALL - SELECT - osm_id, geometry, class, subclass, ramp, oneway, brunnel, service, layer, level, indoor, surface - FROM layer_transportation(bbox, 14) - WHERE zoom_level >= 13 -$$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/transportation/mapping.yaml b/layers/transportation/mapping.yaml index 29c0c2565..05a9c0f83 100644 --- a/layers/transportation/mapping.yaml +++ b/layers/transportation/mapping.yaml @@ -1,57 +1,51 @@ generalized_tables: -# etldoc: imposm3 -> osm_railway_linestring_gen5 - railway_linestring_gen5: - source: railway_linestring_gen4 +# etldoc: osm_railway_linestring_gen_z9 -> osm_railway_linestring_gen_z8 + railway_linestring_gen_z8: + source: railway_linestring_gen_z9 tolerance: ZRES9 -# etldoc: imposm3 -> osm_railway_linestring_gen4 - railway_linestring_gen4: - source: railway_linestring_gen3 +# etldoc: osm_railway_linestring_gen_z10 -> osm_railway_linestring_gen_z9 + railway_linestring_gen_z9: + source: railway_linestring_gen_z10 tolerance: ZRES10 -# etldoc: imposm3 -> osm_railway_linestring_gen3 - railway_linestring_gen3: - source: railway_linestring_gen2 +# etldoc: osm_railway_linestring_gen_z11 -> osm_railway_linestring_gen_z10 + railway_linestring_gen_z10: + source: railway_linestring_gen_z11 tolerance: ZRES11 -# etldoc: imposm3 -> osm_railway_linestring_gen2 - railway_linestring_gen2: - source: railway_linestring_gen1 +# etldoc: osm_railway_linestring_gen_z12 -> osm_railway_linestring_gen_z11 + railway_linestring_gen_z11: + source: railway_linestring_gen_z12 tolerance: ZRES12 -# etldoc: imposm3 -> osm_railway_linestring_gen1 - railway_linestring_gen1: +# etldoc: osm_railway_linestring -> osm_railway_linestring_gen_z12 + railway_linestring_gen_z12: source: railway_linestring sql_filter: railway IN ('rail', 'narrow_gauge', 'light_rail') AND service='' AND ST_IsValid(geometry) tolerance: ZRES13 -# etldoc: imposm3 -> osm_aerialway_linestring_gen1 - aerialway_linestring_gen1: +# etldoc: osm_aerialway_linestring -> osm_aerialway_linestring_gen_z12 + aerialway_linestring_gen_z12: source: aerialway_linestring sql_filter: ST_IsValid(geometry) tolerance: ZRES13 -# etldoc: imposm3 -> osm_shipway_linestring_gen2 - shipway_linestring_gen2: - source: shipway_linestring_gen1 +# etldoc: osm_shipway_linestring_gen_z12 -> osm_shipway_linestring_gen_z11 + shipway_linestring_gen_z11: + source: shipway_linestring_gen_z12 tolerance: ZRES12 -# etldoc: imposm3 -> osm_shipway_linestring_gen1 - shipway_linestring_gen1: +# etldoc: osm_shipway_linestring -> osm_shipway_linestring_gen_z12 + shipway_linestring_gen_z12: source: shipway_linestring sql_filter: ST_IsValid(geometry) tolerance: ZRES13 -# etldoc: imposm3 -> osm_highway_linestring_gen2 - highway_linestring_gen2: - source: highway_linestring_gen1 - 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: imposm3 -> osm_highway_linestring_gen1 - highway_linestring_gen1: +# 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 @@ -98,6 +92,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 @@ -149,16 +151,25 @@ mtb_scale_field: &mtb_scale key: mtb:scale name: mtb_scale type: string +sac_scale_field: &sac_scale + key: sac_scale + name: sac_scale + type: string surface_field: &surface key: surface name: surface type: string +expressway_field: &expressway + key: expressway + name: expressway + type: bool tables: # etldoc: imposm3 -> osm_highway_linestring highway_linestring: type: linestring - fields: + _resolve_wikidata: false + columns: - name: osm_id type: id - name: geometry @@ -188,6 +199,8 @@ tables: - *oneway - *area - *service + - *access + - *toll - *usage - *public_transport - *man_made @@ -195,7 +208,9 @@ tables: - *foot - *horse - *mtb_scale + - *sac_scale - *surface + - *expressway mapping: highway: - motorway @@ -222,16 +237,22 @@ 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: type: linestring - fields: + _resolve_wikidata: false + columns: - name: osm_id type: id - name: geometry @@ -273,7 +294,8 @@ tables: # etldoc: imposm3 -> osm_aerialway_linestring aerialway_linestring: type: linestring - fields: + _resolve_wikidata: false + columns: - name: osm_id type: id - name: geometry @@ -299,13 +321,20 @@ 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: type: linestring - fields: + _resolve_wikidata: false + columns: - name: osm_id type: id - name: geometry @@ -336,7 +365,7 @@ tables: # etldoc: imposm3 -> osm_highway_polygon highway_polygon: type: polygon - fields: + columns: - name: osm_id type: id - name: geometry @@ -354,6 +383,7 @@ tables: type: bool - *public_transport - *man_made + - *service mapping: highway: - path @@ -369,6 +399,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: @@ -385,6 +442,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 eacdce726..c47f739ba 100644 Binary files a/layers/transportation/mapping_diagram.png and b/layers/transportation/mapping_diagram.png differ diff --git a/layers/transportation/network_type.sql b/layers/transportation/network_type.sql new file mode 100644 index 000000000..b54dc8d67 --- /dev/null +++ b/layers/transportation/network_type.sql @@ -0,0 +1,34 @@ +DROP TRIGGER IF EXISTS trigger_store_transportation_route_member ON osm_route_member; +DROP TRIGGER IF EXISTS trigger_store_transportation_highway_linestring ON osm_highway_linestring; +DROP TRIGGER IF EXISTS trigger_flag_transportation_name ON transportation_name.network_changes; +DROP TRIGGER IF EXISTS trigger_refresh_network ON transportation_name.updates_network; + +DROP TRIGGER IF EXISTS trigger_store_transportation_name_network ON osm_transportation_name_network; +DROP TRIGGER IF EXISTS trigger_flag_name ON transportation_name.name_changes; +DROP TRIGGER IF EXISTS trigger_refresh_name ON transportation_name.updates_name; + +DO +$$ + BEGIN + PERFORM 'route_network_type'::regtype; + EXCEPTION + WHEN undefined_object THEN + CREATE TYPE route_network_type AS enum ( + 'us-interstate', 'us-highway', 'us-state', + 'ca-transcanada', + 'gb-motorway', 'gb-trunk' + ); + END +$$; + +DO +$$ + 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; +$$; diff --git a/layers/transportation/style.json b/layers/transportation/style.json new file mode 100644 index 000000000..c5be85154 --- /dev/null +++ b/layers/transportation/style.json @@ -0,0 +1,7143 @@ +{ + "layers": [ + { + "id": "ferry", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 10, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#7d7df8", + "line-width": { + "stops": [ + [ + 10, + 0.5 + ], + [ + 14, + 1.1 + ] + ] + }, + "line-dasharray": [ + 2, + 2 + ] + }, + "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_pedestrian", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(204, 196, 176, 0.45)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 14, + 0.5 + ], + [ + 20, + 6 + ] + ] + }, + "line-dasharray": [ + 1, + 0.75 + ] + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "==", + "brunnel", + "tunnel" + ], + [ + "in", + "class", + "path", + "pedestrian" + ] + ], + "order": 39 + }, + { + "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": 40 + }, + { + "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": 41 + }, + { + "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": 42 + }, + { + "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": 43 + }, + { + "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": 44 + }, + { + "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": 45 + }, + { + "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": 46 + }, + { + "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": 47 + }, + { + "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": 48 + }, + { + "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": 49 + }, + { + "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": 50 + }, + { + "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": 51 + }, + { + "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": 52 + }, + { + "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": 53 + }, + { + "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": 54 + }, + { + "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": 55 + }, + { + "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": 56 + }, + { + "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", + "bridge", + "tunnel" + ], + [ + "==", + "class", + "rail" + ] + ], + "order": 57 + }, + { + "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": 58 + }, + { + "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": 59 + }, + { + "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": 60 + }, + { + "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": 61 + }, + { + "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": 62 + }, + { + "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": 63 + }, + { + "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": 64 + }, + { + "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": 65 + }, + { + "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": 66 + }, + { + "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": 67 + }, + { + "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": 68 + }, + { + "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": 69 + }, + { + "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": 70 + }, + { + "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": 71 + }, + { + "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": 72 + }, + { + "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": 73 + }, + { + "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": 74 + }, + { + "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": 75 + }, + { + "id": "road_path_cycleway_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 14, + "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" + ], + [ + "==", + "subclass", + "cycleway" + ] + ], + "order": 76 + }, + { + "id": "road_path_footway_casing", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 15, + "layout": { + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(255, 255, 255, 0.8)", + "line-width": { + "base": 1.2, + "stops": [ + [ + 14, + 2.7 + ], + [ + 15, + 3 + ], + [ + 17, + 3.3 + ], + [ + 18, + 3.6 + ] + ] + } + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "$type", + "LineString" + ], + [ + "!in", + "brunnel", + "bridge", + "tunnel" + ], + [ + "in", + "class", + "path" + ], + [ + "in", + "subclass", + "footway", + "path" + ] + ], + "order": 77 + }, + { + "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" + ], + [ + "==", + "subclass", + "cycleway" + ] + ], + "order": 78 + }, + { + "id": "road_path_footway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 13, + "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" + ] + ], + "order": 79 + }, + { + "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": 80 + }, + { + "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": 81 + }, + { + "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": 82 + }, + { + "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": 83 + }, + { + "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": 84 + }, + { + "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": 85 + }, + { + "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": 86 + }, + { + "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": 87 + }, + { + "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": 88 + }, + { + "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": 89 + }, + { + "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": 90 + }, + { + "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": 91 + }, + { + "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": 92 + }, + { + "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": 93 + }, + { + "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": 94 + }, + { + "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": 95 + }, + { + "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": 96 + }, + { + "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": 97 + }, + { + "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": 98 + }, + { + "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": 99 + }, + { + "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": 100 + }, + { + "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": 101 + }, + { + "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": 102 + }, + { + "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": 105 + }, + { + "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": 106 + }, + { + "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": 107 + }, + { + "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": 108 + }, + { + "id": "bridge_path_cycleway_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" + ], + [ + "==", + "subclass", + "cycleway" + ] + ], + "order": 109 + }, + { + "id": "bridge_path_footway_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" + ], + [ + "==", + "subclass", + "footway" + ] + ], + "order": 110 + }, + { + "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": 111 + }, + { + "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": 112 + }, + { + "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": 113 + }, + { + "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": 114 + }, + { + "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": 115 + }, + { + "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" + ], + [ + "==", + "subclass", + "cycleway" + ] + ], + "order": 116 + }, + { + "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" + ], + [ + "==", + "subclass", + "footway" + ] + ], + "order": 117 + }, + { + "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": 118 + }, + { + "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": 119 + }, + { + "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": 120 + }, + { + "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": 121 + }, + { + "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": 122 + }, + { + "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": 123 + }, + { + "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": 124 + }, + { + "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": 125 + }, + { + "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": 126 + }, + { + "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": 127 + }, + { + "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": 128 + }, + { + "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": 129 + }, + { + "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": 130 + }, + { + "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": 131 + }, + { + "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": 132 + }, + { + "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": 133 + }, + { + "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": 134 + }, + { + "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": 135 + }, + { + "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": 136 + }, + { + "id": "road_path-cycleway_oneway", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 15, + "layout": { + "icon-size": { + "stops": [ + [ + 15, + 0.7 + ], + [ + 20, + 1 + ] + ] + }, + "icon-image": "oneway-cycleway", + "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" + ], + [ + "==", + "subclass", + "cycleway" + ] + ], + "order": 172 + }, + { + "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": 173 + }, + { + "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": 174 + } + ] +} \ No newline at end of file diff --git a/layers/transportation/transportation.sql b/layers/transportation/transportation.sql new file mode 100644 index 000000000..349fe1ca5 --- /dev/null +++ b/layers/transportation/transportation.sql @@ -0,0 +1,826 @@ +CREATE OR REPLACE FUNCTION highway_is_link(highway text) RETURNS boolean AS +$$ +SELECT highway LIKE '%_link'; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; + + +-- etldoc: layer_transportation[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label=" layer_transportation | z4 | z5 | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; +CREATE OR REPLACE FUNCTION layer_transportation(bbox geometry, zoom_level int) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + class text, + subclass text, + network text, + ramp int, + oneway int, + brunnel text, + service text, + access text, + toll int, + expressway int, + layer int, + level int, + indoor int, + bicycle text, + foot text, + horse text, + mtb_scale text, + surface text + ) +AS +$$ +SELECT osm_id, + geometry, + CASE + WHEN highway <> '' OR public_transport <> '' + THEN highway_class(highway, public_transport, construction) + WHEN railway <> '' THEN railway_class(railway) + WHEN aerialway <> '' THEN 'aerialway' + WHEN shipway <> '' THEN shipway + WHEN man_made <> '' THEN man_made + END AS class, + CASE + WHEN railway IS NOT NULL THEN railway + WHEN (highway IS NOT NULL OR public_transport IS NOT NULL) + AND highway_class(highway, public_transport, construction) = 'path' + THEN COALESCE(NULLIF(public_transport, ''), highway) + WHEN aerialway IS NOT NULL THEN aerialway + END AS subclass, + NULLIF(network, '') AS network, + -- All links are considered as ramps as well + CASE + WHEN highway_is_link(highway) + OR is_ramp + THEN 1 END AS ramp, + CASE WHEN is_oneway <> 0 THEN is_oneway::int END AS oneway, + brunnel(is_bridge, is_tunnel, is_ford) AS brunnel, + NULLIF(service, '') AS service, + access, + CASE WHEN toll = TRUE THEN 1 END AS toll, + CASE WHEN highway NOT IN ('', 'motorway') AND expressway = TRUE THEN 1 END AS expressway, + NULLIF(layer, 0) AS layer, + "level", + CASE WHEN indoor = TRUE THEN 1 END AS indoor, + NULLIF(bicycle, '') AS bicycle, + NULLIF(foot, '') AS foot, + NULLIF(horse, '') AS horse, + NULLIF(mtb_scale, '') AS mtb_scale, + NULLIF(surface, '') AS surface +FROM ( + -- etldoc: osm_transportation_merge_linestring_gen_z4 -> layer_transportation:z4 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + NULL AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z4 + WHERE zoom_level = 4 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z5 -> layer_transportation:z5 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + NULL AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z5 + WHERE zoom_level = 5 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z6 -> layer_transportation:z6 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + NULL AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z6 + WHERE zoom_level = 6 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z7 -> layer_transportation:z7 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + NULL AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z7 + WHERE zoom_level = 7 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z8 -> layer_transportation:z8 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + NULL AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z8 + WHERE zoom_level = 8 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z9 -> layer_transportation:z9 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + access, + toll, + is_bridge, + is_tunnel, + is_ford, + expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + bicycle, + foot, + horse, + mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z9 + WHERE zoom_level = 9 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z10 -> layer_transportation:z10 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + access, + toll, + is_bridge, + is_tunnel, + is_ford, + expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + bicycle, + foot, + horse, + mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z10 + WHERE zoom_level = 10 + UNION ALL + + -- etldoc: osm_transportation_merge_linestring_gen_z11 -> layer_transportation:z11 + SELECT osm_id, + geometry, + highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + NULL AS service, + access, + toll, + is_bridge, + is_tunnel, + is_ford, + expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + bicycle, + foot, + horse, + mtb_scale, + NULL AS surface, + z_order + FROM osm_transportation_merge_linestring_gen_z11 + WHERE zoom_level = 11 + UNION ALL + + -- etldoc: osm_highway_linestring -> layer_transportation:z12 + -- etldoc: osm_highway_linestring -> layer_transportation:z13 + -- etldoc: osm_highway_linestring -> layer_transportation:z14_ + -- etldoc: osm_transportation_name_network -> layer_transportation:z12 + -- etldoc: osm_transportation_name_network -> layer_transportation:z13 + -- etldoc: osm_transportation_name_network -> layer_transportation:z14_ + SELECT hl.osm_id, + hl.geometry, + hl.highway, + construction, + network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + public_transport, + service_value(service) AS service, + CASE WHEN access IN ('private', 'no') THEN 'no' END AS access, + toll, + is_bridge, + is_tunnel, + is_ford, + expressway, + is_ramp, + is_oneway, + man_made, + hl.layer, + CASE WHEN hl.highway IN ('footway', 'steps') THEN hl.level END AS level, + CASE WHEN hl.highway IN ('footway', 'steps') THEN hl.indoor END AS indoor, + bicycle, + foot, + horse, + mtb_scale, + surface_value(surface) AS "surface", + hl.z_order + FROM osm_highway_linestring hl + LEFT OUTER JOIN osm_transportation_name_network n ON hl.osm_id = n.osm_id + WHERE NOT is_area + AND + CASE WHEN zoom_level = 12 THEN + CASE WHEN transportation_filter_z12(hl.highway, hl.construction) THEN TRUE + WHEN hl.highway IN ('track', 'path') THEN n.route_rank = 1 + END + WHEN zoom_level = 13 THEN + CASE WHEN man_made='pier' THEN NOT ST_IsClosed(hl.geometry) + WHEN hl.highway IN ('track', 'path') THEN (hl.name <> '' + OR n.route_rank BETWEEN 1 AND 2 + OR hl.sac_scale <> '' + ) + ELSE transportation_filter_z13(hl.highway, public_transport, hl.construction, service) + END + WHEN zoom_level >= 14 THEN + CASE WHEN man_made='pier' THEN NOT ST_IsClosed(hl.geometry) + ELSE TRUE + END + END + UNION ALL + + -- etldoc: osm_railway_linestring_gen_z8 -> layer_transportation:z8 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_ford, + NULL::boolean AS expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_railway_linestring_gen_z8 + WHERE zoom_level = 8 + AND railway = 'rail' + AND service = '' + AND usage = 'main' + UNION ALL + + -- etldoc: osm_railway_linestring_gen_z9 -> layer_transportation:z9 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_ford, + NULL::boolean AS expressway, + NULL::boolean AS is_ramp, + NULL::int AS is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_railway_linestring_gen_z9 + WHERE zoom_level = 9 + AND railway = 'rail' + AND service = '' + AND usage = 'main' + UNION ALL + + -- etldoc: osm_railway_linestring_gen_z10 -> layer_transportation:z10 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_railway_linestring_gen_z10 + WHERE zoom_level = 10 + AND railway IN ('rail', 'narrow_gauge') + AND service = '' + UNION ALL + + -- etldoc: osm_railway_linestring_gen_z11 -> layer_transportation:z11 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_railway_linestring_gen_z11 + WHERE zoom_level = 11 + AND railway IN ('rail', 'narrow_gauge', 'light_rail') + AND service = '' + UNION ALL + + -- etldoc: osm_railway_linestring_gen_z12 -> layer_transportation:z12 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_railway_linestring_gen_z12 + WHERE zoom_level = 12 + AND railway IN ('rail', 'narrow_gauge', 'light_rail') + AND service = '' + UNION ALL + + -- etldoc: osm_railway_linestring -> layer_transportation:z13 + -- etldoc: osm_railway_linestring -> layer_transportation:z14_ + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + railway, + NULL AS aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_railway_linestring + WHERE zoom_level = 13 + AND railway IN ('rail', 'narrow_gauge', 'light_rail') + AND service = '' + OR zoom_level >= 14 + UNION ALL + + -- etldoc: osm_aerialway_linestring_gen_z12 -> layer_transportation:z12 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_aerialway_linestring_gen_z12 + WHERE zoom_level = 12 + UNION ALL + + -- etldoc: osm_aerialway_linestring -> layer_transportation:z13 + -- etldoc: osm_aerialway_linestring -> layer_transportation:z14_ + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + aerialway, + NULL AS shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_aerialway_linestring + WHERE zoom_level >= 13 + UNION ALL + + -- etldoc: osm_shipway_linestring_gen_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, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_shipway_linestring_gen_z11 + WHERE zoom_level = 11 + UNION ALL + + -- etldoc: osm_shipway_linestring_gen_z12 -> layer_transportation:z12 + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_shipway_linestring_gen_z12 + WHERE zoom_level = 12 + UNION ALL + + -- etldoc: osm_shipway_linestring -> layer_transportation:z13 + -- etldoc: osm_shipway_linestring -> layer_transportation:z14_ + SELECT osm_id, + geometry, + NULL AS highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + shipway, + NULL AS public_transport, + service_value(service) AS service, + NULL::text AS access, + NULL::boolean AS toll, + is_bridge, + is_tunnel, + is_ford, + NULL::boolean AS expressway, + is_ramp, + is_oneway, + NULL AS man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_shipway_linestring + WHERE zoom_level >= 13 + UNION ALL + + -- NOTE: We limit the selection of polys because we need to be + -- careful to net get false positives here because + -- it is possible that closed linestrings appear both as + -- highway linestrings and as polygon + -- etldoc: osm_highway_polygon -> layer_transportation:z13 + -- etldoc: osm_highway_polygon -> layer_transportation:z14_ + SELECT osm_id, + geometry, + highway, + NULL AS construction, + NULL AS network, + NULL AS railway, + NULL AS aerialway, + NULL AS shipway, + public_transport, + NULL AS service, + NULL::text AS access, + NULL::boolean AS toll, + CASE + WHEN man_made IN ('bridge') THEN TRUE + ELSE FALSE + END AS is_bridge, + FALSE AS is_tunnel, + FALSE AS is_ford, + NULL::boolean AS expressway, + FALSE AS is_ramp, + FALSE::int AS is_oneway, + man_made, + layer, + NULL::int AS level, + NULL::boolean AS indoor, + NULL AS bicycle, + NULL AS foot, + NULL AS horse, + NULL AS mtb_scale, + NULL AS surface, + z_order + FROM osm_highway_polygon + -- We do not want underground pedestrian areas for now + WHERE zoom_level >= 13 + AND ( + man_made IN ('bridge', 'pier') + OR (is_area AND COALESCE(layer, 0) >= 0) + ) + ) AS zoom_levels +WHERE geometry && bbox +ORDER BY z_order ASC; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/transportation/transportation.yaml b/layers/transportation/transportation.yaml index b92f6504b..b9cab6446 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,36 +24,79 @@ 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 - - trunk - - primary - - secondary - - tertiary - - minor - - service - - track - - path - - raceway - - motorway_construction - - trunk_construction - - primary_construction - - secondary_construction - - tertiary_construction - - minor_construction - - service_construction - - track_construction - - path_construction - - raceway_construction - - rail - - transit - - cable_car - - gondola - - ferry - - bridge - - pier + motorway: + highway: ['motorway', 'motorway_link'] + trunk: + highway: ['trunk', 'trunk_link'] + primary: + highway: ['primary', 'primary_link'] + secondary: + highway: ['secondary', 'secondary_link'] + tertiary: + highway: ['tertiary', 'tertiary_link'] + minor: + highway: ['unclassified', 'residential', 'living_street', 'road'] + path: + highway: ['pedestrian', 'path', 'footway', 'cycleway', 'steps', 'bridleway', 'corridor'] + public_transport: 'platform' + service: + highway: service + track: + highway: track + raceway: + highway: raceway + busway: + highway: busway + bus_guideway: + highway: bus_guideway + ferry: + highway: shipway + motorway_construction: + __AND__: + highway: construction + construction: ['motorway', 'motorway_link'] + trunk_construction: + __AND__: + highway: construction + construction: ['trunk', 'trunk_link'] + primary_construction: + __AND__: + highway: construction + construction: ['primary', 'primary_link'] + secondary_construction: + __AND__: + highway: construction + construction: ['secondary', 'secondary_link'] + tertiary_construction: + __AND__: + highway: construction + construction: ['tertiary', 'tertiary_link'] + minor_construction: + __AND__: + highway: construction + construction: ['', 'unclassified', 'residential', 'living_street', 'road'] + path_construction: + __AND__: + - highway: construction + - __OR__: + construction: ['pedestrian', 'path', 'footway', 'cycleway', 'steps', 'bridleway', 'corridor'] + public_transport: platform + service_construction: + __AND__: + highway: construction + construction: service + track_construction: + __AND__: + highway: construction + construction: track + raceway_construction: + __AND__: + highway: construction + construction: raceway subclass: description: | Distinguish more specific classes of railway and path: @@ -75,6 +121,13 @@ 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), + or [`gb-*`](http://wiki.openstreetmap.org/wiki/United_Kingdom_Tagging_Guidelines#UK_roads). brunnel: description: | Mark whether way is a tunnel or bridge. @@ -87,12 +140,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. @@ -104,6 +157,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. @@ -138,11 +206,14 @@ 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 - - ./layer.sql + - ./transportation.sql datasources: - type: imposm3 mapping_file: ./mapping.yaml diff --git a/layers/transportation/update_route_member.sql b/layers/transportation/update_route_member.sql new file mode 100644 index 000000000..3a6240291 --- /dev/null +++ b/layers/transportation/update_route_member.sql @@ -0,0 +1,118 @@ +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) 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 = '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) +WHERE network != '' + AND network_type IS DISTINCT FROM osm_route_member_network_type(network) +; + +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; + + INSERT INTO osm_route_member (id, osm_id, network_type, concurrency_index, rank) + SELECT + id, + osm_id, + osm_route_member_network_type(network) AS network_type, + DENSE_RANK() over (PARTITION BY member ORDER BY network_type, 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 osm_route_member rm + WHERE rm.member IN + (SELECT DISTINCT osm_id FROM transportation_name.network_changes) + ON CONFLICT (id, osm_id) DO UPDATE SET concurrency_index = EXCLUDED.concurrency_index, + rank = EXCLUDED.rank, + network_type = EXCLUDED.network_type; +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"); + +CREATE INDEX IF NOT EXISTS osm_highway_linestring_osm_id_idx ON osm_highway_linestring ("osm_id"); +CREATE UNIQUE INDEX IF NOT EXISTS osm_highway_linestring_gen_z11_osm_id_idx ON osm_highway_linestring_gen_z11 ("osm_id"); + +ALTER TABLE osm_route_member ADD COLUMN IF NOT EXISTS concurrency_index int, + ADD COLUMN IF NOT EXISTS rank int; + +-- One-time load of concurrency indexes; updates occur via trigger +INSERT INTO osm_route_member (id, osm_id, concurrency_index, rank) + SELECT + id, + osm_id, + DENSE_RANK() over (PARTITION BY member ORDER BY network_type, 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 osm_route_member + ON CONFLICT (id, osm_id) DO UPDATE SET concurrency_index = EXCLUDED.concurrency_index, rank = EXCLUDED.rank; + +UPDATE osm_highway_linestring hl + SET network = rm.network_type + FROM osm_route_member rm + WHERE hl.osm_id=rm.member AND rm.concurrency_index=1; + +UPDATE osm_highway_linestring_gen_z11 hl + SET network = rm.network_type + FROM osm_route_member 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 a2c185262..68dcc23ad 100644 --- a/layers/transportation/update_transportation_merge.sql +++ b/layers/transportation/update_transportation_merge.sql @@ -1,149 +1,871 @@ -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_merge_linestring CASCADE; -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_merge_linestring_gen3 CASCADE; -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_merge_linestring_gen4 CASCADE; -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_merge_linestring_gen5 CASCADE; -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_merge_linestring_gen6 CASCADE; -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_merge_linestring_gen7 CASCADE; - - -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_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_osm_transportation_merge_linestring_gen_z11 ON osm_transportation_merge_linestring_gen_z11; +DROP TRIGGER IF EXISTS trigger_store_transportation_highway_linestring_gen_z11 ON osm_highway_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; -- Instead of using relations to find out the road names we -- stitch together the touching ways with the same name -- to allow for nice label rendering -- Because this works well for roads that do not have relations as well +-- etldoc: osm_highway_linestring -> osm_transportation_name_network +-- etldoc: osm_route_member -> 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 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 osm_route_member rm1 ON rm1.member = hl.osm_id AND rm1.concurrency_index=1 + LEFT OUTER JOIN osm_route_member rm2 ON rm2.member = hl.osm_id AND rm2.concurrency_index=2 + LEFT OUTER JOIN osm_route_member rm3 ON rm3.member = hl.osm_id AND rm3.concurrency_index=3 + LEFT OUTER JOIN osm_route_member rm4 ON rm4.member = hl.osm_id AND rm4.concurrency_index=4 + LEFT OUTER JOIN osm_route_member rm5 ON rm5.member = hl.osm_id AND rm5.concurrency_index=5 + LEFT OUTER JOIN osm_route_member rm6 ON rm6.member = hl.osm_id AND rm6.concurrency_index=6 + WHERE (hl.name <> '' OR hl.ref <> '' OR rm1.ref <> '' OR rm1.network <> '') + AND hl.highway <> '' +) AS t; +CREATE UNIQUE 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(tags->'name', ''), coalesce(ref, '')); +CREATE INDEX IF NOT EXISTS osm_transportation_name_network_geometry_idx ON osm_transportation_name_network USING gist (geometry); --- Improve performance of the sql in transportation_name/network_type.sql -CREATE INDEX IF NOT EXISTS osm_highway_linestring_highway_idx - ON osm_highway_linestring(highway); - --- Improve performance of the sql below +-- Improve performance of the sql in transportation/update_route_member.sql CREATE INDEX IF NOT EXISTS osm_highway_linestring_highway_partial_idx - ON osm_highway_linestring(highway) - WHERE highway IN ('motorway','trunk', 'primary', 'construction'); + ON osm_highway_linestring (highway) + WHERE highway IN ('motorway', 'trunk'); - -- etldoc: osm_highway_linestring -> osm_transportation_merge_linestring -CREATE MATERIALIZED VIEW osm_transportation_merge_linestring AS ( - SELECT - (ST_Dump(geometry)).geom AS geometry, - NULL::bigint AS osm_id, - highway, construction, - z_order - FROM ( - SELECT - ST_LineMerge(ST_Collect(geometry)) AS geometry, - highway, construction, - 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 - ) AS highway_union + +-- 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, + id SERIAL PRIMARY KEY, + osm_id 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 INDEX IF NOT EXISTS osm_transportation_merge_linestring_geometry_idx - ON osm_transportation_merge_linestring USING gist(geometry); -CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_highway_partial_idx - ON osm_transportation_merge_linestring(highway, construction) - WHERE highway IN ('motorway','trunk', 'primary', 'construction'); - --- etldoc: osm_transportation_merge_linestring -> osm_transportation_merge_linestring_gen3 -CREATE MATERIALIZED VIEW osm_transportation_merge_linestring_gen3 AS ( - SELECT ST_Simplify(geometry, 120) AS geometry, osm_id, highway, construction, z_order - FROM osm_transportation_merge_linestring - WHERE highway IN ('motorway','trunk', 'primary') - OR highway = 'construction' AND construction IN ('motorway','trunk', 'primary') + +INSERT INTO osm_transportation_merge_linestring_gen_z11(geometry, osm_id, 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_Collect(geometry)))).geom AS geometry, + NULL::bigint AS osm_id, + highway, + network, + construction, + is_bridge, + is_tunnel, + is_ford, + expressway, + min(z_order) as z_order, + bicycle, + foot, + horse, + mtb_scale, + sac_scale, + CASE + WHEN access IN ('private', 'no') THEN 'no' + ELSE NULL::text END AS access, + toll, + layer +FROM osm_highway_linestring_gen_z11 +-- mapping.yaml pre-filter: motorway/trunk/primary/secondary/tertiary, with _link variants, construction, ST_IsValid() +GROUP BY highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, bicycle, foot, horse, mtb_scale, sac_scale, access, toll, layer +; +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z11_geometry_idx + ON osm_transportation_merge_linestring_gen_z11 USING gist (geometry); + + +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z10 + (LIKE osm_transportation_merge_linestring_gen_z11); + +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z9 + (LIKE osm_transportation_merge_linestring_gen_z10); + + +CREATE OR REPLACE FUNCTION insert_transportation_merge_linestring_gen_z10(update_id bigint) RETURNS void AS +$$ +BEGIN + DELETE FROM osm_transportation_merge_linestring_gen_z10 + WHERE update_id IS NULL OR id = update_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, + is_bridge, + is_tunnel, + is_ford, + expressway, + z_order, + bicycle, + foot, + horse, + mtb_scale, + sac_scale, + access, + toll, + layer + FROM osm_transportation_merge_linestring_gen_z11 + WHERE (update_id IS NULL OR id = update_id) + AND highway NOT IN ('tertiary', 'tertiary_link', 'busway', 'bus_guideway') + AND construction NOT IN ('tertiary', 'tertiary_link', 'busway', 'bus_guideway') + ; + + DELETE FROM osm_transportation_merge_linestring_gen_z9 + WHERE update_id IS NULL OR id = update_id; + + -- 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, + is_bridge, + is_tunnel, + is_ford, + expressway, + z_order, + bicycle, + foot, + horse, + mtb_scale, + sac_scale, + access, + toll, + layer + FROM osm_transportation_merge_linestring_gen_z10 + WHERE (update_id IS NULL OR id = update_id) + ; +END; +$$ LANGUAGE plpgsql; + +SELECT insert_transportation_merge_linestring_gen_z10(NULL); + +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z10_geometry_idx + ON osm_transportation_merge_linestring_gen_z10 USING gist (geometry); +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z10_id_idx + ON osm_transportation_merge_linestring_gen_z10(id); + +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z9_geometry_idx + ON osm_transportation_merge_linestring_gen_z9 USING gist (geometry); +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z9_id_idx + ON osm_transportation_merge_linestring_gen_z9(id); + + +-- etldoc: osm_transportation_merge_linestring_gen_z9 -> osm_transportation_merge_linestring_gen_z8 +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z8( + geometry geometry, + id SERIAL PRIMARY KEY, + osm_id bigint, + highway character varying, + network character varying, + construction character varying, + is_bridge boolean, + is_tunnel boolean, + is_ford boolean, + expressway boolean, + z_order integer ); -CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen3_geometry_idx - ON osm_transportation_merge_linestring_gen3 USING gist(geometry); -CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen3_highway_partial_idx - ON osm_transportation_merge_linestring_gen3(highway, construction) - WHERE highway IN ('motorway','trunk', 'primary', 'construction'); - --- etldoc: osm_transportation_merge_linestring_gen3 -> osm_transportation_merge_linestring_gen4 -CREATE MATERIALIZED VIEW osm_transportation_merge_linestring_gen4 AS ( - SELECT ST_Simplify(geometry, 200) AS geometry, osm_id, highway, construction, z_order - FROM osm_transportation_merge_linestring_gen3 - WHERE (highway IN ('motorway','trunk', 'primary') OR highway = 'construction' AND construction IN ('motorway','trunk', 'primary')) - AND ST_Length(geometry) > 50 + +INSERT INTO osm_transportation_merge_linestring_gen_z8(geometry, osm_id, highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, z_order) +SELECT ST_Simplify(ST_LineMerge(ST_Collect(geometry)), ZRes(10)) AS geometry, + NULL::bigint AS osm_id, + highway, + network, + construction, + is_bridge, + is_tunnel, + is_ford, + expressway, + min(z_order) as 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 +GROUP BY highway, network, construction, is_bridge, is_tunnel, is_ford, expressway +; +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z8_geometry_idx + ON osm_transportation_merge_linestring_gen_z8 USING gist (geometry); + +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z7 + (LIKE osm_transportation_merge_linestring_gen_z8); + +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z6 + (LIKE osm_transportation_merge_linestring_gen_z7); + +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z5 + (LIKE osm_transportation_merge_linestring_gen_z6); + +CREATE TABLE IF NOT EXISTS osm_transportation_merge_linestring_gen_z4 + (LIKE osm_transportation_merge_linestring_gen_z5); + + +CREATE OR REPLACE FUNCTION insert_transportation_merge_linestring_gen_z7(update_id bigint) RETURNS void AS +$$ +BEGIN + DELETE FROM osm_transportation_merge_linestring_gen_z7 + WHERE update_id IS NULL OR id = update_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, + is_bridge, + is_tunnel, + is_ford, + expressway, + z_order + FROM osm_transportation_merge_linestring_gen_z8 + -- Current view: motorway/trunk/primary + WHERE + (update_id IS NULL OR id = update_id) AND + ST_Length(geometry) > 50; + + DELETE FROM osm_transportation_merge_linestring_gen_z6 + WHERE update_id IS NULL OR id = update_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, + is_bridge, + is_tunnel, + is_ford, + expressway, + z_order + FROM osm_transportation_merge_linestring_gen_z7 + WHERE + (update_id IS NULL OR id = update_id) AND + (highway IN ('motorway', 'trunk') OR construction IN ('motorway', 'trunk')) AND + ST_Length(geometry) > 100; + + DELETE FROM osm_transportation_merge_linestring_gen_z5 + WHERE update_id IS NULL OR id = update_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, + is_bridge, + is_tunnel, + is_ford, + expressway, + z_order + FROM osm_transportation_merge_linestring_gen_z6 + WHERE + (update_id IS NULL OR id = update_id) AND + -- Current view: motorway/trunk + ST_Length(geometry) > 500; + + DELETE FROM osm_transportation_merge_linestring_gen_z4 + WHERE update_id IS NULL OR id = update_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, + is_bridge, + is_tunnel, + is_ford, + expressway, + z_order + FROM osm_transportation_merge_linestring_gen_z5 + WHERE + (update_id IS NULL OR id = update_id) AND + (highway = 'motorway' OR construction = 'motorway') AND + ST_Length(geometry) > 1000; +END; +$$ LANGUAGE plpgsql; + +SELECT insert_transportation_merge_linestring_gen_z7(NULL); + +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z7_geometry_idx + ON osm_transportation_merge_linestring_gen_z7 USING gist (geometry); +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z7_id_idx + ON osm_transportation_merge_linestring_gen_z7(id); + +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z6_geometry_idx + ON osm_transportation_merge_linestring_gen_z6 USING gist (geometry); +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z6_id_idx + ON osm_transportation_merge_linestring_gen_z6(id); + +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z5_geometry_idx + ON osm_transportation_merge_linestring_gen_z5 USING gist (geometry); +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z5_id_idx + ON osm_transportation_merge_linestring_gen_z5(id); + +CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z4_geometry_idx + ON osm_transportation_merge_linestring_gen_z4 USING gist (geometry); +CREATE UNIQUE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen_z4_id_idx + ON osm_transportation_merge_linestring_gen_z4(id); + + +-- Handle updates on +-- osm_highway_linestring_gen_z11 -> osm_transportation_merge_linestring_gen_z11 + +CREATE SCHEMA IF NOT EXISTS transportation; + +CREATE TABLE IF NOT EXISTS transportation.changes_z11 +( + id serial PRIMARY KEY, + is_old boolean, + geometry geometry, + osm_id 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 character varying, + toll boolean, + layer integer ); -CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen4_geometry_idx - ON osm_transportation_merge_linestring_gen4 USING gist(geometry); -CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen4_highway_partial_idx - ON osm_transportation_merge_linestring_gen4(highway, construction) - WHERE highway IN ('motorway','trunk', 'primary', 'construction'); - --- etldoc: osm_transportation_merge_linestring_gen4 -> osm_transportation_merge_linestring_gen5 -CREATE MATERIALIZED VIEW osm_transportation_merge_linestring_gen5 AS ( - SELECT ST_Simplify(geometry, 500) AS geometry, osm_id, highway, construction, z_order - FROM osm_transportation_merge_linestring_gen4 - WHERE (highway IN ('motorway','trunk') OR highway = 'construction' AND construction IN ('motorway','trunk')) - AND ST_Length(geometry) > 100 + +CREATE OR REPLACE FUNCTION transportation.store_z11() RETURNS trigger AS +$$ +BEGIN + IF (tg_op = 'DELETE' OR tg_op = 'UPDATE') THEN + INSERT INTO transportation.changes_z11(is_old, geometry, osm_id, highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, z_order, bicycle, foot, horse, mtb_scale, sac_scale, access, toll, layer) + VALUES (true, old.geometry, old.osm_id, old.highway, old.network, old.construction, old.is_bridge, old.is_tunnel, old.is_ford, old.expressway, old.z_order, old.bicycle, old.foot, old.horse, old.mtb_scale, old.sac_scale, + CASE + WHEN old.access IN ('private', 'no') THEN 'no' + ELSE NULL::text END, + old.toll, old.layer); + END IF; + IF (tg_op = 'UPDATE' OR tg_op = 'INSERT') THEN + INSERT INTO transportation.changes_z11(is_old, geometry, osm_id, highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, z_order, bicycle, foot, horse, mtb_scale, sac_scale, access, toll, layer) + VALUES (false, new.geometry, new.osm_id, new.highway, new.network, new.construction, new.is_bridge, new.is_tunnel, new.is_ford, new.expressway, new.z_order, new.bicycle, new.foot, new.horse, new.mtb_scale, new.sac_scale, + CASE + WHEN new.access IN ('private', 'no') THEN 'no' + ELSE NULL::text END, + new.toll, new.layer); + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS transportation.updates_z11 +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) ); -CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen5_geometry_idx - ON osm_transportation_merge_linestring_gen5 USING gist(geometry); -CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen5_highway_partial_idx - ON osm_transportation_merge_linestring_gen5(highway, construction) - WHERE highway IN ('motorway','trunk', 'construction'); - --- etldoc: osm_transportation_merge_linestring_gen5 -> osm_transportation_merge_linestring_gen6 -CREATE MATERIALIZED VIEW osm_transportation_merge_linestring_gen6 AS ( - SELECT ST_Simplify(geometry, 1000) AS geometry, osm_id, highway, construction, z_order - FROM osm_transportation_merge_linestring_gen5 - WHERE (highway IN ('motorway','trunk') OR highway = 'construction' AND construction IN ('motorway','trunk')) AND ST_Length(geometry) > 500 +CREATE OR REPLACE FUNCTION transportation.flag_z11() RETURNS trigger AS +$$ +BEGIN + INSERT INTO transportation.updates_z11(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION transportation.refresh_z11() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh transportation z11'; + + -- Compact the change history to keep only the first and last version + CREATE TEMP TABLE changes_compact AS + SELECT + * + FROM (( + SELECT DISTINCT ON (osm_id) * + FROM transportation.changes_z11 + WHERE is_old + ORDER BY osm_id, + id ASC + ) UNION ALL ( + SELECT DISTINCT ON (osm_id) * + FROM transportation.changes_z11 + WHERE NOT is_old + ORDER BY osm_id, + id DESC + )) AS t; + + -- Collect all original existing ways from impacted mmerge + CREATE TEMP TABLE osm_highway_linestring_original AS + SELECT DISTINCT ON (h.osm_id) + NULL::integer AS id, + NULL::boolean AS is_old, + h.geometry, + h.osm_id, + h.highway, + h.network, + h.construction, + h.is_bridge, + h.is_tunnel, + h.is_ford, + h.expressway, + h.z_order, + h.bicycle, + h.foot, + h.horse, + h.mtb_scale, + h.sac_scale, + h.access, + h.toll, + h.layer + FROM + changes_compact AS c + JOIN osm_transportation_merge_linestring_gen_z11 AS m ON + m.geometry && c.geometry + AND m.highway IS NOT DISTINCT FROM c.highway + AND m.network IS NOT DISTINCT FROM c.network + AND m.construction IS NOT DISTINCT FROM c.construction + AND m.is_bridge IS NOT DISTINCT FROM c.is_bridge + AND m.is_tunnel IS NOT DISTINCT FROM c.is_tunnel + AND m.is_ford IS NOT DISTINCT FROM c.is_ford + AND m.expressway IS NOT DISTINCT FROM c.expressway + AND m.bicycle IS NOT DISTINCT FROM c.bicycle + AND m.foot IS NOT DISTINCT FROM c.foot + AND m.horse IS NOT DISTINCT FROM c.horse + AND m.mtb_scale IS NOT DISTINCT FROM c.mtb_scale + AND m.sac_scale IS NOT DISTINCT FROM c.sac_scale + AND m.access IS NOT DISTINCT FROM c.access + AND m.toll IS NOT DISTINCT FROM c.toll + AND m.layer IS NOT DISTINCT FROM c.layer + JOIN osm_highway_linestring_gen_z11 AS h ON + h.geometry && c.geometry + AND h.osm_id NOT IN (SELECT osm_id FROM changes_compact) + AND ST_Contains(m.geometry, h.geometry) + AND h.highway IS NOT DISTINCT FROM m.highway + AND h.network IS NOT DISTINCT FROM m.network + AND h.construction IS NOT DISTINCT FROM m.construction + AND h.is_bridge IS NOT DISTINCT FROM m.is_bridge + AND h.is_tunnel IS NOT DISTINCT FROM m.is_tunnel + AND h.is_ford IS NOT DISTINCT FROM m.is_ford + AND h.expressway IS NOT DISTINCT FROM m.expressway + AND h.bicycle IS NOT DISTINCT FROM m.bicycle + AND h.foot IS NOT DISTINCT FROM m.foot + AND h.horse IS NOT DISTINCT FROM m.horse + AND h.mtb_scale IS NOT DISTINCT FROM m.mtb_scale + AND h.sac_scale IS NOT DISTINCT FROM m.sac_scale + AND CASE + WHEN h.access IN ('private', 'no') THEN 'no' + ELSE NULL::text END IS NOT DISTINCT FROM m.access + AND h.toll IS NOT DISTINCT FROM m.toll + AND h.layer IS NOT DISTINCT FROM m.layer + ORDER BY + h.osm_id + ; + + DELETE + FROM osm_transportation_merge_linestring_gen_z11 AS m + USING changes_compact AS c + WHERE + m.geometry && c.geometry + AND m.highway IS NOT DISTINCT FROM c.highway + AND m.network IS NOT DISTINCT FROM c.network + AND m.construction IS NOT DISTINCT FROM c.construction + AND m.is_bridge IS NOT DISTINCT FROM c.is_bridge + AND m.is_tunnel IS NOT DISTINCT FROM c.is_tunnel + AND m.is_ford IS NOT DISTINCT FROM c.is_ford + AND m.expressway IS NOT DISTINCT FROM c.expressway + AND m.bicycle IS NOT DISTINCT FROM c.bicycle + AND m.foot IS NOT DISTINCT FROM c.foot + AND m.horse IS NOT DISTINCT FROM c.horse + AND m.mtb_scale IS NOT DISTINCT FROM c.mtb_scale + AND m.sac_scale IS NOT DISTINCT FROM c.sac_scale + AND m.access IS NOT DISTINCT FROM c.access + AND m.toll IS NOT DISTINCT FROM c.toll + AND m.layer IS NOT DISTINCT FROM c.layer + ; + + INSERT INTO osm_transportation_merge_linestring_gen_z11(geometry, osm_id, 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_Collect(geometry)))).geom AS geometry, + NULL::bigint AS osm_id, + highway, + network, + construction, + is_bridge, + is_tunnel, + is_ford, + expressway, + min(z_order) as z_order, + bicycle, + foot, + horse, + mtb_scale, + sac_scale, + CASE + WHEN access IN ('private', 'no') THEN 'no' + ELSE NULL::text END AS access, + toll, + layer + FROM (( + SELECT * FROM osm_highway_linestring_original + ) UNION ALL ( + -- New or updated ways + SELECT + * + FROM + changes_compact + WHERE + NOT is_old + )) AS t + GROUP BY highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, bicycle, foot, horse, mtb_scale, sac_scale, access, toll, layer + ; + + DROP TABLE osm_highway_linestring_original; + DROP TABLE changes_compact; + -- 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); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER trigger_store_transportation_highway_linestring_gen_z11 + AFTER INSERT OR UPDATE OR DELETE + ON osm_highway_linestring_gen_z11 + FOR EACH ROW +EXECUTE PROCEDURE transportation.store_z11(); + +CREATE TRIGGER trigger_flag_transportation_z11 + AFTER INSERT OR UPDATE OR DELETE + ON osm_highway_linestring_gen_z11 + FOR EACH STATEMENT +EXECUTE PROCEDURE transportation.flag_z11(); + +CREATE CONSTRAINT TRIGGER trigger_refresh_z11 + AFTER INSERT + ON transportation.updates_z11 + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE transportation.refresh_z11(); + + +-- Handle updates on +-- osm_transportation_merge_linestring_gen_z11 -> osm_transportation_merge_linestring_gen_z10 +-- osm_transportation_merge_linestring_gen_z11 -> osm_transportation_merge_linestring_gen_z9 + + +CREATE OR REPLACE FUNCTION transportation.merge_linestring_gen_refresh_z10() RETURNS trigger AS +$$ +BEGIN + IF (tg_op = 'DELETE') THEN + DELETE FROM osm_transportation_merge_linestring_gen_z10 WHERE id = old.id; + DELETE FROM osm_transportation_merge_linestring_gen_z9 WHERE id = old.id; + END IF; + + IF (tg_op = 'UPDATE' OR tg_op = 'INSERT') THEN + PERFORM insert_transportation_merge_linestring_gen_z10(new.id); + END IF; + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER trigger_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.merge_linestring_gen_refresh_z10(); + + +-- Handle updates on +-- osm_transportation_merge_linestring_gen_z9 -> osm_transportation_merge_linestring_gen_z8 + + +CREATE TABLE IF NOT EXISTS transportation.changes_z9 +( + is_old boolean, + geometry geometry, + id bigint, + highway character varying, + network character varying, + construction character varying, + is_bridge boolean, + is_tunnel boolean, + is_ford boolean, + expressway boolean, + z_order integer ); -CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen6_geometry_idx - ON osm_transportation_merge_linestring_gen6 USING gist(geometry); -CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen6_highway_partial_idx - ON osm_transportation_merge_linestring_gen6(highway, construction) - WHERE highway IN ('motorway','trunk', 'construction'); - --- etldoc: osm_transportation_merge_linestring_gen6 -> osm_transportation_merge_linestring_gen7 -CREATE MATERIALIZED VIEW osm_transportation_merge_linestring_gen7 AS ( - SELECT ST_Simplify(geometry, 2000) AS geometry, osm_id, highway, construction, z_order - FROM osm_transportation_merge_linestring_gen6 - WHERE (highway = 'motorway' OR highway = 'construction' AND construction = 'motorway') AND ST_Length(geometry) > 1000 + +CREATE OR REPLACE FUNCTION transportation.store_z9() RETURNS trigger AS +$$ +BEGIN + IF (tg_op = 'DELETE' OR tg_op = 'UPDATE') THEN + INSERT INTO transportation.changes_z9(is_old, geometry, id, highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, z_order) + VALUES (true, old.geometry, old.id, old.highway, old.network, old.construction, old.is_bridge, old.is_tunnel, old.is_ford, old.expressway, old.z_order); + END IF; + IF (tg_op = 'UPDATE' OR tg_op = 'INSERT') THEN + INSERT INTO transportation.changes_z9(is_old, geometry, id, highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, z_order) + VALUES (false, new.geometry, new.id, new.highway, new.network, new.construction, new.is_bridge, new.is_tunnel, new.is_ford, new.expressway, new.z_order); + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS transportation.updates_z9 +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) ); -CREATE INDEX IF NOT EXISTS osm_transportation_merge_linestring_gen7_geometry_idx - ON osm_transportation_merge_linestring_gen7 USING gist(geometry); +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 z9'; --- Handle updates + -- Compact the change history to keep only the first and last version + CREATE TEMP TABLE changes_compact AS + SELECT + * + FROM (( + SELECT DISTINCT ON (id) * + FROM transportation.changes_z9 + WHERE is_old + ORDER BY id, + id ASC + ) UNION ALL ( + SELECT DISTINCT ON (id) * + FROM transportation.changes_z9 + WHERE NOT is_old + ORDER BY id, + id DESC + )) AS t; -CREATE SCHEMA IF NOT EXISTS transportation; + -- Collect all original existing ways from impacted mmerge + CREATE TEMP TABLE osm_highway_linestring_original AS + SELECT DISTINCT ON (h.id) + NULL::boolean AS is_old, + h.geometry, + h.id, + h.highway, + h.network, + h.construction, + h.is_bridge, + h.is_tunnel, + h.is_ford, + h.expressway, + h.z_order + FROM + changes_compact AS c + JOIN osm_transportation_merge_linestring_gen_z8 AS m ON + m.geometry && c.geometry + AND m.highway IS NOT DISTINCT FROM c.highway + AND m.network IS NOT DISTINCT FROM c.network + AND m.construction IS NOT DISTINCT FROM c.construction + AND m.is_bridge IS NOT DISTINCT FROM c.is_bridge + AND m.is_tunnel IS NOT DISTINCT FROM c.is_tunnel + AND m.is_ford IS NOT DISTINCT FROM c.is_ford + AND m.expressway IS NOT DISTINCT FROM c.expressway + JOIN osm_transportation_merge_linestring_gen_z9 AS h ON + h.geometry && c.geometry + AND h.id NOT IN (SELECT id FROM changes_compact) + AND ST_Contains(m.geometry, h.geometry) + AND h.highway IS NOT DISTINCT FROM m.highway + AND h.network IS NOT DISTINCT FROM m.network + AND h.construction IS NOT DISTINCT FROM m.construction + AND h.is_bridge IS NOT DISTINCT FROM m.is_bridge + AND h.is_tunnel IS NOT DISTINCT FROM m.is_tunnel + AND h.is_ford IS NOT DISTINCT FROM m.is_ford + AND h.expressway IS NOT DISTINCT FROM m.expressway + ORDER BY + h.id + ; -CREATE TABLE IF NOT EXISTS transportation.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION transportation.flag() RETURNS trigger AS $$ -BEGIN - INSERT INTO transportation.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + DELETE + FROM osm_transportation_merge_linestring_gen_z8 AS m + USING changes_compact AS c + WHERE + m.geometry && c.geometry + AND m.highway IS NOT DISTINCT FROM c.highway + AND m.network IS NOT DISTINCT FROM c.network + AND m.construction IS NOT DISTINCT FROM c.construction + AND m.is_bridge IS NOT DISTINCT FROM c.is_bridge + AND m.is_tunnel IS NOT DISTINCT FROM c.is_tunnel + AND m.is_ford IS NOT DISTINCT FROM c.is_ford + AND m.expressway IS NOT DISTINCT FROM c.expressway + ; + + INSERT INTO osm_transportation_merge_linestring_gen_z8(geometry, osm_id, highway, network, construction, is_bridge, is_tunnel, is_ford, expressway, z_order) + SELECT (ST_Dump(ST_LineMerge(ST_Collect(geometry)))).geom AS geometry, + NULL::bigint AS osm_id, + highway, + network, + construction, + is_bridge, + is_tunnel, + is_ford, + expressway, + min(z_order) as z_order + FROM (( + SELECT * FROM osm_highway_linestring_original + ) UNION ALL ( + -- New or updated ways + SELECT + * + FROM + changes_compact + WHERE + NOT is_old + )) AS t + GROUP BY highway, network, construction, is_bridge, is_tunnel, is_ford, expressway + ; + + DROP TABLE osm_highway_linestring_original; + DROP TABLE changes_compact; + -- noinspection SqlWithoutWhere + DELETE FROM transportation.changes_z9; + -- noinspection SqlWithoutWhere + DELETE FROM transportation.updates_z9; + + RAISE LOG 'Refresh transportation z9 done in %', age(clock_timestamp(), t); + RETURN NULL; END; -$$ language plpgsql; - -CREATE OR REPLACE FUNCTION transportation.refresh() RETURNS trigger AS - $BODY$ - BEGIN - RAISE NOTICE 'Refresh transportation'; - REFRESH MATERIALIZED VIEW osm_transportation_merge_linestring; - REFRESH MATERIALIZED VIEW osm_transportation_merge_linestring_gen3; - REFRESH MATERIALIZED VIEW osm_transportation_merge_linestring_gen4; - REFRESH MATERIALIZED VIEW osm_transportation_merge_linestring_gen5; - REFRESH MATERIALIZED VIEW osm_transportation_merge_linestring_gen6; - REFRESH MATERIALIZED VIEW osm_transportation_merge_linestring_gen7; - DELETE FROM transportation.updates; - RETURN null; - END; - $BODY$ -language plpgsql; - -CREATE TRIGGER trigger_flag_transportation - AFTER INSERT OR UPDATE OR DELETE ON osm_highway_linestring +$$ 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_flag_transportation_z9 + AFTER INSERT OR UPDATE OR DELETE + ON osm_transportation_merge_linestring_gen_z9 FOR EACH STATEMENT - EXECUTE PROCEDURE transportation.flag(); +EXECUTE PROCEDURE transportation.flag_z9(); -CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON transportation.updates +CREATE CONSTRAINT TRIGGER trigger_refresh_z8 + AFTER INSERT + ON transportation.updates_z9 INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE transportation.refresh(); +EXECUTE PROCEDURE transportation.refresh_z8(); + + +-- Handle updates on +-- 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 OR REPLACE FUNCTION transportation.merge_linestring_gen_refresh_z7() RETURNS trigger AS +$$ +BEGIN + IF (tg_op = 'DELETE') THEN + DELETE FROM osm_transportation_merge_linestring_gen_z7 WHERE id = old.id; + DELETE FROM osm_transportation_merge_linestring_gen_z6 WHERE id = old.id; + DELETE FROM osm_transportation_merge_linestring_gen_z5 WHERE id = old.id; + DELETE FROM osm_transportation_merge_linestring_gen_z4 WHERE id = old.id; + END IF; + + IF (tg_op = 'UPDATE' OR tg_op = 'INSERT') THEN + PERFORM insert_transportation_merge_linestring_gen_z7(new.id); + END IF; + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_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.merge_linestring_gen_refresh_z7(); diff --git a/layers/transportation_name/etl_diagram.png b/layers/transportation_name/etl_diagram.png index cd90e7648..d46ad20c5 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 000000000..b9f8e07ef --- /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/layer.sql b/layers/transportation_name/layer.sql deleted file mode 100644 index 3061cb7fc..000000000 --- a/layers/transportation_name/layer.sql +++ /dev/null @@ -1,142 +0,0 @@ - --- etldoc: layer_transportation_name[shape=record fillcolor=lightpink, style="rounded,filled", --- etldoc: label="layer_transportation_name | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; - -CREATE OR REPLACE FUNCTION layer_transportation_name(bbox geometry, zoom_level integer) -RETURNS TABLE(osm_id bigint, geometry geometry, name text, name_en text, - name_de text, tags hstore, ref text, ref_length int, network text, class - text, subclass text, layer INT, level INT, indoor INT) AS $$ - SELECT osm_id, geometry, - NULLIF(name, '') AS name, - COALESCE(NULLIF(name_en, ''), name) AS name_en, - COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, - tags, - NULLIF(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, - CASE - WHEN highway IS NOT NULL AND highway_class(highway, '', construction) = 'path' - THEN highway - ELSE NULL - END AS subclass, - NULLIF(layer, 0) AS layer, - "level", - CASE WHEN indoor=TRUE THEN 1 ELSE NULL END as indoor - FROM ( - - -- etldoc: osm_transportation_name_linestring_gen4 -> layer_transportation_name:z6 - SELECT *, - NULL::int AS layer, NULL::int AS level, NULL::boolean AS indoor - FROM osm_transportation_name_linestring_gen4 - WHERE zoom_level = 6 - UNION ALL - - -- etldoc: osm_transportation_name_linestring_gen3 -> layer_transportation_name:z7 - SELECT *, - NULL::int AS layer, NULL::int AS level, NULL::boolean AS indoor - FROM osm_transportation_name_linestring_gen3 - WHERE zoom_level = 7 - UNION ALL - - -- etldoc: osm_transportation_name_linestring_gen2 -> layer_transportation_name:z8 - SELECT *, - NULL::int AS layer, NULL::int AS level, NULL::boolean AS indoor - FROM osm_transportation_name_linestring_gen2 - WHERE zoom_level = 8 - UNION ALL - - -- etldoc: osm_transportation_name_linestring_gen1 -> layer_transportation_name:z9 - -- etldoc: osm_transportation_name_linestring_gen1 -> layer_transportation_name:z10 - -- etldoc: osm_transportation_name_linestring_gen1 -> layer_transportation_name:z11 - SELECT *, - NULL::int AS layer, NULL::int AS level, NULL::boolean AS indoor - FROM osm_transportation_name_linestring_gen1 - WHERE zoom_level BETWEEN 9 AND 11 - UNION ALL - - -- etldoc: osm_transportation_name_linestring -> layer_transportation_name:z12 - SELECT - geometry, - osm_id, - name, - name_en, - name_de, - "tags", - ref, - highway, - construction, - network, - z_order, - layer, - "level", - indoor - FROM osm_transportation_name_linestring - WHERE zoom_level = 12 - AND LineLabel(zoom_level, COALESCE(NULLIF(name, ''), ref), geometry) - AND highway_class(highway, '', construction) NOT IN ('minor', 'track', 'path') - AND NOT highway_is_link(highway) - UNION ALL - - -- etldoc: osm_transportation_name_linestring -> layer_transportation_name:z13 - SELECT - geometry, - osm_id, - name, - name_en, - name_de, - "tags", - ref, - highway, - construction, - network, - z_order, - layer, - "level", - indoor - FROM osm_transportation_name_linestring - WHERE zoom_level = 13 - AND LineLabel(zoom_level, COALESCE(NULLIF(name, ''), ref), geometry) - AND highway_class(highway, '', construction) NOT IN ('track', 'path') - UNION ALL - - -- etldoc: osm_transportation_name_linestring -> layer_transportation_name:z14_ - SELECT - geometry, - osm_id, - name, - name_en, - name_de, - "tags", - ref, - highway, - construction, - network, - z_order, - layer, - "level", - indoor - FROM osm_transportation_name_linestring - WHERE zoom_level >= 14 - - ) AS zoom_levels - WHERE geometry && bbox - ORDER BY z_order ASC; -$$ LANGUAGE SQL IMMUTABLE; - - -CREATE OR REPLACE FUNCTION layer_transportation_name_lite(bbox geometry, zoom_level integer) -RETURNS TABLE(osm_id bigint, geometry geometry, name text, name_en text, - name_de text, tags hstore, ref text, ref_length int, network text, class - text, subclass text, layer INT, level INT, indoor INT) AS $$ - SELECT * FROM layer_transportation_name(bbox, zoom_level) - WHERE zoom_level < 13 - UNION ALL - SELECT * FROM layer_transportation_name(bbox, 14) - WHERE zoom_level >= 13 -$$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/transportation_name/mapping_diagram.png b/layers/transportation_name/mapping_diagram.png index eacdce726..c47f739ba 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/network_type.sql b/layers/transportation_name/network_type.sql deleted file mode 100644 index 325099182..000000000 --- a/layers/transportation_name/network_type.sql +++ /dev/null @@ -1,30 +0,0 @@ -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_name_network CASCADE; -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_name_linestring CASCADE; -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_name_linestring_gen1 CASCADE; -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_name_linestring_gen2 CASCADE; -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_name_linestring_gen3 CASCADE; -DROP MATERIALIZED VIEW IF EXISTS osm_transportation_name_linestring_gen4 CASCADE; - -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'route_network_type') THEN - CREATE TYPE route_network_type AS ENUM ( - 'us-interstate', 'us-highway', 'us-state', - 'ca-transcanada', - 'gb-motorway', 'gb-trunk' - ); - END IF; -END -$$ -; - -DO $$ - 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; -$$ -; diff --git a/layers/transportation_name/style.json b/layers/transportation_name/style.json new file mode 100644 index 000000000..e280f680a --- /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": 175 + }, + { + "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": 176 + }, + { + "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": 177 + }, + { + "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": 178 + }, + { + "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": 179 + }, + { + "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": 180 + } + ] +} \ No newline at end of file diff --git a/layers/transportation_name/transportation_name.sql b/layers/transportation_name/transportation_name.sql new file mode 100644 index 000000000..60d0fd8bd --- /dev/null +++ b/layers/transportation_name/transportation_name.sql @@ -0,0 +1,193 @@ +-- etldoc: layer_transportation_name[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_transportation_name | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; + +CREATE OR REPLACE FUNCTION layer_transportation_name(bbox geometry, zoom_level integer) + RETURNS TABLE + ( + geometry geometry, + name text, + name_en text, + name_de text, + tags hstore, + ref text, + ref_length int, + network text, + route_1 text, + route_2 text, + route_3 text, + route_4 text, + route_5 text, + route_6 text, + class text, + subclass text, + brunnel text, + layer int, + level int, + indoor int + ) +AS +$$ +SELECT geometry, + tags->'name' AS name, + COALESCE(tags->'name:en', tags->'name') AS name_en, + COALESCE(tags->'name:de', tags->'name', tags->'name:en') AS name_de, + tags, + ref, + NULLIF(LENGTH(ref), 0) AS ref_length, + CASE + WHEN network IS NOT NULL + THEN network::text + WHEN length(coalesce(ref, '')) > 0 + THEN 'road' + END AS network, + route_1, 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, '', subclass) = 'path' + THEN highway + ELSE subclass + END AS subclass, + brunnel, + NULLIF(layer, 0) AS layer, + "level", + CASE WHEN indoor = TRUE THEN 1 END AS indoor +FROM ( + + -- etldoc: osm_transportation_name_linestring_gen4 -> layer_transportation_name:z6 + SELECT *, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor + FROM osm_transportation_name_linestring_gen4 + WHERE zoom_level = 6 + UNION ALL + + -- etldoc: osm_transportation_name_linestring_gen3 -> layer_transportation_name:z7 + SELECT *, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor + FROM osm_transportation_name_linestring_gen3 + WHERE zoom_level = 7 + UNION ALL + + -- etldoc: osm_transportation_name_linestring_gen2 -> layer_transportation_name:z8 + SELECT *, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor + FROM osm_transportation_name_linestring_gen2 + WHERE zoom_level = 8 + UNION ALL + + -- etldoc: osm_transportation_name_linestring_gen1 -> layer_transportation_name:z9 + -- etldoc: osm_transportation_name_linestring_gen1 -> layer_transportation_name:z10 + -- etldoc: osm_transportation_name_linestring_gen1 -> layer_transportation_name:z11 + SELECT *, + NULL::int AS layer, + NULL::int AS level, + NULL::boolean AS indoor + FROM osm_transportation_name_linestring_gen1 + WHERE zoom_level BETWEEN 9 AND 11 + UNION ALL + + -- etldoc: osm_transportation_name_linestring -> layer_transportation_name:z12 + SELECT geometry, + "tags", + ref, + highway, + subclass, + brunnel, + network, + route_1, route_2, route_3, route_4, route_5, route_6, + z_order, + layer, + "level", + indoor + FROM osm_transportation_name_linestring + WHERE zoom_level = 12 + AND LineLabel(zoom_level, COALESCE(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, + "tags", + ref, + highway, + subclass, + brunnel, + network, + route_1, route_2, route_3, route_4, route_5, route_6, + z_order, + layer, + "level", + indoor + FROM osm_transportation_name_linestring + WHERE zoom_level = 13 + AND LineLabel(zoom_level, COALESCE(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, + "tags", + ref, + highway, + subclass, + brunnel, + network, + route_1, route_2, route_3, route_4, route_5, route_6, + z_order, + layer, + "level", + indoor + FROM osm_transportation_name_linestring + WHERE zoom_level >= 14 + UNION ALL + + -- etldoc: osm_highway_point -> layer_transportation_name:z10 + SELECT + p.geometry, + p.tags, + p.ref, + ( + SELECT highest_highway(l.tags->'highway') + FROM osm_highway_linestring l + WHERE ST_Intersects(p.geometry,l.geometry) + ) AS class, + 'junction'::text AS subclass, + NULL AS brunnel, + NULL AS network, + NULL::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; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/transportation_name/transportation_name.yaml b/layers/transportation_name/transportation_name.yaml index 746ec502c..790fc421b 100644 --- a/layers/transportation_name/transportation_name.yaml +++ b/layers/transportation_name/transportation_name.yaml @@ -1,5 +1,9 @@ layer: id: "transportation_name" + # transportation_name relies on the function highway_class() defined in transportation layer + requires: + layers: + - transportation description: | This is the layer for labelling the highways. Only highways that are named `name=*` and are long enough to place text upon appear. The OSM roads are stitched together if they contain the same name @@ -53,11 +57,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 @@ -67,6 +73,14 @@ layer: - bridleway - corridor - platform + - junction + brunnel: + description: | + Mark whether way is a bridge, a tunnel or a ford. + values: + - bridge + - tunnel + - ford level: description: | Experimental feature! Filled only for steps and footways. Original @@ -81,15 +95,20 @@ 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, 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 - - ./layer.sql + - ./transportation_name.sql datasources: - type: imposm3 mapping_file: ../transportation/mapping.yaml diff --git a/layers/transportation_name/update_route_member.sql b/layers/transportation_name/update_route_member.sql deleted file mode 100644 index 60716a3b5..000000000 --- a/layers/transportation_name/update_route_member.sql +++ /dev/null @@ -1,73 +0,0 @@ -DROP TRIGGER IF EXISTS trigger_flag_transportation_name ON osm_route_member; - - --- create GBR relations (so we can use it in the same way as other relations) -CREATE OR REPLACE FUNCTION update_gbr_route_members() RETURNS VOID AS $$ -DECLARE gbr_geom geometry; -BEGIN - select st_buffer(geometry, 10000) into gbr_geom from ne_10m_admin_0_countries where iso_a2 = 'GB'; - delete from osm_route_member where network IN('omt-gb-motorway', 'omt-gb-trunk'); - - insert into osm_route_member (osm_id, member, ref, network) - ( - SELECT 0, hw.osm_id, substring(hw.ref from E'^[AM][0-9AM()]+'), 'omt-gb-motorway' - from osm_highway_linestring hw - where length(hw.ref)>0 and ST_Intersects(hw.geometry, gbr_geom) - and hw.highway IN ('motorway') - ) UNION ( - SELECT 0, hw.osm_id, substring(hw.ref from E'^[AM][0-9AM()]+'), 'omt-gb-trunk' - from osm_highway_linestring hw - where length(hw.ref)>0 and ST_Intersects(hw.geometry, gbr_geom) - and hw.highway IN ('trunk') - ) - ; -END; -$$ LANGUAGE plpgsql; - - --- etldoc: osm_route_member -> osm_route_member -CREATE OR REPLACE FUNCTION update_osm_route_member() RETURNS VOID AS $$ -BEGIN - PERFORM update_gbr_route_members(); - - -- see http://wiki.openstreetmap.org/wiki/Relation:route#Road_routes - UPDATE osm_route_member - SET network_type = - 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' AND ref IN ('16')) OR - (network = 'CA:ON:primary' AND ref IN ('17', '417')) OR - (name = 'Route Transcanadienne (QC)') OR - (network = 'CA:NB' AND ref IN ('2', '16')) OR - (network = 'CA:PEI' 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 (Super)') - 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 - ELSE NULL - END - ; - -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"); - -SELECT update_osm_route_member(); - -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 cb7dcf853..d04d14954 100644 --- a/layers/transportation_name/update_transportation_name.sql +++ b/layers/transportation_name/update_transportation_name.sql @@ -1,183 +1,716 @@ -DROP TRIGGER IF EXISTS trigger_flag_transportation_name ON osm_highway_linestring; -DROP TRIGGER IF EXISTS trigger_refresh ON transportation_name.updates; - -- Instead of using relations to find out the road names we -- stitch together the touching ways with the same name -- to allow for nice label rendering -- Because this works well for roads that do not have relations as well +-- etldoc: osm_transportation_name_network -> osm_transportation_name_linestring +-- etldoc: osm_shipway_linestring -> osm_transportation_name_linestring +-- etldoc: osm_aerialway_linestring -> osm_transportation_name_linestring +CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring AS +SELECT (ST_Dump(geometry)).geom AS geometry, + tags || get_basic_names(tags, geometry) AS tags, + ref, + highway, + subclass, + brunnel, + sac_scale, + "level", + layer, + indoor, + network_type AS network, + route_1, route_2, route_3, route_4, route_5, route_6, + z_order, + route_rank +FROM ( + SELECT ST_LineMerge(ST_Collect(geometry)) AS geometry, + tags, + ref, + highway, + subclass, + CASE WHEN COUNT(*) = COUNT(brunnel) AND MAX(brunnel) = MIN(brunnel) + THEN MAX(brunnel) + ELSE NULL::text END AS 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 osm_transportation_name_network + WHERE tags->'name' <> '' OR ref <> '' + GROUP BY tags, ref, highway, subclass, "level", layer, sac_scale, indoor, network_type, + route_1, route_2, route_3, route_4, route_5, route_6 + UNION ALL --- etldoc: osm_highway_linestring -> osm_transportation_name_network --- etldoc: osm_route_member -> osm_transportation_name_network -CREATE MATERIALIZED VIEW osm_transportation_name_network AS ( - SELECT - hl.geometry, - hl.osm_id, - CASE WHEN length(hl.name)>15 THEN osml10n_street_abbrev_all(hl.name) ELSE hl.name END AS "name", - CASE WHEN length(hl.name_en)>15 THEN osml10n_street_abbrev_en(hl.name_en) ELSE hl.name_en END AS "name_en", - CASE WHEN length(hl.name_de)>15 THEN osml10n_street_abbrev_de(hl.name_de) ELSE hl.name_de END AS "name_de", - hl.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 hl.ref - end as ref, - hl.highway, - hl.construction, - CASE WHEN highway IN ('footway', 'steps') THEN layer - ELSE NULL::int - END AS layer, - CASE WHEN highway IN ('footway', 'steps') THEN "level" - ELSE NULL::int - END AS "level", - CASE WHEN highway IN ('footway', 'steps') THEN indoor - ELSE NULL::boolean - 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 - -- NOTE: As Qwant uses `use_single_id_space` parameter in imposm, `osm_id` - -- columns are modified, but not the column `member`. For a way, the - -- transformation only multiplies the id by -1, as defined here: - -- https://github.com/omniscale/imposm3/blob/0ca82bbe/writer/ways.go#L60 - left join osm_route_member rm on (rm.member = -hl.osm_id) -); -CREATE INDEX IF NOT EXISTS osm_transportation_name_network_geometry_idx ON osm_transportation_name_network USING gist(geometry); - + SELECT ST_LineMerge(ST_Collect(geometry)) AS geometry, + 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 osm_shipway_linestring + WHERE name <> '' + GROUP BY name, name_en, name_de, tags, subclass, "level", layer + UNION ALL --- etldoc: osm_transportation_name_network -> osm_transportation_name_linestring -CREATE MATERIALIZED VIEW 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", - ref, - highway, - construction, - "level", - layer, - indoor, - network_type AS network, - z_order - FROM ( - SELECT - ST_LineMerge(ST_Collect(geometry)) AS geometry, - name, - name_en, - name_de, - hstore(string_agg(nullif(slice_language_tags(tags || hstore(ARRAY['name', name, 'name:en', name_en, 'name:de', name_de]))::text, ''), ',')) - AS "tags", - ref, - highway, - construction, - "level", - layer, - indoor, - network_type, - min(z_order) AS z_order - FROM osm_transportation_name_network - WHERE ("rank"=1 OR "rank" is null) - AND (name <> '' OR ref <> '') - AND NULLIF(highway, '') IS NOT NULL - group by name, name_en, name_de, ref, highway, construction, "level", layer, indoor, network_type - ) AS highway_union -); -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_geometry_idx ON osm_transportation_name_linestring USING gist(geometry); + SELECT ST_LineMerge(ST_Collect(geometry)) AS geometry, + 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 osm_aerialway_linestring + WHERE name <> '' + GROUP BY name, name_en, name_de, tags, subclass, "level", layer + ) AS highway_union +; +CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_name_ref_idx ON osm_transportation_name_linestring (coalesce(tags->'name', ''), coalesce(ref, '')); +CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_geometry_idx ON osm_transportation_name_linestring USING gist (geometry); 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'); + ON osm_transportation_name_linestring (highway, subclass) + WHERE highway IN ('motorway', 'trunk', 'construction'); -- etldoc: osm_transportation_name_linestring -> osm_transportation_name_linestring_gen1 -CREATE MATERIALIZED VIEW osm_transportation_name_linestring_gen1 AS ( - SELECT ST_Simplify(geometry, 50) AS geometry, osm_id, name, name_en, name_de, tags, ref, highway, construction, 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 INDEX IF NOT EXISTS osm_transportation_name_linestring_gen1_geometry_idx ON osm_transportation_name_linestring_gen1 USING gist(geometry); +CREATE OR REPLACE VIEW osm_transportation_name_linestring_gen1_view AS +SELECT 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 (highway IN ('motorway', 'trunk') OR highway = 'construction' AND subclass 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(tags->'name', ref))); +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_gen1_highway_partial_idx - ON osm_transportation_name_linestring_gen1(highway, construction) - WHERE highway IN ('motorway','trunk', 'construction'); + ON osm_transportation_name_linestring_gen1 (highway, subclass) + WHERE highway IN ('motorway', 'trunk', 'construction'); -- etldoc: osm_transportation_name_linestring_gen1 -> osm_transportation_name_linestring_gen2 -CREATE MATERIALIZED VIEW osm_transportation_name_linestring_gen2 AS ( - SELECT ST_Simplify(geometry, 120) AS geometry, osm_id, name, name_en, name_de, tags, ref, highway, construction, 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 INDEX IF NOT EXISTS osm_transportation_name_linestring_gen2_geometry_idx ON osm_transportation_name_linestring_gen2 USING gist(geometry); +CREATE OR REPLACE VIEW osm_transportation_name_linestring_gen2_view AS +SELECT 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 (highway IN ('motorway', 'trunk') OR highway = 'construction' AND subclass 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(tags->'name', ref))); +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_gen2_highway_partial_idx - ON osm_transportation_name_linestring_gen2(highway, construction) - WHERE highway IN ('motorway','trunk', 'construction'); + ON osm_transportation_name_linestring_gen2 (highway, subclass) + WHERE highway IN ('motorway', 'trunk', 'construction'); -- etldoc: osm_transportation_name_linestring_gen2 -> osm_transportation_name_linestring_gen3 -CREATE MATERIALIZED VIEW osm_transportation_name_linestring_gen3 AS ( - SELECT ST_Simplify(geometry, 200) AS geometry, osm_id, name, name_en, name_de, tags, ref, highway, construction, network, z_order - FROM osm_transportation_name_linestring_gen2 - WHERE (highway = 'motorway' OR highway = 'construction' AND construction = 'motorway') AND ST_Length(geometry) > 20000 -); -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen3_geometry_idx ON osm_transportation_name_linestring_gen3 USING gist(geometry); +CREATE OR REPLACE VIEW osm_transportation_name_linestring_gen3_view AS +SELECT 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 (highway = 'motorway' OR highway = 'construction' AND subclass = '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(tags->'name', ref))); +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_gen3_highway_partial_idx - ON osm_transportation_name_linestring_gen3(highway, construction) - WHERE highway IN ('motorway', 'construction'); + ON osm_transportation_name_linestring_gen3 (highway, subclass) + WHERE highway IN ('motorway', 'construction'); -- etldoc: osm_transportation_name_linestring_gen3 -> osm_transportation_name_linestring_gen4 -CREATE MATERIALIZED VIEW osm_transportation_name_linestring_gen4 AS ( - SELECT ST_Simplify(geometry, 500) AS geometry, osm_id, name, name_en, name_de, tags, ref, highway, construction, network, z_order - FROM osm_transportation_name_linestring_gen3 - WHERE (highway = 'motorway' OR highway = 'construction' AND construction = 'motorway') AND ST_Length(geometry) > 20000 -); -CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen4_geometry_idx ON osm_transportation_name_linestring_gen4 USING gist(geometry); +CREATE OR REPLACE VIEW osm_transportation_name_linestring_gen4_view AS +SELECT 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 (highway = 'motorway' OR highway = 'construction' AND subclass = '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(tags->'name', ref))); +CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen4_geometry_idx ON osm_transportation_name_linestring_gen4 USING gist (geometry); -- Handle updates CREATE SCHEMA IF NOT EXISTS transportation_name; -CREATE TABLE IF NOT EXISTS transportation_name.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION transportation_name.flag() RETURNS trigger AS $$ +-- Trigger to update "osm_transportation_name_network" from "osm_route_member" and "osm_highway_linestring" + +CREATE TABLE IF NOT EXISTS transportation_name.network_changes +( + osm_id bigint, + UNIQUE (osm_id) +); + +CREATE OR REPLACE FUNCTION transportation_name.route_member_store() RETURNS trigger AS +$$ BEGIN - INSERT INTO transportation_name.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + 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; + + RETURN NULL; END; -$$ language plpgsql; +$$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION transportation_name.refresh() RETURNS trigger AS - $BODY$ - BEGIN - RAISE LOG 'Refresh transportation_name'; +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; + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS transportation_name.updates_network +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION transportation_name.flag_network() RETURNS trigger AS +$$ +BEGIN + INSERT INTO transportation_name.updates_network(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION transportation_name.refresh_network() RETURNS trigger AS +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh transportation_name_network'; PERFORM update_osm_route_member(); - REFRESH MATERIALIZED VIEW osm_transportation_name_network; - REFRESH MATERIALIZED VIEW osm_transportation_name_linestring; - REFRESH MATERIALIZED VIEW osm_transportation_name_linestring_gen1; - REFRESH MATERIALIZED VIEW osm_transportation_name_linestring_gen2; - REFRESH MATERIALIZED VIEW osm_transportation_name_linestring_gen3; - REFRESH MATERIALIZED VIEW osm_transportation_name_linestring_gen4; - DELETE FROM transportation_name.updates; - RETURN null; - END; - $BODY$ -language plpgsql; + + -- 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; + + UPDATE osm_highway_linestring hl + SET network = rm.network_type + FROM transportation_name.network_changes c, + osm_route_member rm + WHERE hl.osm_id=c.osm_id + AND hl.osm_id=rm.member + AND rm.concurrency_index=1; + + UPDATE osm_highway_linestring_gen_z11 hl + SET network = rm.network_type + FROM transportation_name.network_changes c, + osm_route_member rm + WHERE hl.osm_id=c.osm_id + AND hl.osm_id=rm.member + AND rm.concurrency_index=1; + + INSERT INTO osm_transportation_name_network + SELECT + geometry, + osm_id, + tags || get_basic_names(tags, geometry) AS tags, + ref, + highway, + subclass, + brunnel, + level, + sac_scale, + layer, + indoor, + network_type, + route_1, route_2, route_3, route_4, route_5, route_6, + z_order, + route_rank + FROM ( + SELECT hl.geometry, + hl.osm_id, + transportation_name_tags(hl.geometry, hl.tags, hl.name, hl.name_en, hl.name_de) AS tags, + rm1.network_type, + CASE + WHEN rm1.network_type IS NOT NULL AND rm1.ref::text <> '' + THEN rm1.ref::text + ELSE NULLIF(hl.ref, '') + END AS ref, + hl.highway, + NULLIF(hl.construction, '') AS subclass, + brunnel(hl.is_bridge, hl.is_tunnel, hl.is_ford) AS brunnel, + sac_scale, + CASE WHEN highway IN ('footway', 'steps') THEN layer END AS layer, + CASE WHEN highway IN ('footway', 'steps') THEN level END AS level, + CASE WHEN highway IN ('footway', 'steps') THEN indoor END AS indoor, + 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 OUTER JOIN osm_route_member rm1 ON rm1.member = hl.osm_id AND rm1.concurrency_index=1 + LEFT OUTER JOIN osm_route_member rm2 ON rm2.member = hl.osm_id AND rm2.concurrency_index=2 + LEFT OUTER JOIN osm_route_member rm3 ON rm3.member = hl.osm_id AND rm3.concurrency_index=3 + LEFT OUTER JOIN osm_route_member rm4 ON rm4.member = hl.osm_id AND rm4.concurrency_index=4 + LEFT OUTER JOIN osm_route_member rm5 ON rm5.member = hl.osm_id AND rm5.concurrency_index=5 + LEFT OUTER JOIN osm_route_member rm6 ON rm6.member = hl.osm_id AND rm6.concurrency_index=6 + WHERE (hl.name <> '' OR hl.ref <> '' OR rm1.ref <> '' OR rm1.network <> '') + AND hl.highway <> '' + ) AS t + ON CONFLICT DO NOTHING; + + -- noinspection SqlWithoutWhere + DELETE FROM transportation_name.network_changes; + -- noinspection SqlWithoutWhere + DELETE FROM transportation_name.updates_network; + + RAISE LOG 'Refresh transportation_name network done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER trigger_store_transportation_route_member + AFTER INSERT OR UPDATE OR DELETE + ON osm_route_member + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.route_member_store(); + +CREATE TRIGGER trigger_store_transportation_highway_linestring + AFTER INSERT OR UPDATE OR DELETE + ON osm_highway_linestring + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.highway_linestring_store(); CREATE TRIGGER trigger_flag_transportation_name - AFTER INSERT OR UPDATE OR DELETE ON osm_route_member + AFTER INSERT + ON transportation_name.network_changes FOR EACH STATEMENT - EXECUTE PROCEDURE transportation_name.flag(); +EXECUTE PROCEDURE transportation_name.flag_network(); -CREATE TRIGGER trigger_flag_transportation_name - AFTER INSERT OR UPDATE OR DELETE ON osm_highway_linestring +CREATE CONSTRAINT TRIGGER trigger_refresh_network + AFTER INSERT + ON transportation_name.updates_network + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.refresh_network(); + +-- Trigger to update "osm_transportation_name_linestring" from "osm_transportation_name_network" + +CREATE TABLE IF NOT EXISTS transportation_name.name_changes +( + id serial PRIMARY KEY, + is_old boolean, + osm_id bigint, + tags hstore, + ref character varying, + highway character varying, + subclass character varying, + brunnel character varying, + sac_scale character varying, + level integer, + layer integer, + indoor boolean, + network_type route_network_type, + route_1 character varying, + route_2 character varying, + route_3 character varying, + route_4 character varying, + route_5 character varying, + route_6 character varying +); + +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, tags, ref, highway, subclass, + brunnel, sac_scale, level, layer, indoor, network_type, + route_1, route_2, route_3, route_4, route_5, route_6) + VALUES (TRUE, old.osm_id, old.tags, old.ref, old.highway, old.subclass, + old.brunnel, old.sac_scale, old.level, old.layer, old.indoor, old.network_type, + old.route_1, old.route_2, old.route_3, old.route_4, old.route_5, old.route_6); + END IF; + IF (tg_op IN ('UPDATE', 'INSERT')) + THEN + INSERT INTO transportation_name.name_changes(is_old, osm_id, tags, ref, highway, subclass, + brunnel, sac_scale, level, layer, indoor, network_type, + route_1, route_2, route_3, route_4, route_5, route_6) + VALUES (FALSE, new.osm_id, new.tags, new.ref, new.highway, new.subclass, + new.brunnel, new.sac_scale, new.level, new.layer, new.indoor, new.network_type, + new.route_1, new.route_2, new.route_3, new.route_4, new.route_5, new.route_6); + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS transportation_name.updates_name +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION transportation_name.flag_name() RETURNS trigger AS +$$ +BEGIN + INSERT INTO transportation_name.updates_name(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION transportation_name.refresh_name() RETURNS trigger AS +$BODY$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN + RAISE LOG 'Refresh transportation_name'; + + -- REFRESH osm_transportation_name_linestring + + -- 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 (tags, ref, highway, subclass, brunnel, sac_scale, level, layer, indoor, network_type, + route_1, route_2, route_3, route_4, route_5, route_6) + tags, + ref, + highway, + subclass, + brunnel, + sac_scale, + level, + layer, + indoor, + network_type, + route_1, route_2, route_3, route_4, route_5, route_6, + coalesce(tags->'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; + + DELETE + FROM osm_transportation_name_linestring AS n + USING name_changes_compact AS c + WHERE coalesce(n.ref, '') = coalesce(c.ref, '') + AND coalesce(n.tags, '') = coalesce(c.tags, '') + AND n.highway IS NOT DISTINCT FROM c.highway + AND n.subclass IS NOT DISTINCT FROM c.subclass + AND n.brunnel IS NOT DISTINCT FROM c.brunnel + AND n.sac_scale IS NOT DISTINCT FROM c.sac_scale + 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 + AND n.route_1 IS NOT DISTINCT FROM c.route_1 + AND n.route_2 IS NOT DISTINCT FROM c.route_2 + AND n.route_3 IS NOT DISTINCT FROM c.route_3 + AND n.route_4 IS NOT DISTINCT FROM c.route_4 + AND n.route_5 IS NOT DISTINCT FROM c.route_5 + AND n.route_6 IS NOT DISTINCT FROM c.route_6; + + INSERT INTO osm_transportation_name_linestring + SELECT (ST_Dump(geometry)).geom AS geometry, + tags|| get_basic_names(tags, geometry) AS tags, + ref, + highway, + subclass, + brunnel, + sac_scale, + level, + layer, + indoor, + network_type AS network, + route_1, route_2, route_3, route_4, route_5, route_6, + z_order + FROM ( + SELECT ST_LineMerge(ST_Collect(n.geometry)) AS geometry, + n.tags, + n.ref, + n.highway, + n.subclass, + n.brunnel, + n.sac_scale, + n.level, + n.layer, + n.indoor, + n.network_type, + n.route_1, n.route_2, n.route_3, n.route_4, n.route_5, n.route_6, + min(n.z_order) AS z_order + FROM osm_transportation_name_network AS n + JOIN name_changes_compact AS c ON + coalesce(n.ref, '') = coalesce(c.ref, '') + AND coalesce(n.tags, '') = coalesce(c.tags, '') + AND n.highway IS NOT DISTINCT FROM c.highway + AND n.subclass IS NOT DISTINCT FROM c.subclass + AND n.brunnel IS NOT DISTINCT FROM c.brunnel + AND n.sac_scale IS NOT DISTINCT FROM c.sac_scale + 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 + AND n.route_1 IS NOT DISTINCT FROM c.route_1 + AND n.route_2 IS NOT DISTINCT FROM c.route_2 + AND n.route_3 IS NOT DISTINCT FROM c.route_3 + AND n.route_4 IS NOT DISTINCT FROM c.route_4 + AND n.route_5 IS NOT DISTINCT FROM c.route_5 + AND n.route_6 IS NOT DISTINCT FROM c.route_6 + GROUP BY n.tags, n.ref, n.highway, n.subclass, n.brunnel, n.sac_scale, n.level, n.layer, n.indoor, n.network_type, + n.route_1, n.route_2, n.route_3, n.route_4, n.route_5, n.route_6 + ) AS highway_union; + + -- REFRESH osm_transportation_name_linestring_gen1 + DELETE FROM osm_transportation_name_linestring_gen1 AS n + USING name_changes_compact AS c + WHERE + coalesce(n.tags->'name', n.ref) = c.name_ref + AND coalesce(n.tags, '') = coalesce(c.tags, '') + AND n.ref IS NOT DISTINCT FROM c.ref + AND n.highway IS NOT DISTINCT FROM c.highway + AND n.subclass IS NOT DISTINCT FROM c.subclass + AND n.brunnel IS NOT DISTINCT FROM c.brunnel + AND n.network IS NOT DISTINCT FROM c.network_type + AND n.route_1 IS NOT DISTINCT FROM c.route_1 + AND n.route_2 IS NOT DISTINCT FROM c.route_2 + AND n.route_3 IS NOT DISTINCT FROM c.route_3 + AND n.route_4 IS NOT DISTINCT FROM c.route_4 + AND n.route_5 IS NOT DISTINCT FROM c.route_5 + AND n.route_6 IS NOT DISTINCT FROM c.route_6; + + 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.tags->'name', n.ref) = c.name_ref + AND coalesce(n.tags, '') = coalesce(c.tags, '') + AND n.ref IS NOT DISTINCT FROM c.ref + AND n.highway IS NOT DISTINCT FROM c.highway + AND n.subclass IS NOT DISTINCT FROM c.subclass + AND n.brunnel IS NOT DISTINCT FROM c.brunnel + AND n.network IS NOT DISTINCT FROM c.network_type + AND n.route_1 IS NOT DISTINCT FROM c.route_1 + AND n.route_2 IS NOT DISTINCT FROM c.route_2 + AND n.route_3 IS NOT DISTINCT FROM c.route_3 + AND n.route_4 IS NOT DISTINCT FROM c.route_4 + AND n.route_5 IS NOT DISTINCT FROM c.route_5 + AND n.route_6 IS NOT DISTINCT FROM c.route_6; + + -- REFRESH osm_transportation_name_linestring_gen2 + DELETE FROM osm_transportation_name_linestring_gen2 AS n + USING name_changes_compact AS c + WHERE + coalesce(n.tags->'name', n.ref) = c.name_ref + AND coalesce(n.tags, '') = coalesce(c.tags, '') + AND n.ref IS NOT DISTINCT FROM c.ref + AND n.highway IS NOT DISTINCT FROM c.highway + AND n.subclass IS NOT DISTINCT FROM c.subclass + AND n.brunnel IS NOT DISTINCT FROM c.brunnel + AND n.network IS NOT DISTINCT FROM c.network_type + AND n.route_1 IS NOT DISTINCT FROM c.route_1 + AND n.route_2 IS NOT DISTINCT FROM c.route_2 + AND n.route_3 IS NOT DISTINCT FROM c.route_3 + AND n.route_4 IS NOT DISTINCT FROM c.route_4 + AND n.route_5 IS NOT DISTINCT FROM c.route_5 + AND n.route_6 IS NOT DISTINCT FROM c.route_6; + + 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.tags->'name', n.ref) = c.name_ref + AND coalesce(n.tags, '') = coalesce(c.tags, '') + AND n.ref IS NOT DISTINCT FROM c.ref + AND n.highway IS NOT DISTINCT FROM c.highway + AND n.subclass IS NOT DISTINCT FROM c.subclass + AND n.brunnel IS NOT DISTINCT FROM c.brunnel + AND n.network IS NOT DISTINCT FROM c.network_type + AND n.route_1 IS NOT DISTINCT FROM c.route_1 + AND n.route_2 IS NOT DISTINCT FROM c.route_2 + AND n.route_3 IS NOT DISTINCT FROM c.route_3 + AND n.route_4 IS NOT DISTINCT FROM c.route_4 + AND n.route_5 IS NOT DISTINCT FROM c.route_5 + AND n.route_6 IS NOT DISTINCT FROM c.route_6; + + -- REFRESH osm_transportation_name_linestring_gen3 + DELETE FROM osm_transportation_name_linestring_gen3 AS n + USING name_changes_compact AS c + WHERE + coalesce(n.tags->'name', n.ref) = c.name_ref + AND coalesce(n.tags, '') = coalesce(c.tags, '') + AND n.ref IS NOT DISTINCT FROM c.ref + AND n.highway IS NOT DISTINCT FROM c.highway + AND n.subclass IS NOT DISTINCT FROM c.subclass + AND n.brunnel IS NOT DISTINCT FROM c.brunnel + AND n.network IS NOT DISTINCT FROM c.network_type + AND n.route_1 IS NOT DISTINCT FROM c.route_1 + AND n.route_2 IS NOT DISTINCT FROM c.route_2 + AND n.route_3 IS NOT DISTINCT FROM c.route_3 + AND n.route_4 IS NOT DISTINCT FROM c.route_4 + AND n.route_5 IS NOT DISTINCT FROM c.route_5 + AND n.route_6 IS NOT DISTINCT FROM c.route_6; + + 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.tags->'name', n.ref) = c.name_ref + AND coalesce(n.tags, '') = coalesce(c.tags, '') + AND n.ref IS NOT DISTINCT FROM c.ref + AND n.highway IS NOT DISTINCT FROM c.highway + AND n.subclass IS NOT DISTINCT FROM c.subclass + AND n.brunnel IS NOT DISTINCT FROM c.brunnel + AND n.network IS NOT DISTINCT FROM c.network_type + AND n.route_1 IS NOT DISTINCT FROM c.route_1 + AND n.route_2 IS NOT DISTINCT FROM c.route_2 + AND n.route_3 IS NOT DISTINCT FROM c.route_3 + AND n.route_4 IS NOT DISTINCT FROM c.route_4 + AND n.route_5 IS NOT DISTINCT FROM c.route_5 + AND n.route_6 IS NOT DISTINCT FROM c.route_6; + + -- REFRESH osm_transportation_name_linestring_gen4 + DELETE FROM osm_transportation_name_linestring_gen4 AS n + USING name_changes_compact AS c + WHERE + coalesce(n.tags->'name', n.ref) = c.name_ref + AND coalesce(n.tags, '') = coalesce(c.tags, '') + AND n.ref IS NOT DISTINCT FROM c.ref + AND n.highway IS NOT DISTINCT FROM c.highway + AND n.subclass IS NOT DISTINCT FROM c.subclass + AND n.brunnel IS NOT DISTINCT FROM c.brunnel + AND n.network IS NOT DISTINCT FROM c.network_type + AND n.route_1 IS NOT DISTINCT FROM c.route_1 + AND n.route_2 IS NOT DISTINCT FROM c.route_2 + AND n.route_3 IS NOT DISTINCT FROM c.route_3 + AND n.route_4 IS NOT DISTINCT FROM c.route_4 + AND n.route_5 IS NOT DISTINCT FROM c.route_5 + AND n.route_6 IS NOT DISTINCT FROM c.route_6; + + 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.tags->'name', n.ref) = c.name_ref + AND coalesce(n.tags, '') = coalesce(c.tags, '') + AND n.ref IS NOT DISTINCT FROM c.ref + AND n.highway IS NOT DISTINCT FROM c.highway + AND n.subclass IS NOT DISTINCT FROM c.subclass + AND n.brunnel IS NOT DISTINCT FROM c.brunnel + AND n.network IS NOT DISTINCT FROM c.network_type + AND n.route_1 IS NOT DISTINCT FROM c.route_1 + AND n.route_2 IS NOT DISTINCT FROM c.route_2 + AND n.route_3 IS NOT DISTINCT FROM c.route_3 + AND n.route_4 IS NOT DISTINCT FROM c.route_4 + AND n.route_5 IS NOT DISTINCT FROM c.route_5 + AND n.route_6 IS NOT DISTINCT FROM c.route_6; + + DROP TABLE name_changes_compact; + DELETE FROM transportation_name.name_changes; + DELETE FROM transportation_name.updates_name; + + RAISE LOG 'Refresh transportation_name done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$BODY$ + LANGUAGE plpgsql; + + +CREATE TRIGGER trigger_store_transportation_name_network + AFTER INSERT OR UPDATE OR DELETE + ON osm_transportation_name_network + FOR EACH ROW +EXECUTE PROCEDURE transportation_name.name_network_store(); + +CREATE TRIGGER trigger_flag_name + AFTER INSERT + ON transportation_name.name_changes FOR EACH STATEMENT - EXECUTE PROCEDURE transportation_name.flag(); +EXECUTE PROCEDURE transportation_name.flag_name(); -CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON transportation_name.updates +CREATE CONSTRAINT TRIGGER trigger_refresh_name + AFTER INSERT + ON transportation_name.updates_name INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE transportation_name.refresh(); +EXECUTE PROCEDURE transportation_name.refresh_name(); diff --git a/layers/water/etl_diagram.png b/layers/water/etl_diagram.png index 031bf9dd9..6efbbb815 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 68c5bfc13..012331590 100644 --- a/layers/water/mapping.yaml +++ b/layers/water/mapping.yaml @@ -1,39 +1,38 @@ generalized_tables: - - # etldoc: imposm3 -> osm_water_polygon_gen6 - water_polygon_gen6: - source: water_polygon_gen5 + # etldoc: osm_water_polygon_gen_z7 -> osm_water_polygon_gen_z6 + water_polygon_gen_z6: + source: water_polygon_gen_z7 sql_filter: area>power(ZRES5,2) tolerance: ZRES7 - # etldoc: imposm3 -> osm_water_polygon_gen5 - water_polygon_gen5: - source: water_polygon_gen4 + # etldoc: osm_water_polygon_gen_z8 -> osm_water_polygon_gen_z7 + water_polygon_gen_z7: + source: water_polygon_gen_z8 sql_filter: area>power(ZRES6,2) tolerance: ZRES8 - # etldoc: imposm3 -> osm_water_polygon_gen4 - water_polygon_gen4: - source: water_polygon_gen3 + # etldoc: osm_water_polygon_gen_z9 -> osm_water_polygon_gen_z8 + water_polygon_gen_z8: + source: water_polygon_gen_z9 sql_filter: area>power(ZRES7,2) tolerance: ZRES9 - # etldoc: imposm3 -> osm_water_polygon_gen3 - water_polygon_gen3: - source: water_polygon_gen2 + # etldoc: osm_water_polygon_gen_z10 -> osm_water_polygon_gen_z9 + water_polygon_gen_z9: + source: water_polygon_gen_z10 sql_filter: area>power(ZRES8,2) tolerance: ZRES10 - # etldoc: imposm3 -> osm_water_polygon_gen2 - water_polygon_gen2: - source: water_polygon_gen1 + # etldoc: osm_water_polygon_gen_z11 -> osm_water_polygon_gen_z10 + water_polygon_gen_z10: + source: water_polygon_gen_z11 sql_filter: area>power(ZRES9,2) tolerance: ZRES11 - # etldoc: imposm3 -> osm_water_polygon_gen1 - water_polygon_gen1: + # etldoc: osm_water_polygon -> osm_water_polygon_gen_z11 + water_polygon_gen_z11: source: water_polygon - sql_filter: area>power(ZRES10,2) AND ST_IsValid(geometry) + sql_filter: area>power(ZRES10,2) tolerance: ZRES12 tunnel_field: &tunnel @@ -49,7 +48,7 @@ tables: # etldoc: imposm3 -> osm_water_polygon water_polygon: - fields: + columns: - name: osm_id type: id - name: geometry @@ -76,6 +75,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 @@ -87,15 +92,16 @@ tables: mapping: landuse: - reservoir + - basin + - salt_pond + leisure: + - swimming_pool natural: - water - bay + - spring waterway: - - river - - riverbank - - stream - - canal - - drain - - ditch - dock + water: + - river type: polygon diff --git a/layers/water/mapping_diagram.png b/layers/water/mapping_diagram.png index 2dce3fcbb..52e8c4446 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 000000000..9ce2fcab3 --- /dev/null +++ b/layers/water/style.json @@ -0,0 +1,54 @@ +{ + "layers": [ + { + "id": "water_intermittent", + "type": "fill", + "source": "openmaptiles", + "source-layer": "water", + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": "rgba(172, 218, 251, 1)", + "fill-opacity": 0.85 + }, + "metadata": {}, + "filter": [ + "all", + [ + "==", + "intermittent", + 1 + ] + ], + "order": 17 + }, + { + "id": "water", + "type": "fill", + "source": "openmaptiles", + "source-layer": "water", + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": "#aad3df" + }, + "metadata": {}, + "filter": [ + "all", + [ + "!=", + "intermittent", + 1 + ], + [ + "!=", + "brunnel", + "tunnel" + ] + ], + "order": 18 + } + ] +} \ No newline at end of file diff --git a/layers/water/update_water.sql b/layers/water/update_water.sql new file mode 100644 index 000000000..b85ad24bc --- /dev/null +++ b/layers/water/update_water.sql @@ -0,0 +1,142 @@ +-- Recreate ocean layer by union regular squares into larger polygons +-- etldoc: osm_ocean_polygon -> osm_ocean_polygon_union +CREATE TABLE IF NOT EXISTS osm_ocean_polygon_union AS + ( + SELECT (ST_Dump(ST_Union(ST_MakeValid(geometry)))).geom::geometry(Polygon, 3857) AS geometry + FROM osm_ocean_polygon + --for union select just full square (not big triangles) + WHERE ST_Area(geometry) > 100000000 AND + ST_NPoints(geometry) = 5 + UNION ALL + SELECT geometry + FROM osm_ocean_polygon + -- as 321 records have less then 5 coordinates (triangle) + -- bigger then 5 coordinates have squares with holes from island and coastline + WHERE ST_NPoints(geometry) <> 5 + ); + +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_union_geom_idx + ON osm_ocean_polygon_union + USING GIST (geometry); + +--Drop data from original table but keep table as `CREATE TABLE IF NOT EXISTS` still test if query is valid +TRUNCATE TABLE osm_ocean_polygon; + +-- This statement can be deleted after the water importer image stops creating this object as a table +DO +$$ + BEGIN + DROP TABLE IF EXISTS osm_ocean_polygon_gen_z11 CASCADE; + EXCEPTION + WHEN wrong_object_type THEN + END; +$$ LANGUAGE plpgsql; + +-- etldoc: osm_ocean_polygon_union -> osm_ocean_polygon_gen_z11 +DROP MATERIALIZED VIEW IF EXISTS osm_ocean_polygon_gen_z11 CASCADE; +CREATE MATERIALIZED VIEW osm_ocean_polygon_gen_z11 AS +( +SELECT ST_Simplify(geometry, ZRes(13)) AS geometry +FROM osm_ocean_polygon_union + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_gen_z11_idx ON osm_ocean_polygon_gen_z11 USING gist (geometry); + + +-- This statement can be deleted after the water importer image stops creating this object as a table +DO +$$ + BEGIN + DROP TABLE IF EXISTS osm_ocean_polygon_gen_z10 CASCADE; + EXCEPTION + WHEN wrong_object_type THEN + END; +$$ LANGUAGE plpgsql; + +-- etldoc: osm_ocean_polygon_gen_z11 -> osm_ocean_polygon_gen_z10 +DROP MATERIALIZED VIEW IF EXISTS osm_ocean_polygon_gen_z10 CASCADE; +CREATE MATERIALIZED VIEW osm_ocean_polygon_gen_z10 AS +( +SELECT ST_Simplify(geometry, ZRes(12)) AS geometry +FROM osm_ocean_polygon_gen_z11 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_gen_z10_idx ON osm_ocean_polygon_gen_z10 USING gist (geometry); + + +-- This statement can be deleted after the water importer image stops creating this object as a table +DO +$$ + BEGIN + DROP TABLE IF EXISTS osm_ocean_polygon_gen_z9 CASCADE; + EXCEPTION + WHEN wrong_object_type THEN + END; +$$ LANGUAGE plpgsql; + +-- etldoc: osm_ocean_polygon_gen_z10 -> osm_ocean_polygon_gen_z9 +DROP MATERIALIZED VIEW IF EXISTS osm_ocean_polygon_gen_z9 CASCADE; +CREATE MATERIALIZED VIEW osm_ocean_polygon_gen_z9 AS +( +SELECT ST_Simplify(geometry, ZRes(11)) AS geometry +FROM osm_ocean_polygon_gen_z10 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_gen_z9_idx ON osm_ocean_polygon_gen_z9 USING gist (geometry); + + +-- This statement can be deleted after the water importer image stops creating this object as a table +DO +$$ + BEGIN + DROP TABLE IF EXISTS osm_ocean_polygon_gen_z8 CASCADE; + EXCEPTION + WHEN wrong_object_type THEN + END; +$$ LANGUAGE plpgsql; + +-- etldoc: osm_ocean_polygon_gen_z9 -> osm_ocean_polygon_gen_z8 +DROP MATERIALIZED VIEW IF EXISTS osm_ocean_polygon_gen_z8 CASCADE; +CREATE MATERIALIZED VIEW osm_ocean_polygon_gen_z8 AS +( +SELECT ST_Simplify(geometry, ZRes(10)) AS geometry +FROM osm_ocean_polygon_gen_z9 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_gen_z8_idx ON osm_ocean_polygon_gen_z8 USING gist (geometry); + + +-- This statement can be deleted after the water importer image stops creating this object as a table +DO +$$ + BEGIN + DROP TABLE IF EXISTS osm_ocean_polygon_gen_z7 CASCADE; + EXCEPTION + WHEN wrong_object_type THEN + END; +$$ LANGUAGE plpgsql; + +-- etldoc: osm_ocean_polygon_gen_z8 -> osm_ocean_polygon_gen_z7 +DROP MATERIALIZED VIEW IF EXISTS osm_ocean_polygon_gen_z7 CASCADE; +CREATE MATERIALIZED VIEW osm_ocean_polygon_gen_z7 AS +( +SELECT ST_Simplify(geometry, ZRes(9)) AS geometry +FROM osm_ocean_polygon_gen_z8 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_gen_z7_idx ON osm_ocean_polygon_gen_z7 USING gist (geometry); + + +-- This statement can be deleted after the water importer image stops creating this object as a table +DO +$$ + BEGIN + DROP TABLE IF EXISTS osm_ocean_polygon_gen_z6 CASCADE; + EXCEPTION + WHEN wrong_object_type THEN + END; +$$ LANGUAGE plpgsql; + +-- etldoc: osm_ocean_polygon_gen_z7 -> osm_ocean_polygon_gen_z6 +DROP MATERIALIZED VIEW IF EXISTS osm_ocean_polygon_gen_z6 CASCADE; +CREATE MATERIALIZED VIEW osm_ocean_polygon_gen_z6 AS +( +SELECT ST_Simplify(geometry, ZRes(8)) AS geometry +FROM osm_ocean_polygon_gen_z7 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS osm_ocean_polygon_gen_z6_idx ON osm_ocean_polygon_gen_z6 USING gist (geometry); diff --git a/layers/water/water.sql b/layers/water/water.sql index a7ca1f6b2..a78c07ae9 100644 --- a/layers/water/water.sql +++ b/layers/water/water.sql @@ -1,336 +1,631 @@ -CREATE OR REPLACE FUNCTION water_class(waterway TEXT) RETURNS TEXT AS $$ - SELECT CASE - WHEN waterway='' THEN 'lake' - WHEN waterway='lake' THEN 'lake' - WHEN waterway='dock' THEN 'dock' - ELSE 'river' - END; -$$ LANGUAGE SQL IMMUTABLE; - - -CREATE OR REPLACE FUNCTION waterway_brunnel(is_bridge BOOL, is_tunnel BOOL) RETURNS TEXT AS $$ - SELECT CASE - WHEN is_bridge THEN 'bridge' - WHEN is_tunnel THEN 'tunnel' - ELSE NULL - END; -$$ LANGUAGE SQL IMMUTABLE STRICT; - - - -CREATE OR REPLACE VIEW water_z0 AS ( - -- etldoc: ne_110m_ocean -> water_z0 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM ne_110m_ocean - UNION ALL - -- etldoc: ne_110m_lakes -> water_z0 - SELECT geometry, - 'lake'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM ne_110m_lakes -); +CREATE OR REPLACE FUNCTION water_class(waterway text, water text, leisure text) RETURNS text AS +$$ +SELECT CASE + %%FIELD_MAPPING: class %% + ELSE 'lake' + END; +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; -CREATE OR REPLACE VIEW water_z1 AS ( - -- etldoc: ne_110m_ocean -> water_z1 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM ne_110m_ocean - UNION ALL - -- etldoc: ne_110m_lakes -> water_z1 - SELECT geometry, - 'lake'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM ne_110m_lakes -); -CREATE OR REPLACE VIEW water_z2 AS ( - -- etldoc: ne_50m_ocean -> water_z2 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM ne_50m_ocean - UNION ALL - -- etldoc: ne_50m_lakes -> water_z2 - SELECT geometry, - 'lake'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM ne_50m_lakes -); +CREATE OR REPLACE FUNCTION waterway_brunnel(is_bridge bool, is_tunnel bool) RETURNS text AS +$$ +SELECT CASE + WHEN is_bridge THEN 'bridge' + WHEN is_tunnel THEN 'tunnel' + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; -CREATE OR REPLACE VIEW water_z4 AS ( - -- etldoc: ne_50m_ocean -> water_z4 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM ne_50m_ocean - UNION ALL - -- etldoc: ne_10m_lakes -> water_z4 - SELECT geometry, - 'lake'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM ne_10m_lakes -); -CREATE OR REPLACE VIEW water_z5 AS ( - -- etldoc: ne_10m_ocean -> water_z5 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM ne_10m_ocean - UNION ALL - -- etldoc: ne_10m_lakes -> water_z5 - SELECT geometry, - 'lake'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM ne_10m_lakes -); +-- 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 -CREATE OR REPLACE VIEW water_z6 AS ( - -- etldoc: osm_ocean_polygon_gen4 -> water_z6 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_ocean_polygon_gen4 - UNION ALL - -- etldoc: osm_water_polygon_gen6 -> water_z6 - SELECT geometry, - water_class(waterway) AS class, - is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_water_polygon_gen6 - WHERE "natural" != 'bay' -); +UNION -CREATE OR REPLACE VIEW water_z7 AS ( - -- etldoc: osm_ocean_polygon_gen4 -> water_z7 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_ocean_polygon_gen4 - UNION ALL - -- etldoc: osm_water_polygon_gen5 -> water_z7 - SELECT geometry, - water_class(waterway) AS class, - is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_water_polygon_gen5 - WHERE "natural" != 'bay' +SELECT ne_id, + osm_id +FROM geom_match ); -CREATE OR REPLACE VIEW water_z8 AS ( - -- etldoc: osm_ocean_polygon_gen4 -> water_z8 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_ocean_polygon_gen4 - UNION ALL - -- etldoc: osm_water_polygon_gen4 -> water_z8 - SELECT geometry, - water_class(waterway) AS class, - is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_water_polygon_gen4 - WHERE "natural" != 'bay' -); +-- ne_10m_ocean +-- etldoc: ne_10m_ocean -> ne_10m_ocean_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_ocean_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_ocean_gen_z5 AS +( +SELECT NULL::integer AS id, + (ST_Dump(ST_Simplify(geometry, ZRes(7)))).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM ne_10m_ocean + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_ocean_gen_z5_idx ON ne_10m_ocean_gen_z5 USING gist (geometry); -CREATE OR REPLACE VIEW water_z9 AS ( - -- etldoc: osm_ocean_polygon_gen3 -> water_z9 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_ocean_polygon_gen3 - UNION ALL - -- etldoc: osm_water_polygon_gen3 -> water_z9 - SELECT geometry, - water_class(waterway) AS class, - is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_water_polygon_gen3 - WHERE "natural" != 'bay' -); +-- ne_10m_lakes +-- etldoc: ne_10m_lakes -> ne_10m_lakes_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_lakes_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_lakes_gen_z5 AS +( +SELECT COALESCE(osm.osm_id, ne_id) AS id, + -- Union fixing e.g. Lake Huron and Georgian Bay duplicity + (ST_Dump(ST_MakeValid(ST_Simplify(ST_Union(geometry), ZRes(7))))).geom AS geometry, + 'lake'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM ne_10m_lakes +LEFT JOIN match_osm_ne_id osm USING (ne_id) +GROUP BY COALESCE(osm.osm_id, ne_id), is_intermittent, is_bridge, is_tunnel + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_lakes_gen_z5_idx ON ne_10m_lakes_gen_z5 USING gist (geometry); -CREATE OR REPLACE VIEW water_z10 AS ( - -- etldoc: osm_ocean_polygon_gen2 -> water_z10 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_ocean_polygon_gen2 - UNION ALL - -- etldoc: osm_water_polygon_gen2 -> water_z10 - SELECT geometry, - water_class(waterway) AS class, - is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_water_polygon_gen2 - WHERE "natural" != 'bay' -); +-- etldoc: ne_10m_lakes_gen_z5 -> ne_10m_lakes_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_10m_lakes_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_10m_lakes_gen_z4 AS +( +SELECT id, + (ST_Dump(ST_MakeValid(ST_Simplify(geometry, ZRes(6))))).geom AS geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_10m_lakes_gen_z5 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_10m_lakes_gen_z4_idx ON ne_10m_lakes_gen_z4 USING gist (geometry); -CREATE OR REPLACE VIEW water_z11 AS ( - -- etldoc: osm_ocean_polygon_gen1 -> water_z11 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_ocean_polygon_gen1 - UNION ALL - -- etldoc: osm_water_polygon_gen1 -> water_z11 - SELECT geometry, - water_class(waterway) AS class, - is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_water_polygon_gen1 - WHERE "natural" != 'bay' -); +-- ne_50m_ocean +-- etldoc: ne_50m_ocean -> ne_50m_ocean_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_ocean_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_ocean_gen_z4 AS +( +SELECT NULL::integer AS id, + (ST_Dump(ST_Simplify(geometry, ZRes(6)))).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM ne_50m_ocean + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_ocean_gen_z4_idx ON ne_50m_ocean_gen_z4 USING gist (geometry); -CREATE OR REPLACE VIEW water_z12 AS ( - -- etldoc: osm_ocean_polygon_gen1 -> water_z12 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_ocean_polygon - UNION ALL - -- etldoc: osm_water_polygon -> water_z12 - SELECT geometry, - water_class(waterway) AS class, - is_intermittent, - is_bridge, - is_tunnel - FROM osm_water_polygon - WHERE "natural" != 'bay' -); +-- etldoc: ne_50m_ocean_gen_z4 -> ne_50m_ocean_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_ocean_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_ocean_gen_z3 AS +( +SELECT id, + ST_Simplify(geometry, ZRes(5)) AS geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_ocean_gen_z4 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_ocean_gen_z3_idx ON ne_50m_ocean_gen_z3 USING gist (geometry); -CREATE OR REPLACE VIEW water_z13 AS ( - -- etldoc: osm_ocean_polygon -> water_z13 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_ocean_polygon - UNION ALL - -- etldoc: osm_water_polygon -> water_z13 - SELECT geometry, - water_class(waterway) AS class, - is_intermittent, - is_bridge, - is_tunnel - FROM osm_water_polygon - WHERE "natural" != 'bay' -); +-- etldoc: ne_50m_ocean_gen_z3 -> ne_50m_ocean_gen_z2 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_ocean_gen_z2 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_ocean_gen_z2 AS +( +SELECT id, + ST_Simplify(geometry, ZRes(4)) AS geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_ocean_gen_z3 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_ocean_gen_z2_idx ON ne_50m_ocean_gen_z2 USING gist (geometry); -CREATE OR REPLACE VIEW water_z14 AS ( - -- etldoc: osm_ocean_polygon -> water_z14 - SELECT geometry, - 'ocean'::text AS class, - NULL::boolean AS is_intermittent, - NULL::boolean AS is_bridge, - NULL::boolean AS is_tunnel - FROM osm_ocean_polygon - UNION ALL - -- etldoc: osm_water_polygon -> water_z14 - SELECT geometry, - water_class(waterway) AS class, - is_intermittent, - is_bridge, - is_tunnel - FROM osm_water_polygon - WHERE "natural" != 'bay' -); +-- ne_50m_lakes +-- etldoc: ne_50m_lakes -> ne_50m_lakes_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_lakes_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_lakes_gen_z3 AS +( +SELECT COALESCE(osm.osm_id, ne_id) AS id, + (ST_Dump(ST_MakeValid(ST_Simplify(geometry, ZRes(5))))).geom AS geometry, + 'lake'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM ne_50m_lakes +LEFT JOIN match_osm_ne_id osm USING (ne_id) + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_lakes_gen_z3_idx ON ne_50m_lakes_gen_z3 USING gist (geometry); + +-- etldoc: ne_50m_lakes_gen_z3 -> ne_50m_lakes_gen_z2 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_lakes_gen_z2 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_lakes_gen_z2 AS +( +SELECT id, + (ST_Dump(ST_MakeValid(ST_Simplify(geometry, ZRes(4))))).geom AS geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_lakes_gen_z3 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_lakes_gen_z2_idx ON ne_50m_lakes_gen_z2 USING gist (geometry); + +--ne_110m_ocean +-- etldoc: ne_110m_ocean -> ne_110m_ocean_gen_z1 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_ocean_gen_z1 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_ocean_gen_z1 AS +( +SELECT NULL::integer AS id, + ST_Simplify(geometry, ZRes(3)) AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM ne_110m_ocean + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_ocean_gen_z1_idx ON ne_110m_ocean_gen_z1 USING gist (geometry); + +-- etldoc: ne_110m_ocean_gen_z1 -> ne_110m_ocean_gen_z0 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_ocean_gen_z0 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_ocean_gen_z0 AS +( +SELECT id, + ST_Simplify(geometry, ZRes(2)) AS geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_110m_ocean_gen_z1 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_ocean_gen_z0_idx ON ne_110m_ocean_gen_z0 USING gist (geometry); + + +-- ne_110m_lakes +-- etldoc: ne_110m_lakes -> ne_110m_lakes_gen_z1 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_lakes_gen_z1 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_lakes_gen_z1 AS +( +SELECT COALESCE(osm.osm_id, ne_id) AS id, + (ST_Dump(ST_Simplify(geometry, ZRes(3)))).geom AS geometry, + 'lake'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM ne_110m_lakes +LEFT JOIN match_osm_ne_id osm USING (ne_id) + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_lakes_gen_z1_idx ON ne_110m_lakes_gen_z1 USING gist (geometry); + +-- etldoc: ne_110m_lakes_gen_z1 -> ne_110m_lakes_gen_z0 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_lakes_gen_z0 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_lakes_gen_z0 AS +( +SELECT id, + (ST_Dump(ST_Simplify(geometry, ZRes(2)))).geom AS geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_110m_lakes_gen_z1 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_lakes_gen_z0_idx ON ne_110m_lakes_gen_z0 USING gist (geometry); + +DROP MATERIALIZED VIEW IF EXISTS water_z6; +DROP MATERIALIZED VIEW IF EXISTS water_z7; +DROP MATERIALIZED VIEW IF EXISTS water_z8; +DROP MATERIALIZED VIEW IF EXISTS water_z9; +DROP MATERIALIZED VIEW IF EXISTS water_z10; +DROP MATERIALIZED VIEW IF EXISTS water_z11; +DROP MATERIALIZED VIEW IF EXISTS water_z12; + +CREATE OR REPLACE VIEW water_z0 AS +( +-- etldoc: ne_110m_ocean_gen_z0 -> water_z0 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_110m_ocean_gen_z0 +UNION ALL +-- etldoc: ne_110m_lakes_gen_z0 -> water_z0 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_110m_lakes_gen_z0 + ); + +CREATE OR REPLACE VIEW water_z1 AS +( +-- etldoc: ne_110m_ocean_gen_z1 -> water_z1 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_110m_ocean_gen_z1 +UNION ALL +-- etldoc: ne_110m_lakes_gen_z1 -> water_z1 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_110m_lakes_gen_z1 + ); + +CREATE OR REPLACE VIEW water_z2 AS +( +-- etldoc: ne_50m_ocean_gen_z2 -> water_z2 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_ocean_gen_z2 +UNION ALL +-- etldoc: ne_50m_lakes_gen_z2 -> water_z2 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_lakes_gen_z2 + ); + +CREATE OR REPLACE VIEW water_z3 AS +( +-- etldoc: ne_50m_ocean_gen_z3 -> water_z3 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_ocean_gen_z3 +UNION ALL +-- etldoc: ne_50m_lakes_gen_z3 -> water_z3 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_lakes_gen_z3 + ); + +CREATE OR REPLACE VIEW water_z4 AS +( +-- etldoc: ne_50m_ocean_gen_z4 -> water_z4 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_50m_ocean_gen_z4 +UNION ALL +-- etldoc: ne_10m_lakes_gen_z4 -> water_z4 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_10m_lakes_gen_z4 + ); + + +CREATE OR REPLACE VIEW water_z5 AS +( +-- etldoc: ne_10m_ocean_gen_z5 -> water_z5 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_10m_ocean_gen_z5 +UNION ALL +-- etldoc: ne_10m_lakes_gen_z5 -> water_z5 +SELECT id, + geometry, + class, + is_intermittent, + is_bridge, + is_tunnel +FROM ne_10m_lakes_gen_z5 + ); + +CREATE MATERIALIZED VIEW water_z6 AS +( +-- etldoc: osm_ocean_polygon_gen_z6 -> water_z6 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_gen_z6 +UNION ALL +-- etldoc: osm_water_polygon_gen_z6 -> water_z6 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_water_polygon_gen_z6 +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z6 USING gist(geometry); + +CREATE MATERIALIZED VIEW water_z7 AS +( +-- etldoc: osm_ocean_polygon_gen_z7 -> water_z7 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_gen_z7 +UNION ALL +-- etldoc: osm_water_polygon_gen_z7 -> water_z7 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_water_polygon_gen_z7 +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z7 USING gist(geometry); + +CREATE MATERIALIZED VIEW water_z8 AS +( +-- etldoc: osm_ocean_polygon_gen_z8 -> water_z8 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_gen_z8 +UNION ALL +-- etldoc: osm_water_polygon_gen_z8 -> water_z8 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_water_polygon_gen_z8 +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z8 USING gist(geometry); + +CREATE MATERIALIZED VIEW water_z9 AS +( +-- etldoc: osm_ocean_polygon_gen_z9 -> water_z9 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_gen_z9 +UNION ALL +-- etldoc: osm_water_polygon_gen_z9 -> water_z9 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_water_polygon_gen_z9 +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z9 USING gist(geometry); + +CREATE MATERIALIZED VIEW water_z10 AS +( +-- etldoc: osm_ocean_polygon_gen_z10 -> water_z10 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_gen_z10 +UNION ALL +-- etldoc: osm_water_polygon_gen_z10 -> water_z10 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_water_polygon_gen_z10 +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z10 USING gist(geometry); + +CREATE MATERIALIZED VIEW water_z11 AS +( +-- etldoc: osm_ocean_polygon_gen_z11 -> water_z11 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_gen_z11 +UNION ALL +-- etldoc: osm_water_polygon_gen_z11 -> water_z11 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_water_polygon_gen_z11 +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z11 USING gist(geometry); + +CREATE MATERIALIZED VIEW water_z12 AS +( +-- etldoc: osm_ocean_polygon_union -> water_z12 +SELECT NULL::integer AS id, + (ST_Dump(geometry)).geom AS geometry, + 'ocean'::text AS class, + NULL::boolean AS is_intermittent, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel +FROM osm_ocean_polygon_union +UNION ALL +-- etldoc: osm_water_polygon -> water_z12 +SELECT osm_id AS id, + (ST_Dump(geometry)).geom AS geometry, + water_class(waterway, water, leisure) AS class, + is_intermittent, + is_bridge, + is_tunnel +FROM osm_water_polygon +WHERE "natural" != 'bay' + ); +CREATE INDEX ON water_z12 USING gist(geometry); -- etldoc: layer_water [shape=record fillcolor=lightpink, style="rounded,filled", --- etldoc: label="layer_water | z0|z1|z2|z3 | z4|z5|z6|z7| z8 | z9 | z10 | z11 | z12| z13| z14+" ] ; - -CREATE OR REPLACE FUNCTION layer_water (bbox geometry, zoom_level int) -RETURNS TABLE(geometry geometry, class text, brunnel text, intermittent int) AS $$ - SELECT geometry, - class::text, - waterway_brunnel(is_bridge, is_tunnel) AS brunnel, - is_intermittent::int AS intermittent - FROM ( - -- etldoc: water_z0 -> layer_water:z0 - SELECT * FROM water_z0 WHERE zoom_level = 0 - UNION ALL - -- etldoc: water_z1 -> layer_water:z1 - SELECT * FROM water_z1 WHERE zoom_level = 1 - UNION ALL - -- etldoc: water_z2 -> layer_water:z2 - -- etldoc: water_z2 -> layer_water:z3 - SELECT * FROM water_z2 WHERE zoom_level BETWEEN 2 AND 3 - UNION ALL - -- etldoc: water_z4 -> layer_water:z4 - SELECT * FROM water_z4 WHERE zoom_level = 4 - UNION ALL - -- etldoc: water_z5 -> layer_water:z5 - SELECT * FROM water_z5 WHERE zoom_level = 5 - UNION ALL - -- etldoc: water_z6 -> layer_water:z6 - SELECT * FROM water_z6 WHERE zoom_level = 6 - UNION ALL - -- etldoc: water_z7 -> layer_water:z7 - SELECT * FROM water_z7 WHERE zoom_level = 7 - UNION ALL - -- etldoc: water_z8 -> layer_water:z8 - SELECT * FROM water_z8 WHERE zoom_level = 8 - UNION ALL - -- etldoc: water_z9 -> layer_water:z9 - SELECT * FROM water_z9 WHERE zoom_level = 9 - UNION ALL - -- etldoc: water_z10 -> layer_water:z10 - SELECT * FROM water_z10 WHERE zoom_level = 10 - UNION ALL - -- etldoc: water_z11 -> layer_water:z11 - SELECT * FROM water_z11 WHERE zoom_level = 11 - UNION ALL - -- etldoc: water_z12 -> layer_water:z12 - SELECT * FROM water_z12 WHERE zoom_level = 12 - UNION ALL - -- etldoc: water_z13 -> layer_water:z13 - SELECT * FROM water_z13 WHERE zoom_level = 13 - UNION ALL - -- etldoc: water_z14 -> layer_water:z14_ - SELECT * FROM water_z14 WHERE zoom_level >= 14 - ) AS zoom_levels - WHERE geometry && bbox; -$$ LANGUAGE SQL IMMUTABLE; +-- etldoc: label="layer_water | z0|z1|z2|z3 | z4|z5|z6|z7| z8 | z9 | z10 | z11 | z12+" ] ; + +CREATE OR REPLACE FUNCTION layer_water(bbox geometry, zoom_level int) + RETURNS TABLE + ( + id bigint, + geometry geometry, + class text, + brunnel text, + intermittent int + ) +AS +$$ +SELECT id, + geometry, + class::text, + waterway_brunnel(is_bridge, is_tunnel) AS brunnel, + is_intermittent::int AS intermittent +FROM ( + -- etldoc: water_z0 -> layer_water:z0 + SELECT * + FROM water_z0 + WHERE zoom_level = 0 + UNION ALL + -- etldoc: water_z1 -> layer_water:z1 + SELECT * + FROM water_z1 + WHERE zoom_level = 1 + UNION ALL + -- etldoc: water_z2 -> layer_water:z2 + SELECT * + FROM water_z2 + WHERE zoom_level = 2 + UNION ALL + -- etldoc: water_z3 -> layer_water:z3 + SELECT * + FROM water_z3 + WHERE zoom_level = 3 + UNION ALL + -- etldoc: water_z4 -> layer_water:z4 + SELECT * + FROM water_z4 + WHERE zoom_level = 4 + UNION ALL + -- etldoc: water_z5 -> layer_water:z5 + SELECT * + FROM water_z5 + WHERE zoom_level = 5 + UNION ALL + -- etldoc: water_z6 -> layer_water:z6 + SELECT * + FROM water_z6 + WHERE zoom_level = 6 + UNION ALL + -- etldoc: water_z7 -> layer_water:z7 + SELECT * + FROM water_z7 + WHERE zoom_level = 7 + UNION ALL + -- etldoc: water_z8 -> layer_water:z8 + SELECT * + FROM water_z8 + WHERE zoom_level = 8 + UNION ALL + -- etldoc: water_z9 -> layer_water:z9 + SELECT * + FROM water_z9 + WHERE zoom_level = 9 + UNION ALL + -- etldoc: water_z10 -> layer_water:z10 + SELECT * + FROM water_z10 + WHERE zoom_level = 10 + UNION ALL + -- etldoc: water_z11 -> layer_water:z11 + SELECT * + FROM water_z11 + WHERE zoom_level = 11 + UNION ALL + -- etldoc: water_z12 -> layer_water:z12 + SELECT * + FROM water_z12 + WHERE zoom_level >= 12 + ) AS zoom_levels +WHERE geometry && bbox; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/water/water.yaml b/layers/water/water.yaml index 2e77b7b99..8b76a7059 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,14 +18,26 @@ 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. + Water bodies with the [`water=river`](http://wiki.openstreetmap.org/wiki/Tag:water=river) tag are classified as river. Wet and dry docks + tagged [`waterway=dock`](http://wiki.openstreetmap.org/wiki/Tag:waterway=dock) are classified as a `dock`. + 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: - - ocean - - lake - - river + dock: + waterway: 'dock' + river: + water: 'river' + 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. @@ -29,8 +50,9 @@ 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 datasources: - type: imposm3 diff --git a/layers/water_name/etl_diagram.png b/layers/water_name/etl_diagram.png index b0e4a40b8..8308ce14b 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 7d27ac4c8..558ad61ac 100644 --- a/layers/water_name/mapping.yaml +++ b/layers/water_name/mapping.yaml @@ -2,7 +2,7 @@ tables: # etldoc: imposm3 -> osm_marine_point marine_point: type: point - fields: + columns: - name: osm_id type: id - name: geometry diff --git a/layers/water_name/mapping_diagram.png b/layers/water_name/mapping_diagram.png index 2dce3fcbb..3bbee61b6 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 000000000..82967fbce --- /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": 143 + }, + { + "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": 144 + } + ] +} \ 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 c33a506ed..5fdbc7a26 100644 --- a/layers/water_name/update_marine_point.sql +++ b/layers/water_name/update_marine_point.sql @@ -1,69 +1,105 @@ DROP TRIGGER IF EXISTS trigger_flag ON osm_marine_point; +DROP TRIGGER IF EXISTS trigger_store ON osm_marine_point; DROP TRIGGER IF EXISTS trigger_refresh ON water_name_marine.updates; -CREATE EXTENSION IF NOT EXISTS unaccent; +CREATE SCHEMA IF NOT EXISTS water_name_marine; -CREATE OR REPLACE FUNCTION update_osm_marine_point() RETURNS VOID AS $$ -BEGIN - -- etldoc: osm_marine_point -> osm_marine_point - UPDATE osm_marine_point AS osm SET "rank" = NULL WHERE "rank" IS NOT NULL; - - -- etldoc: ne_10m_geography_marine_polys -> osm_marine_point - -- etldoc: osm_marine_point -> osm_marine_point - - WITH important_marine_point AS ( - SELECT osm.geometry, osm.osm_id, osm.name, osm.name_en, ne.scalerank, osm.is_intermittent - FROM ne_10m_geography_marine_polys AS ne, osm_marine_point AS osm - WHERE trim(regexp_replace(ne.name, '\\s+', ' ', 'g')) ILIKE osm.name - OR trim(regexp_replace(ne.name, '\\s+', ' ', 'g')) ILIKE osm.tags->'name:en' - OR trim(regexp_replace(ne.name, '\\s+', ' ', 'g')) ILIKE osm.tags->'name:es' - OR osm.name ILIKE trim(regexp_replace(ne.name, '\\s+', ' ', 'g')) || ' %' - ) - UPDATE osm_marine_point AS osm - SET "rank" = scalerank - FROM important_marine_point AS ne - WHERE osm.osm_id = ne.osm_id; - - UPDATE osm_marine_point - SET tags = update_tags(tags, geometry) - WHERE COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL; +CREATE TABLE IF NOT EXISTS water_name_marine.osm_ids +( + osm_id bigint +); -END; -$$ LANGUAGE plpgsql; +CREATE OR REPLACE FUNCTION update_osm_marine_point(full_update boolean) RETURNS void AS +$$ + -- etldoc: ne_10m_geography_marine_polys -> osm_marine_point + -- etldoc: osm_marine_point -> osm_marine_point + + WITH important_marine_point AS ( + SELECT osm.osm_id, ne.scalerank + FROM osm_marine_point AS osm + LEFT JOIN ne_10m_geography_marine_polys AS ne ON + lower(trim(regexp_replace(ne.name, '\\s+', ' ', 'g'))) IN (lower(osm.name), lower(osm.tags->'name:en'), lower(osm.tags->'name:es')) + OR substring(lower(trim(regexp_replace(ne.name, '\\s+', ' ', 'g'))) FROM 1 FOR length(lower(osm.name))) = lower(osm.name) + ) + UPDATE osm_marine_point AS osm + SET "rank" = scalerank + FROM important_marine_point AS ne + WHERE (full_update OR osm.osm_id IN (SELECT osm_id FROM water_name_marine.osm_ids)) + AND osm.osm_id = ne.osm_id + AND "rank" IS DISTINCT FROM scalerank; -SELECT update_osm_marine_point(); + UPDATE osm_marine_point + SET tags = update_tags(tags, geometry) + WHERE (full_update OR osm_id IN (SELECT osm_id FROM water_name_marine.osm_ids)) + AND COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL + AND tags != update_tags(tags, geometry); -CREATE INDEX IF NOT EXISTS osm_marine_point_rank_idx ON osm_marine_point("rank"); +$$ LANGUAGE SQL; + +SELECT update_osm_marine_point(true); + +CREATE INDEX IF NOT EXISTS osm_marine_point_rank_idx ON osm_marine_point ("rank"); -- Handle updates -CREATE SCHEMA IF NOT EXISTS water_name_marine; -CREATE TABLE IF NOT EXISTS water_name_marine.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION water_name_marine.flag() RETURNS trigger AS $$ +CREATE OR REPLACE FUNCTION water_name_marine.store() RETURNS trigger AS +$$ BEGIN - INSERT INTO water_name_marine.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + 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; + RETURN NULL; END; -$$ language plpgsql; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS water_name_marine.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION water_name_marine.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO water_name_marine.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION water_name_marine.refresh() RETURNS trigger AS - $BODY$ - BEGIN +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN RAISE LOG 'Refresh water_name_marine rank'; - PERFORM update_osm_marine_point(); + PERFORM update_osm_marine_point(false); + -- noinspection SqlWithoutWhere + DELETE FROM water_name_marine.osm_ids; + -- noinspection SqlWithoutWhere DELETE FROM water_name_marine.updates; - RETURN null; - END; - $BODY$ -language plpgsql; + + RAISE LOG 'Refresh water_name_marine done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_marine_point + FOR EACH ROW +EXECUTE PROCEDURE water_name_marine.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE ON osm_marine_point + AFTER INSERT OR UPDATE OR DELETE + ON osm_marine_point FOR EACH STATEMENT - EXECUTE PROCEDURE water_name_marine.flag(); +EXECUTE PROCEDURE water_name_marine.flag(); CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON water_name_marine.updates + AFTER INSERT + ON water_name_marine.updates INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE water_name_marine.refresh(); +EXECUTE PROCEDURE water_name_marine.refresh(); diff --git a/layers/water_name/update_water_lakeline.sql b/layers/water_name/update_water_lakeline.sql index 3136d8edb..fccc317e9 100644 --- a/layers/water_name/update_water_lakeline.sql +++ b/layers/water_name/update_water_lakeline.sql @@ -1,53 +1,94 @@ -DROP TRIGGER IF EXISTS trigger_flag_line ON osm_water_polygon; -DROP TRIGGER IF EXISTS trigger_refresh ON water_lakeline.updates; +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 -DROP MATERIALIZED VIEW IF EXISTS osm_water_lakeline CASCADE; - -CREATE MATERIALIZED VIEW osm_water_lakeline 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) -); -CREATE INDEX IF NOT EXISTS osm_water_lakeline_geometry_idx ON osm_water_lakeline USING gist(geometry); +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 TABLE IF NOT EXISTS water_lakeline.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION water_lakeline.flag() RETURNS trigger AS $$ +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 - INSERT INTO water_lakeline.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + 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.refresh() RETURNS trigger AS - $BODY$ - BEGIN - RAISE LOG 'Refresh water_lakeline'; - REFRESH MATERIALIZED VIEW osm_water_lakeline; - DELETE FROM water_lakeline.updates; - RETURN null; - END; - $BODY$ -language plpgsql; - -CREATE TRIGGER trigger_flag_line - AFTER INSERT OR UPDATE OR DELETE ON osm_water_polygon - FOR EACH STATEMENT - EXECUTE PROCEDURE water_lakeline.flag(); - -CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON water_lakeline.updates - INITIALLY DEFERRED +$$ 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.refresh(); +EXECUTE PROCEDURE water_lakeline.insert(); diff --git a/layers/water_name/update_water_point.sql b/layers/water_name/update_water_point.sql index 33e6d9bde..5accc8042 100644 --- a/layers/water_name/update_water_point.sql +++ b/layers/water_name/update_water_point.sql @@ -1,53 +1,95 @@ -DROP TRIGGER IF EXISTS trigger_flag_point ON osm_water_polygon; -DROP TRIGGER IF EXISTS trigger_refresh ON water_point.updates; +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 -DROP MATERIALIZED VIEW IF EXISTS osm_water_point CASCADE; - -CREATE MATERIALIZED VIEW osm_water_point AS ( - SELECT - wp.osm_id, ST_PointOnSurface(ST_MakeValid(wp.geometry)) AS geometry, - wp.name, wp.name_en, wp.name_de, - update_tags(wp.tags, ST_PointOnSurface(ST_MakeValid(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 <> '' -); +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 TABLE IF NOT EXISTS water_point.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION water_point.flag() RETURNS trigger AS $$ +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 - INSERT INTO water_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + 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.refresh() RETURNS trigger AS - $BODY$ - BEGIN - RAISE LOG 'Refresh water_point'; - REFRESH MATERIALIZED VIEW osm_water_point; - DELETE FROM water_point.updates; - RETURN null; - END; - $BODY$ -language plpgsql; - -CREATE TRIGGER trigger_flag_point - AFTER INSERT OR UPDATE OR DELETE ON osm_water_polygon - FOR EACH STATEMENT - EXECUTE PROCEDURE water_point.flag(); - -CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON water_point.updates - INITIALLY DEFERRED +$$ 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.refresh(); +EXECUTE PROCEDURE water_point.insert(); diff --git a/layers/water_name/layer.sql b/layers/water_name/water_name.sql similarity index 55% rename from layers/water_name/layer.sql rename to layers/water_name/water_name.sql index e0b60cd16..41ed54358 100644 --- a/layers/water_name/layer.sql +++ b/layers/water_name/water_name.sql @@ -1,57 +1,79 @@ - -- etldoc: layer_water_name[shape=record fillcolor=lightpink, style="rounded,filled", -- etldoc: label="layer_water_name | z0_8 | z9_13 | z14+" ] ; CREATE OR REPLACE FUNCTION layer_water_name(bbox geometry, zoom_level integer) -RETURNS TABLE(osm_id bigint, geometry geometry, name text, name_en text, name_de text, tags hstore, class text, intermittent int) AS $$ + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + name text, + name_en text, + name_de text, + tags hstore, + class text, + intermittent int + ) +AS +$$ +SELECT -- etldoc: osm_water_lakeline -> layer_water_name:z9_13 -- etldoc: osm_water_lakeline -> layer_water_name:z14_ - SELECT - CASE WHEN osm_id<0 THEN -osm_id*10+4 - ELSE osm_id*10+1 - END AS osm_id_hash, - geometry, name, + CASE + WHEN osm_id < 0 THEN -osm_id * 10 + 4 + ELSE osm_id * 10 + 1 + END AS osm_id_hash, + geometry, + name, COALESCE(NULLIF(name_en, ''), name) AS name_en, COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, tags, 'lake'::text AS class, is_intermittent::int AS intermittent - FROM osm_water_lakeline - WHERE geometry && bbox - AND ((zoom_level BETWEEN 9 AND 13 AND LineLabel(zoom_level, NULLIF(name, ''), geometry)) - OR (zoom_level >= 14)) +FROM osm_water_lakeline +WHERE geometry && bbox + AND ((zoom_level BETWEEN 9 AND 13 AND LineLabel(zoom_level, NULLIF(name, ''), geometry)) + OR (zoom_level >= 14)) +UNION ALL +SELECT -- etldoc: osm_water_point -> layer_water_name:z9_13 -- etldoc: osm_water_point -> layer_water_name:z14_ - UNION ALL - SELECT - CASE WHEN osm_id<0 THEN -osm_id*10+4 - ELSE osm_id*10+1 - END AS osm_id_hash, - geometry, name, + CASE + WHEN osm_id < 0 THEN -osm_id * 10 + 4 + ELSE osm_id * 10 + 1 + END AS osm_id_hash, + geometry, + name, COALESCE(NULLIF(name_en, ''), name) AS name_en, COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, tags, 'lake'::text AS class, is_intermittent::int AS intermittent - FROM osm_water_point - WHERE geometry && bbox AND ( - (zoom_level BETWEEN 9 AND 13 AND area > 70000*2^(20-zoom_level)) +FROM osm_water_point +WHERE geometry && bbox + AND ( + (zoom_level BETWEEN 9 AND 13 AND area > 70000 * 2 ^ (20 - zoom_level)) OR (zoom_level >= 14) ) +UNION ALL +SELECT -- etldoc: osm_marine_point -> layer_water_name:z0_8 -- etldoc: osm_marine_point -> layer_water_name:z9_13 -- etldoc: osm_marine_point -> layer_water_name:z14_ - UNION ALL - SELECT osm_id*10, geometry, name, + osm_id * 10 AS osm_id_hash, + geometry, + name, COALESCE(NULLIF(name_en, ''), name) AS name_en, COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, tags, place::text AS class, is_intermittent::int AS intermittent - FROM osm_marine_point - WHERE geometry && bbox AND ( +FROM osm_marine_point +WHERE geometry && bbox + AND ( place = 'ocean' OR (zoom_level >= "rank" AND "rank" IS NOT NULL) OR (zoom_level >= 8) ); -$$ LANGUAGE SQL IMMUTABLE; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/water_name/water_name.yaml b/layers/water_name/water_name.yaml index 68a7f7ef0..e47490552 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,11 @@ 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` and `sea`. values: - lake + - sea + - ocean intermittent: description: | Mark with `1` if it is an [intermittent](http://wiki.openstreetmap.org/wiki/Key:intermittent) lake. @@ -29,7 +35,7 @@ schema: - ./update_marine_point.sql - ./update_water_lakeline.sql - ./update_water_point.sql - - ./layer.sql + - ./water_name.sql datasources: - type: imposm3 mapping_file: ../water/mapping.yaml diff --git a/layers/waterway/etl_diagram.png b/layers/waterway/etl_diagram.png index e2e229deb..e88b513d5 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 2255b1659..cecd114bd 100644 --- a/layers/waterway/mapping.yaml +++ b/layers/waterway/mapping.yaml @@ -1,20 +1,3 @@ -generalized_tables: - # etldoc: imposm3 -> osm_waterway_linestring_gen3 - waterway_linestring_gen3: - source: waterway_linestring_gen2 - sql_filter: waterway IN ('river') - tolerance: ZRES9 - # etldoc: imposm3 -> osm_waterway_linestring_gen2 - waterway_linestring_gen2: - source: waterway_linestring_gen1 - sql_filter: waterway IN ('river') - tolerance: ZRES10 - # etldoc: imposm3 -> osm_waterway_linestring_gen1 - waterway_linestring_gen1: - source: waterway_linestring - sql_filter: waterway IN ('river') AND ST_IsValid(geometry) - tolerance: ZRES11 - tunnel_field: &tunnel key: tunnel name: is_tunnel @@ -28,7 +11,7 @@ tables: # etldoc: imposm3 -> osm_waterway_linestring waterway_linestring: type: linestring - fields: + columns: - name: osm_id type: id - name: geometry @@ -59,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 cd6c0b36c..45b61feb1 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 000000000..c13162a0a --- /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": 103 + }, + { + "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": 104 + }, + { + "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": 142 + } + ] +} \ No newline at end of file diff --git a/layers/waterway/update_important_waterway.sql b/layers/waterway/update_important_waterway.sql index 1c6f2c5de..28eabe600 100644 --- a/layers/waterway/update_important_waterway.sql +++ b/layers/waterway/update_important_waterway.sql @@ -1,95 +1,269 @@ +DROP TRIGGER IF EXISTS trigger_important_waterway_linestring ON osm_important_waterway_linestring; +DROP TRIGGER IF EXISTS trigger_store ON osm_waterway_linestring; +DROP TRIGGER IF EXISTS trigger_flag ON osm_waterway_linestring; DROP TRIGGER IF EXISTS trigger_refresh ON waterway_important.updates; -- We merge the waterways by name like the highways -- This helps to drop not important rivers (since they do not have a name) -- and also makes it possible to filter out too short rivers +CREATE INDEX IF NOT EXISTS osm_waterway_linestring_waterway_partial_idx + ON osm_waterway_linestring ((true)) + WHERE name <> '' + AND waterway = 'river' + AND ST_IsValid(geometry); + +CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring ( + id SERIAL PRIMARY KEY, + geometry geometry, + name varchar, + name_en varchar, + name_de varchar, + tags hstore +); + -- etldoc: osm_waterway_linestring -> osm_important_waterway_linestring -DROP MATERIALIZED VIEW IF EXISTS osm_important_waterway_linestring CASCADE; -DROP MATERIALIZED VIEW IF EXISTS osm_important_waterway_linestring_gen1 CASCADE; -DROP MATERIALIZED VIEW IF EXISTS osm_important_waterway_linestring_gen2 CASCADE; -DROP MATERIALIZED VIEW IF EXISTS osm_important_waterway_linestring_gen3 CASCADE; +INSERT INTO osm_important_waterway_linestring (geometry, name, name_en, name_de, tags) +SELECT (ST_Dump(geometry)).geom AS geometry, + name, + name_en, + name_de, + 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_geometry_idx ON osm_important_waterway_linestring USING gist (geometry); -CREATE INDEX IF NOT EXISTS osm_waterway_linestring_waterway_partial_idx - ON osm_waterway_linestring(waterway) - WHERE waterway = 'river'; +CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring_gen_z11 +(LIKE osm_important_waterway_linestring); -CREATE INDEX IF NOT EXISTS osm_waterway_linestring_name_partial_idx - ON osm_waterway_linestring(name) - WHERE name <> ''; +CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring_gen_z10 +(LIKE osm_important_waterway_linestring_gen_z11); -CREATE MATERIALIZED VIEW osm_important_waterway_linestring AS ( - SELECT - (ST_Dump(geometry)).geom AS geometry, - name, name_en, name_de, 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_geometry_idx ON osm_important_waterway_linestring USING gist(geometry); +CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring_gen_z9 +(LIKE osm_important_waterway_linestring_gen_z10); --- etldoc: osm_important_waterway_linestring -> osm_important_waterway_linestring_gen1 -CREATE MATERIALIZED VIEW osm_important_waterway_linestring_gen1 AS ( - SELECT ST_Simplify(geometry, 60) AS geometry, name, name_en, name_de, tags +CREATE OR REPLACE FUNCTION insert_important_waterway_linestring_gen(update_id bigint) RETURNS void AS +$$ +BEGIN + -- 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 ST_Length(geometry) > 1000 -); -CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen1_geometry_idx ON osm_important_waterway_linestring_gen1 USING gist(geometry); + WHERE + (update_id IS NULL OR id = update_id) AND + ST_Length(geometry) > 1000; --- etldoc: osm_important_waterway_linestring_gen1 -> osm_important_waterway_linestring_gen2 -CREATE MATERIALIZED VIEW osm_important_waterway_linestring_gen2 AS ( - SELECT ST_Simplify(geometry, 100) AS geometry, name, name_en, name_de, tags - FROM osm_important_waterway_linestring_gen1 - WHERE ST_Length(geometry) > 4000 -); -CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen2_geometry_idx ON osm_important_waterway_linestring_gen2 USING gist(geometry); + -- 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 + (update_id IS NULL OR id = update_id) AND + ST_Length(geometry) > 4000; + + -- 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 + (update_id IS NULL OR id = update_id) AND + ST_Length(geometry) > 8000; +END; +$$ LANGUAGE plpgsql; + +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(NULL); + +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); --- etldoc: osm_important_waterway_linestring_gen2 -> osm_important_waterway_linestring_gen3 -CREATE MATERIALIZED VIEW osm_important_waterway_linestring_gen3 AS ( - SELECT ST_Simplify(geometry, 200) AS geometry, name, name_en, name_de, tags - FROM osm_important_waterway_linestring_gen2 - WHERE ST_Length(geometry) > 8000 -); -CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen3_geometry_idx ON osm_important_waterway_linestring_gen3 USING gist(geometry); -- Handle updates CREATE SCHEMA IF NOT EXISTS waterway_important; -CREATE TABLE IF NOT EXISTS waterway_important.updates(id serial primary key, t text, unique (t)); -CREATE OR REPLACE FUNCTION waterway_important.flag() RETURNS trigger AS $$ +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 +); +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)); + 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)); + END IF; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE IF NOT EXISTS waterway_important.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION waterway_important.flag() RETURNS trigger AS +$$ BEGIN - INSERT INTO waterway_important.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; - RETURN null; + INSERT INTO waterway_important.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; END; -$$ language plpgsql; +$$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION waterway_important.refresh() RETURNS trigger AS - $BODY$ - BEGIN +$$ +DECLARE + t TIMESTAMP WITH TIME ZONE := clock_timestamp(); +BEGIN RAISE LOG 'Refresh waterway'; - REFRESH MATERIALIZED VIEW osm_important_waterway_linestring; - REFRESH MATERIALIZED VIEW osm_important_waterway_linestring_gen1; - REFRESH MATERIALIZED VIEW osm_important_waterway_linestring_gen2; - REFRESH MATERIALIZED VIEW osm_important_waterway_linestring_gen3; + + -- 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; + + 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; + + INSERT INTO osm_important_waterway_linestring (geometry, name, name_en, name_de, tags) + SELECT (ST_Dump(geometry)).geom AS geometry, + name, + name_en, + name_de, + tags + 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; + + DROP TABLE changes_compact; + -- noinspection SqlWithoutWhere + DELETE FROM waterway_important.changes; + -- noinspection SqlWithoutWhere DELETE FROM waterway_important.updates; - RETURN null; - END; - $BODY$ -language plpgsql; + + RAISE LOG 'Refresh waterway done in %', age(clock_timestamp(), t); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION waterway_important.important_waterway_linestring_gen_refresh() RETURNS trigger AS +$$ +BEGIN + IF (tg_op = 'DELETE' OR tg_op = 'UPDATE') THEN + DELETE FROM osm_important_waterway_linestring_gen_z11 WHERE id = old.id; + DELETE FROM osm_important_waterway_linestring_gen_z10 WHERE id = old.id; + DELETE FROM osm_important_waterway_linestring_gen_z9 WHERE id = old.id; + END IF; + + IF (tg_op = 'UPDATE' OR tg_op = 'INSERT') THEN + PERFORM insert_important_waterway_linestring_gen(new.id); + END IF; + + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_important_waterway_linestring + AFTER INSERT OR UPDATE OR DELETE + ON osm_important_waterway_linestring + FOR EACH ROW +EXECUTE PROCEDURE waterway_important.important_waterway_linestring_gen_refresh(); + +CREATE TRIGGER trigger_store + AFTER INSERT OR UPDATE OR DELETE + ON osm_waterway_linestring + FOR EACH ROW +EXECUTE PROCEDURE waterway_important.store(); CREATE TRIGGER trigger_flag - AFTER INSERT OR UPDATE OR DELETE ON osm_waterway_linestring + AFTER INSERT OR UPDATE OR DELETE + ON osm_waterway_linestring FOR EACH STATEMENT - EXECUTE PROCEDURE waterway_important.flag(); +EXECUTE PROCEDURE waterway_important.flag(); CREATE CONSTRAINT TRIGGER trigger_refresh - AFTER INSERT ON waterway_important.updates + AFTER INSERT + ON waterway_important.updates INITIALLY DEFERRED FOR EACH ROW - EXECUTE PROCEDURE waterway_important.refresh(); +EXECUTE PROCEDURE waterway_important.refresh(); diff --git a/layers/waterway/update_waterway_linestring.sql b/layers/waterway/update_waterway_linestring.sql index e94c8c5cf..7516f08a2 100644 --- a/layers/waterway/update_waterway_linestring.sql +++ b/layers/waterway/update_waterway_linestring.sql @@ -1,36 +1,29 @@ DROP TRIGGER IF EXISTS trigger_flag ON osm_waterway_linestring; DROP TRIGGER IF EXISTS trigger_refresh ON osm_waterway_linestring; -DO $$ -BEGIN - update osm_waterway_linestring - SET tags = update_tags(tags, geometry); - - update osm_waterway_linestring_gen1 - SET tags = update_tags(tags, geometry); - - update osm_waterway_linestring_gen2 - SET tags = update_tags(tags, geometry); - - update osm_waterway_linestring_gen3 - SET tags = update_tags(tags, geometry); -END $$; +DO +$$ + BEGIN + UPDATE osm_waterway_linestring + SET tags = update_tags(tags, geometry); + END +$$; -- Handle updates CREATE SCHEMA IF NOT EXISTS waterway_linestring; CREATE OR REPLACE FUNCTION waterway_linestring.refresh() RETURNS trigger AS - $BODY$ - BEGIN --- RAISE NOTICE 'Refresh waterway_linestring %', NEW.osm_id; +$$ +BEGIN + -- RAISE NOTICE 'Refresh waterway_linestring %', NEW.osm_id; NEW.tags = update_tags(NEW.tags, NEW.geometry); RETURN NEW; - END; - $BODY$ -language plpgsql; +END; +$$ LANGUAGE plpgsql; CREATE TRIGGER trigger_refresh - BEFORE INSERT OR UPDATE ON osm_waterway_linestring + BEFORE INSERT OR UPDATE + ON osm_waterway_linestring FOR EACH ROW - EXECUTE PROCEDURE waterway_linestring.refresh(); +EXECUTE PROCEDURE waterway_linestring.refresh(); diff --git a/layers/waterway/waterway.sql b/layers/waterway/waterway.sql index 64838f6a8..6b2f7f3ad 100644 --- a/layers/waterway/waterway.sql +++ b/layers/waterway/waterway.sql @@ -1,109 +1,404 @@ -CREATE OR REPLACE FUNCTION waterway_brunnel(is_bridge BOOL, is_tunnel BOOL) RETURNS TEXT AS $$ - SELECT CASE - WHEN is_bridge THEN 'bridge' - WHEN is_tunnel THEN 'tunnel' - ELSE NULL - END; -$$ LANGUAGE SQL IMMUTABLE STRICT; - --- etldoc: ne_110m_rivers_lake_centerlines -> waterway_z3 -CREATE OR REPLACE VIEW waterway_z3 AS ( - SELECT geometry, 'river'::text AS class, NULL::text AS name, NULL::text AS name_en, NULL::text AS name_de, NULL::hstore AS tags, NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, NULL::boolean AS is_intermittent - FROM ne_110m_rivers_lake_centerlines - WHERE featurecla = 'River' -); +CREATE OR REPLACE FUNCTION waterway_brunnel(is_bridge bool, is_tunnel bool) RETURNS text AS +$$ +SELECT CASE + WHEN is_bridge THEN 'bridge' + WHEN is_tunnel THEN 'tunnel' + END; +$$ LANGUAGE SQL IMMUTABLE + STRICT + PARALLEL SAFE; +-- ne_110m_rivers_lake_centerlines +-- etldoc: ne_110m_rivers_lake_centerlines -> ne_110m_rivers_lake_centerlines_gen_z3 +DROP MATERIALIZED VIEW IF EXISTS ne_110m_rivers_lake_centerlines_gen_z3 CASCADE; +CREATE MATERIALIZED VIEW ne_110m_rivers_lake_centerlines_gen_z3 AS +( +SELECT ST_Simplify(geometry, ZRes(5)) as geometry, + 'river'::text AS class, + NULL::text AS name, + NULL::text AS name_en, + NULL::text AS name_de, + NULL::hstore AS tags, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_intermittent +FROM ne_110m_rivers_lake_centerlines +WHERE featurecla = 'River' + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_110m_rivers_lake_centerlines_gen_z3_idx ON ne_110m_rivers_lake_centerlines_gen_z3 USING gist (geometry); --- etldoc: ne_50m_rivers_lake_centerlines -> waterway_z4 -CREATE OR REPLACE VIEW waterway_z4 AS ( - SELECT geometry, 'river'::text AS class, NULL::text AS name, NULL::text AS name_en, NULL::text AS name_de, NULL::hstore AS tags, NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, NULL::boolean AS is_intermittent - FROM ne_50m_rivers_lake_centerlines - WHERE featurecla = 'River' -); +-- ne_50m_rivers_lake_centerlines +-- etldoc: ne_50m_rivers_lake_centerlines -> ne_50m_rivers_lake_centerlines_gen_z5 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_rivers_lake_centerlines_gen_z5 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_rivers_lake_centerlines_gen_z5 AS +( +SELECT ST_Simplify(geometry, ZRes(7)) as geometry, + 'river'::text AS class, + NULL::text AS name, + NULL::text AS name_en, + NULL::text AS name_de, + NULL::hstore AS tags, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_intermittent +FROM ne_50m_rivers_lake_centerlines +WHERE featurecla = 'River' + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_rivers_lake_centerlines_gen_z5_idx ON ne_50m_rivers_lake_centerlines_gen_z5 USING gist (geometry); --- etldoc: ne_10m_rivers_lake_centerlines -> waterway_z6 -CREATE OR REPLACE VIEW waterway_z6 AS ( - SELECT geometry, 'river'::text AS class, NULL::text AS name, NULL::text AS name_en, NULL::text AS name_de, NULL::hstore AS tags, NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, NULL::boolean AS is_intermittent - FROM ne_10m_rivers_lake_centerlines - WHERE featurecla = 'River' -); +-- etldoc: ne_50m_rivers_lake_centerlines_gen_z5 -> ne_50m_rivers_lake_centerlines_gen_z4 +DROP MATERIALIZED VIEW IF EXISTS ne_50m_rivers_lake_centerlines_gen_z4 CASCADE; +CREATE MATERIALIZED VIEW ne_50m_rivers_lake_centerlines_gen_z4 AS +( +SELECT ST_Simplify(geometry, ZRes(6)) as geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM ne_50m_rivers_lake_centerlines_gen_z5 + ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS ne_50m_rivers_lake_centerlines_gen_z4_idx ON ne_50m_rivers_lake_centerlines_gen_z4 USING gist (geometry); --- etldoc: osm_important_waterway_linestring_gen3 -> waterway_z9 -CREATE OR REPLACE VIEW waterway_z9 AS ( - SELECT geometry, 'river'::text AS class, name, name_en, name_de, tags, NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, NULL::boolean AS is_intermittent - FROM osm_important_waterway_linestring_gen3 +-- 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: osm_important_waterway_linestring_gen2 -> waterway_z10 -CREATE OR REPLACE VIEW waterway_z10 AS ( - SELECT geometry, 'river'::text AS class, name, name_en, name_de, tags, NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, NULL::boolean AS is_intermittent - FROM osm_important_waterway_linestring_gen2 -); +-- etldoc: waterway_relation -> waterway_relation_gen_z8 +DROP MATERIALIZED VIEW IF EXISTS waterway_relation_gen_z8 CASCADE; +CREATE MATERIALIZED VIEW waterway_relation_gen_z8 AS ( + SELECT ST_Simplify(geometry, ZRes(10)) AS geometry, + 'river'::text AS class, + name, + NULL::text AS name_en, + NULL::text AS name_de, + tags, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_intermittent + FROM waterway_relation + WHERE ST_Length(geometry) > 300000 +) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS waterway_relation_gen_z8_geometry_idx ON waterway_relation_gen_z8 USING gist (geometry); --- etldoc:osm_important_waterway_linestring_gen1 -> waterway_z11 -CREATE OR REPLACE VIEW waterway_z11 AS ( - SELECT geometry, 'river'::text AS class, name, name_en, name_de, tags, NULL::boolean AS is_bridge, NULL::boolean AS is_tunnel, NULL::boolean AS is_intermittent - FROM osm_important_waterway_linestring_gen1 -); +-- etldoc: waterway_relation_gen_z8 -> waterway_relation_gen_z7 +DROP MATERIALIZED VIEW IF EXISTS waterway_relation_gen_z7 CASCADE; +CREATE MATERIALIZED VIEW waterway_relation_gen_z7 AS ( + SELECT ST_Simplify(geometry, ZRes(9)) AS geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent + FROM waterway_relation_gen_z8 + WHERE ST_Length(geometry) > 400000 +) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS waterway_relation_gen_z7_geometry_idx ON waterway_relation_gen_z7 USING gist (geometry); + +-- etldoc: waterway_relation_gen_z7 -> waterway_relation_gen_z6 +DROP MATERIALIZED VIEW IF EXISTS waterway_relation_gen_z6 CASCADE; +CREATE MATERIALIZED VIEW waterway_relation_gen_z6 AS ( + SELECT ST_Simplify(geometry, ZRes(8)) AS geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent + FROM waterway_relation_gen_z7 + WHERE ST_Length(geometry) > 500000 +) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; +CREATE INDEX IF NOT EXISTS waterway_relation_gen_z6_geometry_idx ON waterway_relation_gen_z6 USING gist (geometry); + + +-- etldoc: ne_110m_rivers_lake_centerlines_gen_z3 -> waterway_z3 +CREATE OR REPLACE VIEW waterway_z3 AS +( +SELECT geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM ne_110m_rivers_lake_centerlines_gen_z3 + ); + +-- etldoc: ne_50m_rivers_lake_centerlines_gen_z4 -> waterway_z4 +CREATE OR REPLACE VIEW waterway_z4 AS +( +SELECT geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM ne_50m_rivers_lake_centerlines_gen_z4 + ); + +-- etldoc: ne_50m_rivers_lake_centerlines_gen_z5 -> waterway_z5 +CREATE OR REPLACE VIEW waterway_z5 AS +( +SELECT geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM ne_50m_rivers_lake_centerlines_gen_z5 + ); + +-- etldoc: waterway_relation_gen_z6 -> waterway_z6 +CREATE OR REPLACE VIEW waterway_z6 AS +( +SELECT geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM waterway_relation_gen_z6 + ); + +-- etldoc: waterway_relation_gen_z7 -> waterway_z7 +CREATE OR REPLACE VIEW waterway_z7 AS +( +SELECT geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM waterway_relation_gen_z7 + ); + +-- etldoc: waterway_relation_gen_z8 -> waterway_z8 +CREATE OR REPLACE VIEW waterway_z8 AS +( +SELECT geometry, + class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM waterway_relation_gen_z8 + ); + +-- etldoc: osm_important_waterway_linestring_gen_z9 -> waterway_z9 +CREATE OR REPLACE VIEW waterway_z9 AS +( +SELECT geometry, + 'river'::text AS class, + name, + name_en, + name_de, + tags, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_intermittent +FROM osm_important_waterway_linestring_gen_z9 + ); + +-- etldoc: osm_important_waterway_linestring_gen_z10 -> waterway_z10 +CREATE OR REPLACE VIEW waterway_z10 AS +( +SELECT geometry, + 'river'::text AS class, + name, + name_en, + name_de, + tags, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_intermittent +FROM osm_important_waterway_linestring_gen_z10 + ); + +-- etldoc:osm_important_waterway_linestring_gen_z11 -> waterway_z11 +CREATE OR REPLACE VIEW waterway_z11 AS +( +SELECT geometry, + 'river'::text AS class, + name, + name_en, + name_de, + tags, + NULL::boolean AS is_bridge, + NULL::boolean AS is_tunnel, + NULL::boolean AS is_intermittent +FROM osm_important_waterway_linestring_gen_z11 + ); -- etldoc: osm_waterway_linestring -> waterway_z12 -CREATE OR REPLACE VIEW waterway_z12 AS ( - SELECT geometry, waterway::text AS class, name, name_en, name_de, tags, is_bridge, is_tunnel, is_intermittent - FROM osm_waterway_linestring - WHERE waterway IN ('river', 'canal') -); +CREATE OR REPLACE VIEW waterway_z12 AS +( +SELECT geometry, + waterway::text AS class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM osm_waterway_linestring +WHERE waterway IN ('river', 'canal') + ); -- etldoc: osm_waterway_linestring -> waterway_z13 -CREATE OR REPLACE VIEW waterway_z13 AS ( - SELECT geometry, waterway::text AS class, name, name_en, name_de, tags, is_bridge, is_tunnel, is_intermittent - FROM osm_waterway_linestring - WHERE waterway IN ('river', 'canal', 'stream', 'drain', 'ditch') -); +CREATE OR REPLACE VIEW waterway_z13 AS +( +SELECT geometry, + waterway::text AS class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM osm_waterway_linestring +WHERE waterway IN ('river', 'canal', 'stream', 'drain', 'ditch') + ); -- etldoc: osm_waterway_linestring -> waterway_z14 -CREATE OR REPLACE VIEW waterway_z14 AS ( - SELECT geometry, waterway::text AS class, name, name_en, name_de, tags, is_bridge, is_tunnel, is_intermittent - FROM osm_waterway_linestring -); +CREATE OR REPLACE VIEW waterway_z14 AS +( +SELECT geometry, + waterway::text AS class, + name, + name_en, + name_de, + tags, + is_bridge, + is_tunnel, + is_intermittent +FROM osm_waterway_linestring + ); -- etldoc: layer_waterway[shape=record fillcolor=lightpink, style="rounded,filled", --- etldoc: label="layer_waterway | z3 | z4-z5 | z6-8 | z9 | z10 | z11 | z12| z13| z14+" ]; +-- etldoc: label="layer_waterway | z3 | z4 | z5 | z6 | z7 | z8 | z9 | z10 | z11 | z12| z13| z14+" ]; CREATE OR REPLACE FUNCTION layer_waterway(bbox geometry, zoom_level int) -RETURNS TABLE(geometry geometry, class text, name text, name_en text, name_de text, brunnel text, intermittent int, tags hstore) AS $$ - SELECT geometry, class, - NULLIF(name, '') AS name, - COALESCE(NULLIF(name_en, ''), name) AS name_en, - COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, - waterway_brunnel(is_bridge, is_tunnel) AS brunnel, - is_intermittent::int AS intermittent, - tags - FROM ( - -- etldoc: waterway_z3 -> layer_waterway:z3 - SELECT * FROM waterway_z3 WHERE zoom_level = 3 - UNION ALL - -- etldoc: waterway_z4 -> layer_waterway:z4_5 - SELECT * FROM waterway_z4 WHERE zoom_level BETWEEN 4 AND 5 - UNION ALL - -- etldoc: waterway_z6 -> layer_waterway:z6_8 - SELECT * FROM waterway_z6 WHERE zoom_level BETWEEN 6 AND 8 - UNION ALL - -- etldoc: waterway_z9 -> layer_waterway:z9 - SELECT * FROM waterway_z9 WHERE zoom_level = 9 - UNION ALL - -- etldoc: waterway_z10 -> layer_waterway:z10 - SELECT * FROM waterway_z10 WHERE zoom_level = 10 - UNION ALL - -- etldoc: waterway_z11 -> layer_waterway:z11 - SELECT * FROM waterway_z11 WHERE zoom_level = 11 - UNION ALL - -- etldoc: waterway_z12 -> layer_waterway:z12 - SELECT * FROM waterway_z12 WHERE zoom_level = 12 - UNION ALL - -- etldoc: waterway_z13 -> layer_waterway:z13 - SELECT * FROM waterway_z13 WHERE zoom_level = 13 - UNION ALL - -- etldoc: waterway_z14 -> layer_waterway:z14 - SELECT * FROM waterway_z14 WHERE zoom_level >= 14 - ) AS zoom_levels - WHERE geometry && bbox; -$$ LANGUAGE SQL IMMUTABLE; + RETURNS TABLE + ( + geometry geometry, + class text, + name text, + name_en text, + name_de text, + brunnel text, + intermittent int, + tags hstore + ) +AS +$$ +SELECT geometry, + class, + NULLIF(name, '') AS name, + COALESCE(NULLIF(name_en, ''), NULLIF(name, '')) AS name_en, + COALESCE(NULLIF(name_de, ''), NULLIF(name, ''), NULLIF(name_en, '')) AS name_de, + waterway_brunnel(is_bridge, is_tunnel) AS brunnel, + is_intermittent::int AS intermittent, + tags +FROM ( + -- etldoc: waterway_z3 -> layer_waterway:z3 + SELECT * + FROM waterway_z3 + WHERE zoom_level = 3 + UNION ALL + -- etldoc: waterway_z4 -> layer_waterway:z4 + SELECT * + FROM waterway_z4 + WHERE zoom_level = 4 + UNION ALL + -- etldoc: waterway_z5 -> layer_waterway:z5 + SELECT * + FROM waterway_z5 + WHERE zoom_level = 5 + UNION ALL + -- etldoc: waterway_z6 -> layer_waterway:z6 + SELECT * + FROM waterway_z6 + WHERE zoom_level = 6 + UNION ALL + -- etldoc: waterway_z7 -> layer_waterway:z7 + SELECT * + FROM waterway_z7 + WHERE zoom_level = 7 + UNION ALL + -- etldoc: waterway_z8 -> layer_waterway:z8 + SELECT * + FROM waterway_z8 + WHERE zoom_level = 8 + UNION ALL + -- etldoc: waterway_z9 -> layer_waterway:z9 + SELECT * + FROM waterway_z9 + WHERE zoom_level = 9 + UNION ALL + -- etldoc: waterway_z10 -> layer_waterway:z10 + SELECT * + FROM waterway_z10 + WHERE zoom_level = 10 + UNION ALL + -- etldoc: waterway_z11 -> layer_waterway:z11 + SELECT * + FROM waterway_z11 + WHERE zoom_level = 11 + UNION ALL + -- etldoc: waterway_z12 -> layer_waterway:z12 + SELECT * + FROM waterway_z12 + WHERE zoom_level = 12 + UNION ALL + -- etldoc: waterway_z13 -> layer_waterway:z13 + SELECT * + FROM waterway_z13 + WHERE zoom_level = 13 + UNION ALL + -- etldoc: waterway_z14 -> layer_waterway:z14 + SELECT * + FROM waterway_z14 + WHERE zoom_level >= 14 + ) AS zoom_levels +WHERE geometry && bbox; +$$ LANGUAGE SQL STABLE + -- STRICT + PARALLEL SAFE; diff --git a/layers/waterway/waterway.yaml b/layers/waterway/waterway.yaml index b05dbad18..53ec3bcec 100644 --- a/layers/waterway/waterway.yaml +++ b/layers/waterway/waterway.yaml @@ -1,5 +1,12 @@ layer: id: "waterway" + # waterway relies on the function waterway_brunnel() defined in water layer + requires: + layers: + - water + tables: + - ne_110m_rivers_lake_centerlines + - ne_50m_rivers_lake_centerlines description: | OpenStreetMap [waterways](https://wiki.openstreetmap.org/wiki/Waterways) for higher zoom levels (z9 and more) and Natural Earth rivers and lake centerlines for low zoom levels (z3 - z8). diff --git a/openmaptiles.yaml b/openmaptiles.yaml index 542574c37..0758eab89 100644 --- a/openmaptiles.yaml +++ b/openmaptiles.yaml @@ -17,20 +17,20 @@ tileset: - layers/poi/poi.yaml - layers/aerodrome_label/aerodrome_label.yaml name: OpenMapTiles - version: 3.11.0 + version: 3.14.0 id: openmaptiles description: "A tileset showcasing all layers in OpenMapTiles. https://openmaptiles.org" attribution: '© OpenMapTiles © OpenStreetMap contributors' - center: [-12.2168, 28.6135, 4] + center: [0, 0, 1] bounds: [-180.0,-85.0511,180.0,85.0511] maxzoom: 14 minzoom: 0 pixel_scale: 256 languages: - am # Amharic - - ar # Arabic + - ar # Arabic - az # Azerbaijani, Latin - - be # Belarusian + - be # Belorussian - bg # Bulgarian - br # Breton, Latin - bs # Bosnian, Latin @@ -52,6 +52,7 @@ tileset: - ga # Irish, Latin - gd # Scottish Gaelic, Latin - he # Hebrew + - hi # Hindi - hr # Croatian, Latin - hu # Hungarian, Latin - hy # Armenian @@ -61,11 +62,13 @@ tileset: - ja # Japanese - ja_kana # Japanese Kana form - ja_rm # romanization of Japanese, Latin + - ja-Latn # romanisation of Japanese, Latin since 2018 + - ja-Hira # Japanese Hiragana form - ka # Georgian - kk # Kazakh - kn # Kannada - ko # Korean - - ko_rm # romanization of Korean, Latin + - ko-Latn # romanization of Korean, Latin - ku # Kurdish, Latin - la # Latin, Latin - lb # Luxembourgish, Latin @@ -88,6 +91,8 @@ tileset: - sr # Serbian, Cyrillic - sr-Latn # Serbian, Latin - sv # Swedish, Latin + - ta # Tamil + - te # Telugu - th # Thai - tr # Turkish, Latin - uk # Ukrainian diff --git a/openmaptiles_base.yaml b/openmaptiles_base.yaml index 6f5e1abf5..4deaa1da4 100644 --- a/openmaptiles_base.yaml +++ b/openmaptiles_base.yaml @@ -13,6 +13,7 @@ tileset: - layers/transportation_name/transportation_name.yaml - layers/place/place.yaml - layers/aerodrome_label/aerodrome_label.yaml + - layers/housenumber/housenumber.yaml name: OpenMapTiles version: 3.3.0 id: openmaptiles diff --git a/qa/layer_freq.sh b/qa/layer_freq.sh deleted file mode 100755 index 60c8a2c62..000000000 --- a/qa/layer_freq.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -set -o errexit -set -o pipefail -set -o nounset - -layerid=$1 -classvars=$2 - -echo "# FREQ - $layerid group by : $classvars " - -for z in {0..15} -do -echo " " -echo "## $layerid z$z - freq" - -SQL=$(docker-compose run --rm openmaptiles-tools generate-sqlquery layers/${layerid}/${layerid}.yaml $z ) - -SQLCODE=$(cat <<-END -select $classvars , count(*) as _count_ from -( $SQL ) as t -GROUP BY $classvars -ORDER BY $classvars -; -END -) - -#echo "\`\`\`sql" -#echo "$SQLCODE" -#echo "\`\`\`" - -docker-compose run --rm import-osm /usr/src/app/psql.sh -q -P pager=off -P border=2 -P footer=off -P null='(null)' -c "$SQLCODE" \ - | sed '1d;$d' | sed '$d' | sed 's/+--/|--/g' | sed 's/--+/--|/g' - -done - diff --git a/qa/layer_numvar_analyze.sh b/qa/layer_numvar_analyze.sh deleted file mode 100755 index 614b4e141..000000000 --- a/qa/layer_numvar_analyze.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -set -o errexit -set -o pipefail -set -o nounset - -layerid=$1 -var=$2 - -echo "# NUMVAR-ANALYZE - $layerid - $var " - -for z in {0..15} -do -echo " " -echo "## $layerid z$z - $var " - -SQL=$(docker-compose run --rm openmaptiles-tools generate-sqlquery layers/${layerid}/${layerid}.yaml $z ) - -SQLCODE=$(cat <<-END -SELECT - count($var) as count - ,min($var) as min - ,max($var) as max - ,avg($var) as avg - ,stddev($var) as stddev - ,variance($var) as variance -FROM -( $SQL ) as t -; -END -) - - -#echo "\`\`\`sql" -#echo "$SQLCODE" -#echo "\`\`\`" - -docker-compose run --rm import-osm /usr/src/app/psql.sh -q -P pager=off -P border=2 -P footer=off -P null='(null)' -c "$SQLCODE" \ - | sed '1d;$d' | sed '$d' | sed 's/+--/|--/g' | sed 's/--+/--|/g' - -done diff --git a/qa/layer_toplength.sh b/qa/layer_toplength.sh deleted file mode 100755 index 86a529eb8..000000000 --- a/qa/layer_toplength.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -set -o errexit -set -o pipefail -set -o nounset - -layerid=$1 -classvar=$2 - -for z in {0..15} -do -echo " " -echo "## $layerid z$z max length ($classvar)" - -SQL=$(docker-compose run --rm openmaptiles-tools generate-sqlquery layers/${layerid}/${layerid}.yaml $z ) - -SQLCODE=$(cat <<-END -SELECT DISTINCT $classvar , length( $classvar ) AS _length_ from -( $SQL ) as t -WHERE length( $classvar ) > 0 -ORDER BY length( $classvar ) DESC NULLS LAST -LIMIT 30 -; -END -) - -#echo "\`\`\`sql" -#echo "$SQLCODE" -#echo "\`\`\`" - -docker-compose run --rm import-osm /usr/src/app/psql.sh -q -P pager=off -P border=2 -P footer=off -P null='(null)' -c "$SQLCODE" \ - | sed '1d;$d' | sed '$d' | sed 's/+--/|--/g' | sed 's/--+/--|/g' - -done - diff --git a/qa/run.sh b/qa/run.sh deleted file mode 100755 index f0a1b961c..000000000 --- a/qa/run.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash -set -o errexit -set -o pipefail -set -o nounset - -# example call from the parent folder : ./qa/run.sh -# or make generate-qareports - -# ---- freq --------------------------------------------------------------- - -mkdir -p ./build/qareports -rm -f ./build/qareports/*.md - -# ----- - -./qa/layer_freq.sh aeroway "class" > ./build/qareports/freq_aeroway__class.md - -###Todo:./qa/layer_freq.sh boundary "admin_level,disputed" > ./build/qareports/freq_boundary__admin_level_disputed.md -./qa/layer_freq.sh boundary "admin_level" > ./build/qareports/freq_boundary__admin_level.md -###Todo: ./qa/layer_freq.sh boundary "disputed" > ./build/qareports/freq_boundary__disputed.md - -./qa/layer_freq.sh building "render_min_height" > ./build/qareports/freq_building__render_min_height.md - -#./qa/layer_freq.sh housenumber "housenumber " > ./build/qadocfreq_housenumber.md -./qa/layer_freq.sh landcover "class, subclass" > ./build/qareports/freq_landcover__class_subclass.md - -./qa/layer_freq.sh landuse "class " > ./build/qareports/freq_landuse__class.md - -./qa/layer_freq.sh park "class " > ./build/qareports/freq_park__class.md - -./qa/layer_freq.sh place "class " > ./build/qareports/freq_place__class.md -./qa/layer_freq.sh place "class,capital " > ./build/qareports/freq_place__class_capital.md -./qa/layer_freq.sh place "capital " > ./build/qareports/freq_place__capital.md -./qa/layer_freq.sh place "class,capital,rank " > ./build/qareports/freq_place__class_capital_rank.md -./qa/layer_freq.sh place "rank " > ./build/qareports/freq_place__rank.md - -./qa/layer_freq.sh poi "class " > ./build/qareports/freq_poi__class.md -./qa/layer_freq.sh poi "subclass " > ./build/qareports/freq_poi__subclass.md -./qa/layer_freq.sh poi "rank " > ./build/qareports/freq_poi__rank.md -./qa/layer_freq.sh poi "class,subclass " > ./build/qareports/freq_poi__class_subclass.md -./qa/layer_freq.sh poi "class,subclass,rank" > ./build/qareports/freq_poi__class_subclass_rank.md -./qa/layer_freq.sh poi "class,rank " > ./build/qareports/freq_poi__class_rank.md - -./qa/layer_freq.sh transportation "class, oneway, ramp, brunnel, service" > ./build/qareports/freq_transportation__class_oneway_ramp_brunnel_service.md -./qa/layer_freq.sh transportation "oneway, ramp, brunnel, service " > ./build/qareports/freq_transportation__oneway_ramp_brunnel_service.md -./qa/layer_freq.sh transportation "class " > ./build/qareports/freq_transportation__class.md - -./qa/layer_freq.sh transportation_name "class " > ./build/qareports/freq_transportation_name__class.md -./qa/layer_freq.sh transportation_name "ref_length" > ./build/qareports/freq_transportation_name__ref_length.md - -./qa/layer_freq.sh water "class " > ./build/qareports/freq_water__class.md - -./qa/layer_freq.sh water_name "class " > ./build/qareports/freq_water_name__class.md - -./qa/layer_freq.sh waterway "class " > ./build/qareports/freq_waterway__class.md - -# ---- toplength ------------------------------------------- - -./qa/layer_toplength.sh housenumber "housenumber" > ./build/qareports/toplength_housenumber__housenumber.md - -./qa/layer_toplength.sh place "name" > ./build/qareports/toplength_place__name.md -./qa/layer_toplength.sh place "name_en" > ./build/qareports/toplength_place__name_en.md - -./qa/layer_toplength.sh poi "name" > ./build/qareports/toplength_poi__name.md -./qa/layer_toplength.sh poi "name_en" > ./build/qareports/toplength_poi__name_en.md - -./qa/layer_toplength.sh transportation_name "name" > ./build/qareports/toplength_transportation_name__name.md -./qa/layer_toplength.sh transportation_name "ref" > ./build/qareports/toplength_transportation_name__ref.md -###Todo: ./qa/layer_toplength.sh transportation_name "network" > ./build/qareports/toplength_transportation_name__network.md - -./qa/layer_toplength.sh water_name "name" > ./build/qareports/toplength_water_name__name.md -./qa/layer_toplength.sh water_name "name_en" > ./build/qareports/toplength_water_name__name_en.md - -./qa/layer_toplength.sh waterway "name" > ./build/qareports/toplength_waterway__name.md - - -# ---- numvar analyze ------------------------------------- - -./qa/layer_numvar_analyze.sh building "render_min_height" > ./build/qareports/numvara_building__render_min_height.md -./qa/layer_numvar_analyze.sh building "render_height" > ./build/qareports/numvara_building__render_height.md - -./qa/layer_numvar_analyze.sh transportation_name "ref_length" > ./build/qareports/numvara_transportation_name__ref_length.md - diff --git a/qa/table_sizes.py b/qa/table_sizes.py deleted file mode 100755 index e005b0e15..000000000 --- a/qa/table_sizes.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python -import sys -import argparse -import subprocess - -parser = argparse.ArgumentParser() -parser.add_argument('--noan', action='store_true', help='Not to run make psql-analyze') - -TOTAL_SIZE_SQL = """SELECT - pg_size_pretty(sum(size)) AS size -FROM ( - SELECT - relname as "Table", - pg_total_relation_size(relid) as "size" - FROM pg_catalog.pg_statio_user_tables - WHERE schemaname='public' -) a -;""".replace('\"', '\\\"') - -TABLE_SIZES_SQL = """SELECT - a.relname as "table", - pg_table_size(a.relid) as "size", - b.n_live_tup as "rows" -FROM pg_catalog.pg_statio_user_tables a - LEFT JOIN pg_stat_user_tables b ON (a.relid = b.relid) -WHERE - a.schemaname='public' -ORDER BY a.relname; -""".replace('\"', '\\\"') - -TABLES_SQL = """SELECT - a.relname -FROM pg_catalog.pg_statio_user_tables a -WHERE - a.schemaname='public' -ORDER BY a.relname; -""" - -COLUMN_NAMES_SQL = """SELECT a.attname -FROM pg_class As c - INNER JOIN pg_attribute As a ON c.oid = a.attrelid - LEFT JOIN pg_namespace n ON n.oid = c.relnamespace - LEFT JOIN pg_tablespace t ON t.oid = c.reltablespace -WHERE - c.relkind IN('r', 'v', 'm') AND - a.attnum > 0 AND - n.nspname = 'public' AND - c.relname = '{0}' AND - a.attisdropped = FALSE -ORDER BY a.attname; -""" - -COLUMNS_SQL = """select - sum(pg_column_size(t.*)) as "all", - {0} -from {1} t; -""".replace('\"', '\\\"') - - -def print_column_sizes(tables): - for table in tables: - print("Column sizes of table " + table) - cmds = [ - 'docker-compose run --rm import-osm', - '/usr/src/app/psql.sh -t -A -F\",\" -P pager=off', - '-c \"' + COLUMN_NAMES_SQL.format(table).replace('\n', ' ').replace('\r', - '') + '\"' - ] - # print " ".join(cmds) - output = subprocess.check_output(" ".join(cmds), shell=True) - columns = filter(lambda c: len(c) > 0, - map(lambda l: l.strip(), output.split('\n'))) - - # print columns - - col_sql = ",\n".join( - map(lambda c: "sum(pg_column_size(\\\"" + c + "\\\")) as \\\"" + c + "\\\"", - columns)) - - # print COLUMNS_SQL.format(col_sql, table); - - cmds = [ - 'docker-compose run --rm import-osm', - '/usr/src/app/psql.sh -F\",\" --no-align -P pager=off', - '-c \"' + COLUMNS_SQL.format(col_sql, table).replace('\n', ' ').replace( - '\r', '') + '\"' - ] - # print " ".join(cmds) - col_csv = subprocess.check_output(" ".join(cmds), shell=True) - print(col_csv) - - -if __name__ == "__main__": - args = parser.parse_args() - - try: - - if not args.noan: - print("Running make psql-analyze") - subprocess.check_output("make psql-analyze", shell=True) - - print("Total size of tables") - cmds = [ - 'docker-compose run --rm import-osm', - '/usr/src/app/psql.sh -F\",\" --no-align -P pager=off', - '-c \"' + TOTAL_SIZE_SQL.replace('\n', ' ').replace('\r', '') + '\"' - ] - # print " ".join(cmds) - TOTAL_SIZE_CSV = subprocess.check_output(" ".join(cmds), shell=True) - print(TOTAL_SIZE_CSV) - print("\n") - - print("Table sizes") - cmds = [ - 'docker-compose run --rm import-osm', - '/usr/src/app/psql.sh -F\",\" --no-align -P pager=off', - '-c \"' + TABLE_SIZES_SQL.replace('\n', ' ').replace('\r', '') + '\"' - ] - # print " ".join(cmds) - TABLE_SIZES_CSV = subprocess.check_output(" ".join(cmds), shell=True) - print(TABLE_SIZES_CSV) - print("\n") - - print("Column sizes") - cmds = [ - 'docker-compose run --rm import-osm', - '/usr/src/app/psql.sh -t -A -F\",\" -P pager=off', - '-c \"' + TABLES_SQL.replace('\n', ' ').replace('\r', '') + '\"' - ] - # print " ".join(cmds) - output = subprocess.check_output(" ".join(cmds), shell=True) - tables = filter(lambda t: len(t) > 0, - map(lambda l: l.strip(), output.split('\n'))) - - print_column_sizes(tables); - - # print tables - except subprocess.CalledProcessError as e: - print("Error:\n", e.output) - sys.exit(0) diff --git a/quickstart.sh b/quickstart.sh index f0bedfe18..c37e7501d 100755 --- a/quickstart.sh +++ b/quickstart.sh @@ -7,39 +7,61 @@ set -o nounset ########################################### # OpenMapTiles quickstart.sh for x86_64 linux # +# Usage: +# ./quickstart.sh [--empty] [area [geofabrik|osmfr|bbbike]] +# +# Use a preloaded docker image to speed up, unless the --empty flag is used. +# +# Servers: +# geofabik: http://download.geofabrik.de (default) +# osmfr: http://download.openstreetmap.fr (default for hierarchical area names) +# bbbike: https://www.bbbike.org (default for capitalized area names) +# # Example calls ... # ./quickstart.sh # ./quickstart.sh africa +# ./quickstart.sh africa geofabrik +# ./quickstart.sh africa osmfr # ./quickstart.sh alabama # ./quickstart.sh alaska # ./quickstart.sh albania # ./quickstart.sh alberta # ./quickstart.sh alps +# ./quickstart.sh europe/austria +# ./quickstart.sh europe/austria/salzburg osmfr +# ./quickstart.sh Adelaide +# ./quickstart.sh Adelaide bbbike # .... # -# to list areas : make download-geofabrik-list +# to list geofabrik areas: make list-geofabrik or make list-bbbike # see more QUICKSTART.md # +# If --empty is not given, use preloaded docker image to speed up +if [ $# -gt 0 ] && [[ $1 == --empty ]]; then + export USE_PRELOADED_IMAGE="" + shift +else + export USE_PRELOADED_IMAGE=true +fi + if [ $# -eq 0 ]; then - osm_area=albania # default test country - echo "No parameter - set area=$osm_area " + # default test area + export area=albania + echo "No parameter - set area=$area " else - osm_area=$1 + export area=$1 +fi + +if [ $# -eq 2 ]; then + osm_server=$2 fi -testdata="${osm_area}.osm.pbf" ## Min versions ... MIN_COMPOSE_VER=1.7.1 MIN_DOCKER_VER=1.12.3 STARTTIME=$(date +%s) STARTDATE=$(date +"%Y-%m-%dT%H:%M%z") -githash=$( git rev-parse HEAD ) - -# Options to run with docker and docker-compose - ensure the container is destroyed on exit, -# as well as pass any other common parameters. -# In the future this should use -u $(id -u "$USER"):$(id -g "$USER") instead of running docker as root. -DC_OPTS="--rm" log_file=./quickstart.log rm -f $log_file @@ -57,11 +79,11 @@ docker --version docker-compose --version # based on: http://stackoverflow.com/questions/16989598/bash-comparing-version-numbers -function version { echo "$@" | tr -cs '0-9.' '.' | gawk -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) 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!" + echo "ERR: Your Docker-compose version is known to have bugs, please update docker-compose!" exit 1 fi @@ -86,8 +108,10 @@ echo " Start processing echo "-------------------------------------------------------------------------------------" echo "====> : OpenMapTiles quickstart! [ https://github.com/openmaptiles/openmaptiles ] " echo " : This will be logged to the $log_file file (for debugging) and to the screen" -echo " : Area : $osm_area " -echo " : Git version : $githash " +echo " : Area : $area " +echo " : Download server : ${osm_server:-unset (automatic)} " +echo " : Preloaded image : $USE_PRELOADED_IMAGE " +echo " : Git version : $(git rev-parse HEAD) " echo " : Started : $STARTDATE " echo " : Your bash version: $BASH_VERSION" echo " : Your OS : $OSTYPE" @@ -106,12 +130,18 @@ 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 $(cat /proc/cpuinfo | grep "bogomips" | head -1)" - cat /proc/meminfo | grep Free + echo "CPU number: $(grep -c processor /proc/cpuinfo) x $(grep "bogomips" /proc/cpuinfo | head -1)" + grep Free /proc/meminfo else echo " " echo "Warning : Platforms other than Linux are less tested" @@ -121,53 +151,27 @@ fi echo " " echo "-------------------------------------------------------------------------------------" echo "====> : Stopping running services & removing old containers" -make clean-docker +make destroy-db echo " " echo "-------------------------------------------------------------------------------------" -echo "====> : Checking OpenMapTiles docker images " +echo "====> : Existing OpenMapTiles docker images. Will use version $(source .env && echo "$TOOLS_VERSION")" docker images | grep openmaptiles echo " " echo "-------------------------------------------------------------------------------------" -echo "====> : Making directories - if they don't exist ( ./build ./data ./pgdata ) " -mkdir -p pgdata -mkdir -p build -mkdir -p data -mkdir -p wikidata +echo "====> : Create directories if they don't exist" +make init-dirs echo " " echo "-------------------------------------------------------------------------------------" -echo "====> : Removing old MBTILES if exists ( ./data/*.mbtiles ) " -rm -f ./data/*.mbtiles +echo "====> : Removing old MBTILES if exists ( ./data/${area}.mbtiles ) " +rm -f "./data/${area}.mbtiles" -if [ ! -f "./data/${testdata}" ]; then - echo " " - echo "-------------------------------------------------------------------------------------" - echo "====> : Downloading testdata $testdata" - rm -f ./data/* - #wget $testdataurl -P ./data - make download-geofabrik "area=${osm_area}" - echo " " - echo "-------------------------------------------------------------------------------------" - echo "====> : Osm metadata : $testdata" - cat ./data/osmstat.txt - echo " " - echo "-------------------------------------------------------------------------------------" - echo "====> : Generated docker-compose config" - cat ./data/docker-compose-config.yml -else - echo " " - echo "-------------------------------------------------------------------------------------" - echo "====> : The testdata ./data/$testdata exists, we don't need to download!" -fi - - -if [ ! -f "./data/${testdata}" ]; then - echo " " - echo "Missing ./data/$testdata , Download or Parameter error? " - exit 1 -fi +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Downloading ${area} from ${osm_server:-any source}..." +make "download${osm_server:+-${osm_server}}" echo " " echo "-------------------------------------------------------------------------------------" @@ -176,140 +180,123 @@ make clean echo " " echo "-------------------------------------------------------------------------------------" -echo "====> : Code generating from the layer definitions ( ./build/mapping.yaml; ./build/tileset.sql )" +echo "====> : Code generating from the layer definitions ( ./build/mapping.yaml; ./build/sql/* )" echo " : The tool source code: https://github.com/openmaptiles/openmaptiles-tools " echo " : But we generate the tm2source, Imposm mappings and SQL functions from the layer definitions! " -make - -echo " " -echo "-------------------------------------------------------------------------------------" -echo "====> : Start PostgreSQL service ; create PostgreSQL data volume " -echo " : Source code: https://github.com/openmaptiles/postgis " -echo " : Thank you: https://www.postgresql.org ! Thank you http://postgis.org !" -docker-compose up -d postgres - -echo " " -echo "-------------------------------------------------------------------------------------" -echo "====> : Drop and Recreate PostgreSQL public schema " -# Drop all PostgreSQL tables -# This adds an extra safety belt if the user modifies the docker volume settings -make forced-clean-sql - -echo " " -echo "-------------------------------------------------------------------------------------" -echo "====> : Start importing water data from http://osmdata.openstreetmap.de/ into PostgreSQL " -echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/import-water " -echo " : Data license: https://osmdata.openstreetmap.de/info/license.html " -echo " : Thank you: https://osmdata.openstreetmap.de/info/ " -docker-compose run $DC_OPTS import-water - -echo " " -echo "-------------------------------------------------------------------------------------" -echo "====> : Start importing border data from http://openstreetmap.org into PostgreSQL " -echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/import-osmborder" -echo " : Data license: http://www.openstreetmap.org/copyright" -echo " : Thank you: https://github.com/pnorman/osmborder " -docker-compose run $DC_OPTS import-osmborder - -echo " " -echo "-------------------------------------------------------------------------------------" -echo "====> : Start importing http://www.naturalearthdata.com into PostgreSQL " -echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/import-natural-earth " -echo " : Terms-of-use: http://www.naturalearthdata.com/about/terms-of-use " -echo " : Thank you: Natural Earth Contributors! " -docker-compose run $DC_OPTS import-natural-earth - -echo " " -echo "-------------------------------------------------------------------------------------" -echo "====> : Start importing OpenStreetMap Lakelines data " -echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/import-lakelines " -echo " : https://github.com/lukasmartinelli/osm-lakelines " -echo " : Data license: .. " -docker-compose run $DC_OPTS import-lakelines +make all + +echo " " +echo "-------------------------------------------------------------------------------------" +if [[ "$USE_PRELOADED_IMAGE" == true ]]; then + echo "====> : Start PostgreSQL service using postgis image preloaded with this data:" + echo " : * Water data from http://osmdata.openstreetmap.de" + echo " : Data license: https://osmdata.openstreetmap.de/info/license.html" + echo " : * Natural Earth from http://www.naturalearthdata.com" + echo " : Terms-of-use: http://www.naturalearthdata.com/about/terms-of-use" + echo " : * OpenStreetMap Lakelines data https://github.com/lukasmartinelli/osm-lakelines" + echo " :" + echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/import-data" + echo " : includes all data from the import-data image" + echo " :" + echo " : Use the --empty flag to start with an empty database:" + echo " : ./quickstart.sh --empty albania " + echo " : If desired, you can manually import data by using these commands:" + echo " : make destroy-db" + echo " : make start-db" + echo " : make import-data" + echo " :" + echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/postgis-preloaded" + echo " : Thank you https://www.postgresql.org ! Thank you http://postgis.org !" + make start-db-preloaded +else + echo "====> : Start PostgreSQL service using empty database and importing all the data:" + echo " : * Water data from http://osmdata.openstreetmap.de" + echo " : Data license: https://osmdata.openstreetmap.de/info/license.html" + echo " : * Natural Earth from http://www.naturalearthdata.com" + echo " : Terms-of-use: http://www.naturalearthdata.com/about/terms-of-use" + echo " : * OpenStreetMap Lakelines data https://github.com/lukasmartinelli/osm-lakelines" + echo " :" + echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/import-data" + echo " : includes all data from the import-data image" + echo " :" + echo " : Thank you https://www.postgresql.org ! Thank you http://postgis.org !" + make start-db + make import-data +fi echo " " echo "-------------------------------------------------------------------------------------" -echo "====> : Start importing OpenStreetMap data: ./data/${testdata} -> imposm3[./build/mapping.yaml] -> PostgreSQL" +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 " : The OpenstreetMap data license: https://www.openstreetmap.org/copyright (ODBL) " echo " : Thank you OpenStreetMap Contributors ! " -docker-compose run $DC_OPTS import-osm +make import-osm echo " " echo "-------------------------------------------------------------------------------------" -echo "====> : Start importing Wikidata: ./wikidata/latest-all.json.gz -> PostgreSQL" -echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/import-wikidata " -echo " : The Wikidata license: https://www.wikidata.org/wiki/Wikidata:Database_download/en#License " +echo "====> : Start importing Wikidata: Wikidata Query Service -> PostgreSQL" +echo " : The Wikidata license: CC0 - https://www.wikidata.org/wiki/Wikidata:Main_Page " echo " : Thank you Wikidata Contributors ! " -docker-compose run $DC_OPTS import-wikidata +make import-wikidata echo " " echo "-------------------------------------------------------------------------------------" -echo "====> : Start SQL postprocessing: ./build/tileset.sql -> PostgreSQL " -echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/tree/master/docker/import-sql " +echo "====> : Start SQL postprocessing: ./build/sql/* -> PostgreSQL " +echo " : Source code: https://github.com/openmaptiles/openmaptiles-tools/blob/master/bin/import-sql" # If the output contains a WARNING, stop further processing # Adapted from https://unix.stackexchange.com/questions/307562 -docker-compose run $DC_OPTS openmaptiles-tools import-sql | \ - awk -v s=": WARNING:" '$0~s{print; print "\n*** WARNING detected, aborting"; exit(1)} 1' +make import-sql echo " " echo "-------------------------------------------------------------------------------------" echo "====> : Analyze PostgreSQL tables" -make psql-analyze +make analyze-db echo " " echo "-------------------------------------------------------------------------------------" echo "====> : Testing PostgreSQL tables to match layer definitions metadata" -docker-compose run $DC_OPTS openmaptiles-tools test-perf openmaptiles.yaml --test null --no-color +make test-perf-null echo " " echo "-------------------------------------------------------------------------------------" -echo "====> : Start generating MBTiles (containing gzipped MVT PBF) from a TM2Source project. " -echo " : TM2Source project definitions : ./build/openmaptiles.tm2source/data.yml " -echo " : Output MBTiles: ./data/tiles.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 ... " - -docker-compose -f docker-compose.yml -f ./data/docker-compose-config.yml run $DC_OPTS generate-vectortiles + +if [[ "$(source .env ; echo "$BBOX")" = "-180.0,-85.0511,180.0,85.0511" ]]; then + if [[ "$area" != "planet" ]]; then + echo "====> : Compute bounding box for tile generation" + make generate-bbox-file ${MIN_ZOOM:+MIN_ZOOM="${MIN_ZOOM}"} ${MAX_ZOOM:+MAX_ZOOM="${MAX_ZOOM}"} + else + echo "====> : Skipping bbox calculation when generating the entire planet" + fi + +else + echo "====> : Bounding box is set in .env file" +fi echo " " echo "-------------------------------------------------------------------------------------" -echo "====> : Add special metadata to mbtiles! " -docker-compose run $DC_OPTS openmaptiles-tools generate-metadata ./data/tiles.mbtiles -docker-compose run $DC_OPTS openmaptiles-tools chmod 666 ./data/tiles.mbtiles +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/blob/master/bin/generate-tiles " +make generate-tiles-pg echo " " echo "-------------------------------------------------------------------------------------" echo "====> : Stop PostgreSQL service ( but we keep PostgreSQL data volume for debugging )" -docker-compose stop postgres +make stop-db echo " " echo "-------------------------------------------------------------------------------------" echo "====> : Inputs - Outputs md5sum for debugging " rm -f ./data/quickstart_checklist.chk -md5sum build/mapping.yaml >> ./data/quickstart_checklist.chk -md5sum build/tileset.sql >> ./data/quickstart_checklist.chk -md5sum build/openmaptiles.tm2source/data.yml >> ./data/quickstart_checklist.chk -md5sum "./data/${testdata}" >> ./data/quickstart_checklist.chk -md5sum ./data/tiles.mbtiles >> ./data/quickstart_checklist.chk -md5sum ./data/docker-compose-config.yml >> ./data/quickstart_checklist.chk -md5sum ./data/osmstat.txt >> ./data/quickstart_checklist.chk +{ + find build -type f | sort | xargs md5sum + find data -type f | sort | xargs md5sum +} >> ./data/quickstart_checklist.chk cat ./data/quickstart_checklist.chk ENDTIME=$(date +%s) -ENDDATE=$(date +"%Y-%m-%dT%H:%M%z") -if stat --help >/dev/null 2>&1; then - MODDATE=$(stat -c %y "./data/${testdata}" ) -else - MODDATE=$(stat -f%Sm -t '%F %T %z' "./data/${testdata}" ) -fi echo " " echo " " @@ -322,30 +309,22 @@ echo "====> : (disk space) We have created a lot of docker images: " echo " : Hint: you can remove with: docker rmi IMAGE " docker images | grep openmaptiles - -echo " " -echo "-------------------------------------------------------------------------------------" -echo "====> : (disk space) We have created this new docker volume for PostgreSQL data:" -echo " : Hint: you can remove with : docker volume rm openmaptiles_pgdata " -docker volume ls -q | grep openmaptiles - echo " " echo "-------------------------------------------------------------------------------------" -echo "====> : (disk space) We have created the new vectortiles ( ./data/tiles.mbtiles ) " +echo "====> : (disk space) We have created the new vectortiles ( ./data/${area}.mbtiles ) " echo " : Please respect the licenses (OdBL for OSM data) of the sources when distributing the MBTiles file." -echo " : Created from $testdata ( file moddate: $MODDATE ) " -echo " : Size: " -ls -la ./data/*.mbtiles +echo " : Data directory content:" +ls -la ./data echo " " echo "-------------------------------------------------------------------------------------" -echo "The ./quickstart.sh $osm_area is finished! " -echo "It takes $((ENDTIME - STARTTIME)) seconds to complete" -echo "We saved the log file to $log_file ( for debugging ) You can compare with the travis log !" +echo "The ./quickstart.sh $area is finished! " +echo "It took $((ENDTIME - STARTTIME)) seconds to complete" +echo "We saved the log file to $log_file (for debugging) You can compare with the travis log !" echo " " -echo "Start experimenting! And check the QUICKSTART.MD file!" +echo "Start experimenting and check the QUICKSTART.MD file!" echo " " -echo "* Use make start-postserve to explore tile generation on request" +echo "* Use make start-maputnik to explore tile generation on request" echo "* Use make start-tileserver to view pre-generated tiles" echo " " echo "Available help commands (make help) " diff --git a/style/README.md b/style/README.md new file mode 100644 index 000000000..0058aecf4 --- /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 000000000..1da3f0929 --- /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 000000000..506588edd --- /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 000000000..cd18fb439 --- /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 000000000..ee909df9f --- /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 000000000..01c8407e6 --- /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 000000000..9c9d8af66 --- /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 000000000..46728f45f --- /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 000000000..cc6ca6040 --- /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 000000000..ec769ae0e --- /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 000000000..a23d320fb --- /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 000000000..6b95f600b --- /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 000000000..237775ff7 --- /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 000000000..c6fe7e78d --- /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 000000000..db2df6597 --- /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 000000000..b0ac45a61 --- /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 000000000..58f330e18 --- /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 000000000..1de09e742 --- /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 000000000..dace2bddb --- /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 000000000..4fe94ec5a --- /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 000000000..787b22d1b --- /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 000000000..3480229dd --- /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 000000000..2eb93623c --- /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 000000000..97606d311 --- /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 000000000..649d12238 --- /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 000000000..f2cfad6e2 --- /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 000000000..15c95ed29 --- /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 000000000..6341ca478 --- /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 000000000..1da8f39a9 --- /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 000000000..209d4b6dc --- /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 000000000..3813e62c3 --- /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 000000000..a9bdb447e --- /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 000000000..ba21b2437 --- /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 000000000..153693fc3 --- /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 000000000..ab9513e21 --- /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 000000000..ca99d4c8b --- /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 000000000..1bf0829fe --- /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 000000000..7c318a2ce --- /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 000000000..f6a317170 --- /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 000000000..8ff35da20 --- /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 000000000..3f1b19080 --- /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 000000000..ae9a009af --- /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 000000000..d0a9405b9 --- /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 000000000..f98219e19 --- /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 000000000..0bc0cf9cd --- /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 000000000..59b1cf18f --- /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 000000000..9a61c2d32 --- /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 000000000..c41e48c94 --- /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 000000000..e82f46521 --- /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 000000000..97326bfd8 --- /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 000000000..7e8c85c62 --- /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 000000000..a84ef7864 --- /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 000000000..e2105eff1 --- /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 000000000..e4e7eeb95 --- /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 000000000..f2eef5f28 --- /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 000000000..78187c6e6 --- /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 000000000..b299fe0e1 --- /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 000000000..2de78b7be --- /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 000000000..32150193a --- /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 000000000..4f413a278 --- /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 000000000..cf03ef6c2 --- /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 000000000..a3a9adae6 --- /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 000000000..fd4f83247 --- /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 000000000..234bd93e2 --- /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 000000000..ab55f153b --- /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 000000000..8461e9c3e --- /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 000000000..bf7801077 --- /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 000000000..2297b17b0 --- /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 000000000..55b6c8d61 --- /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 000000000..5e5e067b6 --- /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 000000000..075aaa8e6 --- /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 000000000..2a4c162ff --- /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 000000000..696ce5b0d --- /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 000000000..b8bc34800 --- /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 000000000..259c2afa4 --- /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 000000000..6df47e200 --- /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 000000000..ee5f6f4d6 --- /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 000000000..d0522da97 --- /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 000000000..2fa1cab9d --- /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 000000000..a7f136b54 --- /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 000000000..a492fc648 --- /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 000000000..514cc27f8 --- /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 000000000..be75053d0 --- /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 000000000..8d6a59dbb --- /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 000000000..c32897d20 --- /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 000000000..2f33f7e1d --- /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 000000000..0dd7be006 --- /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 000000000..ea91694d8 --- /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 000000000..1056caf25 --- /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 000000000..890a15ce6 --- /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 000000000..b833d34b9 --- /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 000000000..1440c1c3b --- /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 000000000..e5a64268d --- /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 000000000..f02e3c3e0 --- /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 000000000..ae34d096c --- /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 000000000..a1dd277f1 --- /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 000000000..e28f4fc94 --- /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 000000000..b2d2b7198 --- /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 000000000..c288a1bb7 --- /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 000000000..6541d2bad --- /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 000000000..45b622087 --- /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 000000000..b494a60ef --- /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 000000000..97b30b12f --- /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 000000000..dece825b7 --- /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 000000000..f8f0e317a --- /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 000000000..57ef82b40 --- /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 000000000..7a7c9de82 --- /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 000000000..866b599cc --- /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 000000000..c4a9e3b24 --- /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 000000000..d729b51a2 --- /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 000000000..296a0d752 --- /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 000000000..bffcb858e --- /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 000000000..707fcdc88 --- /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 000000000..08de3663a --- /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 000000000..3a45ad6b2 --- /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 000000000..9fc9b2a4f --- /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 000000000..a51ceb6b7 --- /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 000000000..ddfdce0fa --- /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 000000000..04828ea07 --- /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 000000000..b803ca3fb --- /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 000000000..29c80551b --- /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 000000000..2e975cec4 --- /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 000000000..5d791fe28 --- /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 000000000..e2f789098 --- /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 000000000..84edc7f1d --- /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 000000000..d1e3fe0f2 --- /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 000000000..e6295e2d8 --- /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 000000000..54f45e8c8 --- /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 000000000..fd9d7063e --- /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 000000000..ad132d7c7 --- /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 000000000..08831a37f --- /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 000000000..fcaf03ca4 --- /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 000000000..e73fd054b --- /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 000000000..e3b034045 --- /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 000000000..f4afc468c --- /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 000000000..630aa247b --- /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 000000000..8c8cffec3 --- /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 000000000..1472463c3 --- /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 000000000..fd117a478 --- /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 000000000..78d95e57d --- /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 000000000..0452c6f88 --- /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 000000000..96e9a8b71 --- /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 000000000..ca78e7da6 --- /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 000000000..134fafaeb --- /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 000000000..0e436e59e --- /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 000000000..a9ffc582e --- /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 000000000..2e59fadd2 --- /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 000000000..c464ee2ef --- /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 000000000..19aaec3ca --- /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 000000000..4c1fbd2ad --- /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 000000000..489dd9bef --- /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 000000000..58e23fd42 --- /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 000000000..3bbc253b4 --- /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 000000000..fe1fa8b04 --- /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 000000000..bdfd5e6d1 --- /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 000000000..39c95786f --- /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 000000000..dec15bfd9 --- /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 000000000..faed6695b --- /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 000000000..06c7b4e1d --- /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 000000000..390bf434e --- /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 000000000..0e11675af --- /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 000000000..dc93b2f71 --- /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 000000000..e9f38465c --- /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 000000000..ef3b95f1a --- /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 000000000..98049aca6 --- /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 000000000..981b96031 --- /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 000000000..0cfbd38da --- /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 000000000..009467c4b --- /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 000000000..5ac858e03 --- /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 000000000..4e111d4ef --- /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 000000000..91819703e --- /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 000000000..ac993f62a --- /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 000000000..4fe13273a --- /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 000000000..3010f2e8a --- /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 000000000..7bc5aa0aa --- /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 000000000..db162cb9a --- /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 000000000..4fb44a91b --- /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 000000000..867620e0f --- /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 000000000..60316292f --- /dev/null +++ b/style/icons/office.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/style/icons/oneway-cycleway.svg b/style/icons/oneway-cycleway.svg new file mode 100644 index 000000000..63b053743 --- /dev/null +++ b/style/icons/oneway-cycleway.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 000000000..7838527fa --- /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 000000000..1a37cca40 --- /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 000000000..308f219ed --- /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 000000000..a6f955887 --- /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 000000000..d98b343a7 --- /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 000000000..c0dd268e2 --- /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 000000000..31653ee63 --- /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 000000000..96744d41e --- /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 000000000..dc019e3da --- /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 000000000..6e7848517 --- /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 000000000..6d1ad6545 --- /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 000000000..1d99510bb --- /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 000000000..e07490f83 --- /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 000000000..b4b369593 --- /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 000000000..7237d5e23 --- /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 000000000..26ffc499f --- /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 000000000..574356830 --- /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 000000000..bfc8a861b --- /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 000000000..3ddc15a79 --- /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 000000000..fdfb3dc6a --- /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 000000000..bb0c96787 --- /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 000000000..8d81dc465 --- /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 000000000..42a9906b4 --- /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 000000000..81a8a642b --- /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 000000000..d8bbf380b --- /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 000000000..74e888850 --- /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 000000000..af629faf5 --- /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 000000000..06510fe6a --- /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 000000000..54c2fe7cc --- /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 000000000..91da45b3d --- /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 000000000..d1c1a295d --- /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 000000000..d1084f057 --- /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 000000000..c3bdbdc17 --- /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 000000000..a6311e5b9 --- /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 000000000..e5511614e --- /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 000000000..22d08ca28 --- /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 000000000..cbc635627 --- /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 000000000..b1f9d376b --- /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 000000000..96190fb50 --- /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 000000000..4ece7f23c --- /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 000000000..3a284c78c --- /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 000000000..02afe8d22 --- /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 000000000..00f363ea9 --- /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 000000000..7d94f14a4 --- /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 000000000..417a61d1c --- /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 000000000..aefcbcf44 --- /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 000000000..4b2f1104c --- /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 000000000..40386590e --- /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 000000000..5a0de757e --- /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 000000000..10d25b63d --- /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 000000000..b46dc69a8 --- /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 000000000..9718c32ab --- /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 000000000..efea15104 --- /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 000000000..4ed981544 --- /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 000000000..77674dc5c --- /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 000000000..08963c66c --- /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 000000000..571a056d9 --- /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 000000000..122c52b8e --- /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 000000000..77b2d0fb1 --- /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 000000000..005a0031b --- /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 000000000..ac8045960 --- /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 000000000..f8e131ebd --- /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 000000000..35524f26f --- /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 000000000..a76e86158 --- /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 000000000..c3800fa40 --- /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 000000000..1d889ceb3 --- /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 000000000..0d6228dc9 --- /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 000000000..f06ea2bfa --- /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 000000000..ab328525a --- /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 000000000..12330a656 --- /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 000000000..1963305a3 --- /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 000000000..c28372395 --- /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 000000000..b1742c2ca --- /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 000000000..075194f31 --- /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 000000000..e7c27481c --- /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 000000000..0f179fabc --- /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 000000000..d29e518e9 --- /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 000000000..c4a7eaffb --- /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 000000000..ca9b0b984 --- /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 000000000..aeacd073d --- /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 000000000..47077f0f3 --- /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 000000000..b9ab9fe94 --- /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 000000000..52576ad05 --- /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 000000000..9e2f5176f --- /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 000000000..dead6c33d --- /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 000000000..ec0b808b3 --- /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 000000000..01daa6cef --- /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 000000000..b55b489df --- /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 000000000..8fd6ce623 --- /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 000000000..d3b39d283 --- /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 000000000..19eead1cd --- /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 000000000..aabc27e83 --- /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 000000000..72533f58c --- /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 000000000..4c6115f97 --- /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 000000000..1b9a982bb --- /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 000000000..ee52d2a8c --- /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 000000000..55836c6a0 --- /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 000000000..463110180 --- /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 000000000..1c7ee24b8 --- /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 000000000..085d53dab --- /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 000000000..085d53dab --- /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 000000000..f75b35d05 --- /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 000000000..675673a59 --- /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 000000000..a02d2b364 --- /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 000000000..7947e317a --- /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 000000000..2dac756e5 --- /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 000000000..f97cc778b --- /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 000000000..67ada9772 --- /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 000000000..350c6339d --- /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 000000000..30943b507 --- /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 000000000..9ed702d0a --- /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 000000000..9fef5e41d --- /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 000000000..26907e161 --- /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 000000000..f5784d740 --- /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 000000000..9901de6a4 --- /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 000000000..df654b43d --- /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 000000000..14a9067f7 --- /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 000000000..837c7bf0f --- /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 000000000..b4e31cb8c --- /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 000000000..018e2a506 --- /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 000000000..afe15c31f --- /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 000000000..4b6b6737a --- /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 000000000..76b5b9637 --- /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 000000000..d61fbc80f --- /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 000000000..32013dd17 --- /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 000000000..afe911068 --- /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 000000000..f01fdbecf --- /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 000000000..ba3ec2d10 --- /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 000000000..7805b994b --- /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 000000000..630648ae0 --- /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 000000000..79740b4a2 --- /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 000000000..0667a36ec --- /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 000000000..19cf6db0d --- /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 000000000..fda6bee1a --- /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 000000000..7fcbec92b --- /dev/null +++ b/tests/import/500_import-highway.osm @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/import/600_import-poi.osm b/tests/import/600_import-poi.osm new file mode 100644 index 000000000..93de4f6b6 --- /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 000000000..a2a0c28a8 --- /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 000000000..f874a25a9 --- /dev/null +++ b/tests/test-post-import.sql @@ -0,0 +1,203 @@ +-- 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 highway='motorway'; + IF cnt < 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z4 motorway count expected >=1, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z4 WHERE highway='trunk'; + IF cnt <> 0 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z4 trunk count expected 0, got ' || cnt); + END IF; + + SELECT COUNT(*) INTO cnt FROM osm_transportation_merge_linestring_gen_z5 WHERE highway='trunk'; + IF cnt < 1 THEN + INSERT INTO omt_test_failures VALUES(500, 'import', 'osm_transportation_linestring z5 trunk 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; + + -- 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 000000000..969531044 --- /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 000000000..ca67695ea --- /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 000000000..c6fc222bb --- /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 000000000..4f3fe1514 --- /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 000000000..9d7e99cab --- /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 000000000..f56451732 --- /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 000000000..e03e31f3b --- /dev/null +++ b/tests/update/600_update-poi.osc @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + +