diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..245afac --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,60 @@ +Copyright (c) 2016, KlokanTech.com & OpenMapTiles contributors. +All rights reserved. + +The vector tile schema has been developed by Klokan Technologies GmbH and +was initially modelled after the cartography of the CARTO's Positron basemap +with permission from CartoDB Inc. +The vector tile schema has been refined and improved in cooperation with +the Wikimedia Foundation and is heavily influenced by years of +Paul Norman's experience of creating maps from OpenStreetMap data. + +# Code license: BSD 3-Clause License + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Design license: CC-BY 4.0 + +The cartography and visual design features of the map tile schema (also known as +the "look and feel" of the map) are licensed under the Creative Commons +Attribution 4.0 license. +To view a copy of the license, visit http://creativecommons.org/licenses/by/4.0/. + +Products or services using maps derived from OpenMapTiles schema need to visibly +credit "OpenMapTiles.org" or reference "OpenMapTiles" with a link to +http://openmaptiles.org/. + +For a browsable electronic map based on OpenMapTiles and OpenStreetMap data, the +credit should appear in the corner of the map. For example: + +[© OpenMapTiles](http://openmaptiles.org/) [© OpenStreetMap contributors](http://www.openstreetmap.org/copyright) + +For printed and static maps a similar attribution should be made in a textual +description near the image, in the same fashion as if you cite a photograph. + +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 +exceptions on a commercial basis. diff --git a/Makefile b/Makefile index 937060b..05a662f 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ help: @echo " " @echo "Hints for designers:" @echo " ....TODO.... # start Maputnik " - @echo " ....TODO.... # start Tileserver-gl-light" + @echo " make start-tileserver # start klokantech/tileserver-gl [ see localhost:8080 ] " @echo " make start-mapbox-studio # start Mapbox Studio" @echo " " @echo "Hints for developers:" @@ -61,9 +61,9 @@ refresh-docker-images: remove-docker-images: @echo "Deleting all openmaptiles related docker image(s)..." @docker-compose down - @docker images | grep "" | awk -F" " '{print $$3}' | xargs --no-run-if-empty docker rmi - @docker images | grep "openmaptiles" | awk -F" " '{print $$3}' | xargs --no-run-if-empty docker rmi - @docker images | grep "osm2vectortiles/mapbox-studio" | awk -F" " '{print $$3}' | xargs --no-run-if-empty docker rmi + @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 docker-unnecessary-clean: @echo "Deleting unnecessary container(s)..." @@ -117,6 +117,26 @@ list: download-geofabrik-list: docker-compose run --rm import-osm ./download-geofabrik-list.sh +start-tileserver: + @echo " " + @echo "***********************************************************" + @echo "* " + @echo "* Download/refresh klokantech/tileserver-gl docker image" + @echo "* see documentation: https://github.com/klokantech/tileserver-gl" + @echo "* " + @echo "***********************************************************" + @echo " " + docker pull klokantech/tileserver-gl + @echo " " + @echo "***********************************************************" + @echo "* " + @echo "* Start klokantech/tileserver-gl " + @echo "* ----------------------------> check localhost:8080 " + @echo "* " + @echo "***********************************************************" + @echo " " + docker run -it --rm -v $$(pwd)/data:/data -p 8080:80 klokantech/tileserver-gl + start-mapbox-studio: docker-compose up mapbox-studio diff --git a/QUICKSTART.md b/QUICKSTART.md index 36abe18..a0f646a 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -272,14 +272,18 @@ This is generating `.mbtiles` for your area : [ MIN_ZOOM: "0" - MAX_ZOOM: "7" ./quickstart.sh yukon ``` -### Check other commands +### Check tileserver -`make help` +start: +* ` make start-tileserver` +and the generated maps are going to be available in webbrowser on [localhost:8080](http://localhost:8080/). + +This is only a quick preview, because your mbtiles only generated to zoom level 7 ! ### Change MIN_ZOOM and MAX_ZOOM -modify the settings in the `.env` file +modify the settings in the `.env` file, the defaults : * QUICKSTART_MIN_ZOOM=0 * QUICKSTART_MAX_ZOOM=7 @@ -292,3 +296,6 @@ Hints: * Small increments! Never starts with the MAX_ZOOM = 14 * The suggested MAX_ZOOM = 14 - use only with small extracts +### Check other commands + +`make help` diff --git a/README.md b/README.md index 7958aee..7bf58c6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## OpenMapTiles +## OpenMapTiles [![Build Status](https://travis-ci.org/openmaptiles/openmaptiles.svg?branch=master)](https://travis-ci.org/openmaptiles/openmaptiles) OpenMapTiles is an extensible and open vector tile schema for a OpenStreetMap basemap. It is used to generate vector tiles for [openmaptiles.org](http://openmaptiles.org/) and [openmaptiles.com](http://openmaptiles.com/). @@ -116,7 +116,7 @@ Each time you modify layer SQL code run `make` and `docker-compose run import-sq make clean && make && docker-compose run import-sql ``` -Now you are ready to **generate the vector tiles** using a single process (for a full blown distributed workflow of rendering tiles check out [openmaptiles/distributed](https://github.com/openmaptiles/distributed)). Using environment variables +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`). ``` @@ -125,6 +125,14 @@ docker-compose run generate-vectortiles ## License -*LICENSE HAS NOT BEEN YET DECIDED* +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 [MIT license](./LICENSE) and the cartography decisions encoded in the schema and SQL is licensed under [CC0](https://creativecommons.org/publicdomain/zero/1.0/). +Products or services using maps derived from OpenMapTiles schema need to visibly credit "OpenMapTiles.org" or reference "OpenMapTiles" with a link to http://openmaptiles.org/. Exceptions to attribution requirement can be granted on request. + +For a browsable electronic map based on OpenMapTiles and OpenStreetMap data, the +credit should appear in the corner of the map. For example: + +[© OpenMapTiles](http://openmaptiles.org/) [© OpenStreetMap contributors](http://www.openstreetmap.org/copyright) + +For printed and static maps a similar attribution should be made in a textual +description near the image, in the same fashion as if you cite a photograph. diff --git a/UPDATE.md b/UPDATE.md new file mode 100644 index 0000000..ed3a32c --- /dev/null +++ b/UPDATE.md @@ -0,0 +1,38 @@ +# Keep 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 +or import specific change files. + +### Keep Database Updated + +You can use the new imposm3 feature to keep the database updated (thanks to the [work for @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 of you should also have a list of tiles that have updated. + +``` +docker-compose run update-osm +``` + +### 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. + +``` +docker-compose run import-osm-diff +``` + +## Generate Changed Tiles + +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. + +Now run the command to read the tilelist and write the vector tiles for it to a new MBTiles. + +``` +docker-compose run generate-changed-vectortiles +``` diff --git a/docker-compose.yml b/docker-compose.yml index 4e775f1..85a63c6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,6 +36,35 @@ services: - ./data:/import - ./build:/mapping - cache:/cache + import-osmborder: + image: "openmaptiles/import-osmborder:0.2" + env_file: .env + links: + - postgres + import-osm-diff: + image: "openmaptiles/import-osm:latest" + env_file: .env + command: ./import_diff.sh + environment: + DIFF_MODE: ${DIFF_MODE} + links: + - postgres + volumes: + - ./data:/import + - ./build:/mapping + - cache:/cache + update-osm: + image: "openmaptiles/import-osm:latest" + env_file: .env + environment: + DIFF_MODE: ${DIFF_MODE} + command: ./import_update.sh + links: + - postgres + volumes: + - ./data:/import + - ./build:/mapping + - cache:/cache import-sql: image: "openmaptiles/import-sql:0.1" env_file: .env @@ -59,6 +88,15 @@ services: - postgres:db ports: - "3000:3000" + generate-changed-vectortiles: + image: "openmaptiles/generate-vectortiles:0.1" + command: ./export-list.sh + volumes: + - ./data:/export + - ./build/openmaptiles.tm2source:/tm2source + links: + - postgres:postgres + env_file: .env generate-vectortiles: image: "openmaptiles/generate-vectortiles:0.1" volumes: diff --git a/layers/aeroway/mapping.yaml b/layers/aeroway/mapping.yaml index 0d77037..2f1b90e 100644 --- a/layers/aeroway/mapping.yaml +++ b/layers/aeroway/mapping.yaml @@ -23,7 +23,7 @@ tables: key: aeroway type: string - name: area - type: pseudoarea + type: webmerc_area mapping: aeroway: - aerodrome diff --git a/layers/boundary/boundary.sql b/layers/boundary/boundary.sql index 31e951c..d0b7b2f 100644 --- a/layers/boundary/boundary.sql +++ b/layers/boundary/boundary.sql @@ -3,7 +3,7 @@ -- etldoc: ne_110m_admin_0_boundary_lines_land -> boundary_z0 CREATE OR REPLACE VIEW boundary_z0 AS ( - SELECT geometry, 2 AS admin_level + SELECT geometry, 2 AS admin_level, false AS disputed, false AS maritime FROM ne_110m_admin_0_boundary_lines_land ); @@ -11,10 +11,10 @@ CREATE OR REPLACE VIEW boundary_z0 AS ( -- etldoc: ne_50m_admin_1_states_provinces_lines -> boundary_z1 CREATE OR REPLACE VIEW boundary_z1 AS ( - SELECT geometry, 2 AS admin_level + SELECT geometry, 2 AS admin_level, false AS disputed, false AS maritime FROM ne_50m_admin_0_boundary_lines_land UNION ALL - SELECT geometry, 4 AS admin_level + SELECT geometry, 4 AS admin_level, false AS disputed, false AS maritime FROM ne_50m_admin_1_states_provinces_lines WHERE scalerank <= 2 ); @@ -24,102 +24,108 @@ CREATE OR REPLACE VIEW boundary_z1 AS ( -- etldoc: ne_50m_admin_1_states_provinces_lines -> boundary_z3 CREATE OR REPLACE VIEW boundary_z3 AS ( - SELECT geometry, 2 AS admin_level + SELECT geometry, 2 AS admin_level, false AS disputed, false AS maritime FROM ne_50m_admin_0_boundary_lines_land UNION ALL - SELECT geometry, 4 AS admin_level + SELECT geometry, 4 AS admin_level, false AS disputed, false AS maritime FROM ne_50m_admin_1_states_provinces_lines ); -- etldoc: ne_10m_admin_0_boundary_lines_land -> boundary_z4 -- etldoc: ne_10m_admin_1_states_provinces_lines_shp -> boundary_z4 +-- etldoc: osm_border_linestring_gen10 -> boundary_z4 CREATE OR REPLACE VIEW boundary_z4 AS ( - SELECT geometry, 2 AS admin_level + SELECT geometry, 2 AS admin_level, false AS disputed, false AS maritime FROM ne_10m_admin_0_boundary_lines_land UNION ALL - SELECT geometry, 4 AS admin_level + SELECT geometry, 4 AS admin_level, false AS disputed, false AS maritime FROM ne_10m_admin_1_states_provinces_lines_shp WHERE scalerank <= 3 AND featurecla = 'Adm-1 boundary' + UNION ALL + SELECT geometry, admin_level, disputed, maritime + FROM osm_border_linestring_gen10 + WHERE maritime=true AND admin_level <= 2 ); -- etldoc: ne_10m_admin_0_boundary_lines_land -> boundary_z5 -- etldoc: ne_10m_admin_1_states_provinces_lines_shp -> boundary_z5 +-- etldoc: osm_border_linestring_gen9 -> boundary_z5 CREATE OR REPLACE VIEW boundary_z5 AS ( - SELECT geometry, 2 AS admin_level + SELECT geometry, 2 AS admin_level, false AS disputed, false AS maritime FROM ne_10m_admin_0_boundary_lines_land UNION ALL - SELECT geometry, 4 AS admin_level + SELECT geometry, 4 AS admin_level, false AS disputed, false AS maritime FROM ne_10m_admin_1_states_provinces_lines_shp WHERE scalerank <= 7 AND featurecla = 'Adm-1 boundary' + UNION ALL + SELECT geometry, admin_level, disputed, maritime + FROM osm_border_linestring_gen9 + WHERE maritime=true AND admin_level <= 2 ); --- etldoc: ne_10m_admin_0_boundary_lines_land -> boundary_z6 --- etldoc: ne_10m_admin_1_states_provinces_lines_shp -> boundary_z6 - +-- etldoc: osm_border_linestring_gen8 -> boundary_z6 CREATE OR REPLACE VIEW boundary_z6 AS ( - SELECT geometry, 2 AS admin_level - FROM ne_10m_admin_0_boundary_lines_land - UNION ALL - SELECT geometry, 4 AS admin_level - FROM ne_10m_admin_1_states_provinces_lines_shp - WHERE scalerank <= 9 AND featurecla = 'Adm-1 boundary' + SELECT geometry, admin_level, disputed, maritime + FROM osm_border_linestring_gen8 + WHERE admin_level <= 4 ); --- etldoc: ne_10m_admin_0_boundary_lines_land -> boundary_z7 --- etldoc: ne_10m_admin_1_states_provinces_lines_shp -> boundary_z7 +-- etldoc: osm_border_linestring_gen7 -> boundary_z7 CREATE OR REPLACE VIEW boundary_z7 AS ( - SELECT geometry, 2 AS admin_level - FROM ne_10m_admin_0_boundary_lines_land - UNION ALL - SELECT geometry, 4 AS admin_level - FROM ne_10m_admin_1_states_provinces_lines_shp - WHERE featurecla = 'Adm-1 boundary' - + SELECT geometry, admin_level, disputed, maritime + FROM osm_border_linestring_gen7 + WHERE admin_level <= 4 ); --- etldoc: osm_boundary_linestring_gen5 -> boundary_z8 +-- etldoc: osm_border_linestring_gen6 -> boundary_z8 CREATE OR REPLACE VIEW boundary_z8 AS ( - SELECT geometry, admin_level - FROM osm_boundary_linestring_gen5 - WHERE admin_level <= 4 AND ST_Length(geometry) > 1000 + SELECT geometry, admin_level, disputed, maritime + FROM osm_border_linestring_gen6 + WHERE admin_level <= 4 ); --- etldoc: osm_boundary_linestring_gen4 -> boundary_z9 +-- etldoc: osm_border_linestring_gen5 -> boundary_z9 CREATE OR REPLACE VIEW boundary_z9 AS ( - SELECT geometry, admin_level - FROM osm_boundary_linestring_gen4 + SELECT geometry, admin_level, disputed, maritime + FROM osm_border_linestring_gen5 WHERE admin_level <= 6 ); --- etldoc: osm_boundary_linestring_gen3 -> boundary_z10 +-- etldoc: osm_border_linestring_gen4 -> boundary_z10 CREATE OR REPLACE VIEW boundary_z10 AS ( - SELECT geometry, admin_level - FROM osm_boundary_linestring_gen3 + SELECT geometry, admin_level, disputed, maritime + FROM osm_border_linestring_gen4 WHERE admin_level <= 6 ); --- etldoc: osm_boundary_linestring_gen2 -> boundary_z11 +-- etldoc: osm_border_linestring_gen3 -> boundary_z11 CREATE OR REPLACE VIEW boundary_z11 AS ( - SELECT geometry, admin_level - FROM osm_boundary_linestring_gen2 + SELECT geometry, admin_level, disputed, maritime + FROM osm_border_linestring_gen3 WHERE admin_level <= 8 ); --- etldoc: osm_boundary_linestring_gen1 -> boundary_z12 +-- etldoc: osm_border_linestring_gen2 -> boundary_z12 CREATE OR REPLACE VIEW boundary_z12 AS ( - SELECT geometry, admin_level - FROM osm_boundary_linestring_gen1 + SELECT geometry, admin_level, disputed, maritime + FROM osm_border_linestring_gen2 +); + +-- etldoc: osm_border_linestring_gen1 -> boundary_z12 +CREATE OR REPLACE VIEW boundary_z13 AS ( + SELECT geometry, admin_level, disputed, maritime + FROM osm_border_linestring_gen1 ); -- 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) AS $$ - SELECT geometry, admin_level FROM ( +RETURNS TABLE(geometry geometry, admin_level int, disputed int, maritime int) AS $$ + SELECT geometry, admin_level, disputed::int, maritime::int FROM ( -- etldoc: boundary_z0 -> layer_boundary:z0 SELECT * FROM boundary_z0 WHERE geometry && bbox AND zoom_level = 0 UNION ALL @@ -156,7 +162,7 @@ RETURNS TABLE(geometry geometry, admin_level int) AS $$ -- etldoc: boundary_z12 -> layer_boundary:z12 SELECT * FROM boundary_z12 WHERE geometry && bbox AND zoom_level = 12 UNION ALL - -- etldoc: boundary_z12 -> layer_boundary:z13 - SELECT * FROM boundary_z12 WHERE geometry && bbox AND zoom_level >= 13 + -- etldoc: boundary_z13 -> layer_boundary:z13 + SELECT * FROM boundary_z13 WHERE geometry && bbox AND zoom_level >= 13 ) AS zoom_levels; $$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/boundary/boundary.yaml b/layers/boundary/boundary.yaml index 29dec59..f4acdc2 100644 --- a/layers/boundary/boundary.yaml +++ b/layers/boundary/boundary.yaml @@ -15,12 +15,16 @@ layer: At low zoom levels the Natural Earth boundaries are mapped to the equivalent admin levels. disputed: description: | - Mark with `1` if the boundary is disputed. + Mark with `1` if the border is disputed. + values: [0, 1] + maritime: + description: | + Mark with `1` if it is a maritime border. values: [0, 1] buffer_size: 4 datasource: geometry_field: geometry - query: (SELECT geometry, admin_level FROM layer_boundary(!bbox!, z(!scale_denominator!))) AS t + query: (SELECT geometry, admin_level, disputed, maritime FROM layer_boundary(!bbox!, z(!scale_denominator!))) AS t schema: - ./boundary.sql datasources: diff --git a/layers/building/mapping.yaml b/layers/building/mapping.yaml index f4c4e85..27ad924 100644 --- a/layers/building/mapping.yaml +++ b/layers/building/mapping.yaml @@ -16,7 +16,7 @@ tables: - name: geometry type: validated_geometry - name: area - type: pseudoarea + type: webmerc_area - name: building key: building type: string diff --git a/layers/housenumber/housenumber_centroid.sql b/layers/housenumber/housenumber_centroid.sql index 8a81d6a..20e323c 100644 --- a/layers/housenumber/housenumber_centroid.sql +++ b/layers/housenumber/housenumber_centroid.sql @@ -1,4 +1,45 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_housenumber_point; +DROP TRIGGER IF EXISTS trigger_refresh ON housenumber.updates; -- etldoc: osm_housenumber_point -> osm_housenumber_point -UPDATE osm_housenumber_point SET geometry=topoint(geometry) -WHERE ST_GeometryType(geometry) <> 'ST_Point'; +CREATE OR REPLACE FUNCTION convert_housenumber_point() RETURNS VOID AS $$ +BEGIN + UPDATE osm_housenumber_point SET geometry=ST_PointOnSurface(geometry) WHERE ST_GeometryType(geometry) <> 'ST_Point'; +END; +$$ LANGUAGE plpgsql; + +SELECT convert_housenumber_point(); + +-- Handle updates + +CREATE SCHEMA IF NOT EXISTS housenumber; + +CREATE TABLE IF NOT EXISTS housenumber.updates(id serial primary key, t text, unique (t)); +CREATE OR REPLACE FUNCTION housenumber.flag() RETURNS trigger AS $$ +BEGIN + INSERT INTO housenumber.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN null; +END; +$$ language plpgsql; + +CREATE OR REPLACE FUNCTION housenumber.refresh() RETURNS trigger AS + $BODY$ + BEGIN + RAISE LOG 'Refresh housenumber'; + PERFORM convert_housenumber_point(); + DELETE FROM housenumber.updates; + RETURN null; + END; + $BODY$ +language plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE ON osm_housenumber_point + FOR EACH STATEMENT + EXECUTE PROCEDURE housenumber.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT ON housenumber.updates + INITIALLY DEFERRED + FOR EACH ROW + EXECUTE PROCEDURE housenumber.refresh(); diff --git a/layers/landcover/mapping.yaml b/layers/landcover/mapping.yaml index ffa6d7d..337ac5e 100644 --- a/layers/landcover/mapping.yaml +++ b/layers/landcover/mapping.yaml @@ -58,7 +58,7 @@ tables: key: wetland type: string - name: area - type: pseudoarea + type: webmerc_area mapping: landuse: - allotments diff --git a/layers/landuse/mapping.yaml b/layers/landuse/mapping.yaml index 3773a38..7527e8a 100644 --- a/layers/landuse/mapping.yaml +++ b/layers/landuse/mapping.yaml @@ -39,7 +39,7 @@ tables: key: leisure type: string - name: area - type: pseudoarea + type: webmerc_area mapping: amenity: - school diff --git a/layers/park/mapping.yaml b/layers/park/mapping.yaml index 29fb1f0..0649d9a 100644 --- a/layers/park/mapping.yaml +++ b/layers/park/mapping.yaml @@ -67,7 +67,7 @@ tables: key: boundary type: string - name: area - type: pseudoarea + type: webmerc_area mapping: leisure: - nature_reserve diff --git a/layers/place/island_polygon_update.sql b/layers/place/island_polygon_update.sql index 7b5c44c..91e6b36 100644 --- a/layers/place/island_polygon_update.sql +++ b/layers/place/island_polygon_update.sql @@ -1,5 +1,46 @@ --- etldoc: osm_island_polygon -> osm_island_polygon -UPDATE osm_island_polygon SET geometry=topoint(geometry) -WHERE ST_GeometryType(geometry) <> 'ST_Point'; +DROP TRIGGER IF EXISTS trigger_flag ON osm_island_polygon; +DROP TRIGGER IF EXISTS trigger_refresh ON place_island.updates; -ANALYZE osm_island_polygon; +-- etldoc: osm_island_polygon -> osm_island_polygon +CREATE OR REPLACE FUNCTION convert_island_polygon_point() RETURNS VOID AS $$ +BEGIN + UPDATE osm_island_polygon SET geometry=ST_PointOnSurface(geometry) WHERE ST_GeometryType(geometry) <> 'ST_Point'; + ANALYZE osm_island_polygon; +END; +$$ LANGUAGE plpgsql; + +SELECT convert_island_polygon_point(); + +-- Handle updates + +CREATE SCHEMA IF NOT EXISTS place_island; + +CREATE TABLE IF NOT EXISTS place_island.updates(id serial primary key, t text, unique (t)); +CREATE OR REPLACE FUNCTION place_island.flag() RETURNS trigger AS $$ +BEGIN + INSERT INTO place_island.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN null; +END; +$$ language plpgsql; + +CREATE OR REPLACE FUNCTION place_island.refresh() RETURNS trigger AS + $BODY$ + BEGIN + RAISE LOG 'Refresh place_island'; + PERFORM convert_island_polygon_point(); + DELETE FROM place_island.updates; + RETURN null; + END; + $BODY$ +language plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE ON osm_island_polygon + FOR EACH STATEMENT + EXECUTE PROCEDURE place_island.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT ON place_island.updates + INITIALLY DEFERRED + FOR EACH ROW + EXECUTE PROCEDURE place_island.refresh(); diff --git a/layers/place/layer.sql b/layers/place/layer.sql index 0fbe902..247d93b 100644 --- a/layers/place/layer.sql +++ b/layers/place/layer.sql @@ -21,7 +21,7 @@ RETURNS TABLE(osm_id bigint, geometry geometry, name text, name_en text, class t osm_id, geometry, name, COALESCE(NULLIF(name_en, ''), name) AS name_en, 'country' AS class, "rank", NULL::int AS capital FROM osm_country_point - WHERE geometry && bbox AND "rank" <= zoom_level AND name <> '' + WHERE geometry && bbox AND "rank" <= zoom_level + 1 AND name <> '' UNION ALL -- etldoc: osm_state_point -> layer_place:z0_3 diff --git a/layers/place/mapping.yaml b/layers/place/mapping.yaml index 6e42b65..5c7afa1 100644 --- a/layers/place/mapping.yaml +++ b/layers/place/mapping.yaml @@ -97,7 +97,7 @@ tables: - name: geometry type: geometry - name: area - type: pseudoarea + type: webmerc_area - *name - *name_en - *name_de diff --git a/layers/place/merge_city_rank.sql b/layers/place/merge_city_rank.sql index 9c68a7e..fa91994 100644 --- a/layers/place/merge_city_rank.sql +++ b/layers/place/merge_city_rank.sql @@ -1,38 +1,82 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_city_point; +DROP TRIGGER IF EXISTS trigger_refresh ON place_city.updates; CREATE EXTENSION IF NOT EXISTS unaccent; --- 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; +CREATE OR REPLACE FUNCTION update_osm_city_point() RETURNS VOID AS $$ +BEGIN --- etldoc: ne_10m_populated_places -> osm_city_point --- etldoc: osm_city_point -> osm_city_point + -- 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; -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 - ( - 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.meganame ILIKE osm.name OR - ne.meganame ILIKE osm.name_en OR - ne.gn_ascii ILIKE osm.name OR - ne.gn_ascii 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; + -- 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 + ( + 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.meganame ILIKE osm.name OR + ne.meganame ILIKE osm.name_en OR + ne.gn_ascii ILIKE osm.name OR + ne.gn_ascii 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; + +END; +$$ LANGUAGE plpgsql; + +SELECT update_osm_city_point(); 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 TABLE IF NOT EXISTS place_city.updates(id serial primary key, t text, unique (t)); +CREATE OR REPLACE FUNCTION place_city.flag() RETURNS trigger AS $$ +BEGIN + INSERT INTO place_city.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN null; +END; +$$ language plpgsql; + +CREATE OR REPLACE FUNCTION place_city.refresh() RETURNS trigger AS + $BODY$ + BEGIN + RAISE LOG 'Refresh place_city rank'; + PERFORM update_osm_city_point(); + DELETE FROM place_city.updates; + RETURN null; + END; + $BODY$ +language plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE ON osm_city_point + FOR EACH STATEMENT + EXECUTE PROCEDURE place_city.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT ON place_city.updates + INITIALLY DEFERRED + FOR EACH ROW + EXECUTE PROCEDURE place_city.refresh(); diff --git a/layers/place/merge_country_rank.sql b/layers/place/merge_country_rank.sql index f1489e3..afc14ea 100644 --- a/layers/place/merge_country_rank.sql +++ b/layers/place/merge_country_rank.sql @@ -1,34 +1,79 @@ +DROP TRIGGER IF EXISTS trigger_flag 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; -- etldoc: ne_10m_admin_0_countries -> osm_country_point -- etldoc: osm_country_point -> osm_country_point -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 only match whether the point is within the Natural Earth polygon - -- because name matching is to difficult since OSM does not contain good - -- enough coverage of ISO codesy - ST_Within(osm.geometry, ne.geometry) - -- We leave out tiny countries - AND ne.scalerank <= 1 -) -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; +CREATE OR REPLACE FUNCTION update_osm_country_point() RETURNS VOID AS $$ +BEGIN -UPDATE osm_country_point AS osm -SET "rank" = 6 -WHERE "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 only match whether the point is within the Natural Earth polygon + -- because name matching is to difficult since OSM does not contain good + -- enough coverage of ISO codesy + ST_Within(osm.geometry, ne.geometry) + -- We leave out tiny countries + AND ne.scalerank <= 1 + ) + 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; --- 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 AS osm + SET "rank" = 6 + WHERE "rank" IS NULL; -ALTER TABLE osm_country_point ADD CONSTRAINT osm_country_point_rank_constraint CHECK("rank" BETWEEN 1 AND 6); + -- TODO: This shouldn't be necessary? The rank function makes something wrong... + UPDATE osm_country_point AS osm + SET "rank" = 1 + WHERE "rank" = 0; + +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"); + +-- Handle updates + +CREATE SCHEMA IF NOT EXISTS place_country; + +CREATE TABLE IF NOT EXISTS place_country.updates(id serial primary key, t text, unique (t)); +CREATE OR REPLACE FUNCTION place_country.flag() RETURNS trigger AS $$ +BEGIN + INSERT INTO place_country.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN null; +END; +$$ language plpgsql; + +CREATE OR REPLACE FUNCTION place_country.refresh() RETURNS trigger AS + $BODY$ + BEGIN + RAISE LOG 'Refresh place_country rank'; + PERFORM update_osm_country_point(); + DELETE FROM place_country.updates; + RETURN null; + END; + $BODY$ +language plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE ON osm_country_point + FOR EACH STATEMENT + EXECUTE PROCEDURE place_country.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT ON place_country.updates + INITIALLY DEFERRED + FOR EACH ROW + EXECUTE PROCEDURE place_country.refresh(); diff --git a/layers/place/merge_state_rank.sql b/layers/place/merge_state_rank.sql index 54e6803..fcf3279 100644 --- a/layers/place/merge_state_rank.sql +++ b/layers/place/merge_state_rank.sql @@ -1,30 +1,75 @@ +DROP TRIGGER IF EXISTS trigger_flag 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; -- etldoc: ne_10m_admin_1_states_provinces_shp -> osm_state_point -- etldoc: osm_state_point -> osm_state_point -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_shp 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; +CREATE OR REPLACE FUNCTION update_osm_state_point() RETURNS VOID AS $$ +BEGIN --- TODO: This shouldn't be necessary? The rank function makes something wrong... -UPDATE osm_state_point AS osm -SET "rank" = 1 -WHERE "rank" = 0; + 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_shp 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; -DELETE FROM osm_state_point WHERE "rank" IS NULL; + -- TODO: This shouldn't be necessary? The rank function makes something wrong... + UPDATE osm_state_point AS osm + SET "rank" = 1 + WHERE "rank" = 0; -ALTER TABLE osm_state_point ADD CONSTRAINT osm_state_point_rank_constraint CHECK("rank" BETWEEN 1 AND 6); + DELETE FROM osm_state_point WHERE "rank" IS 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 $$ +BEGIN + INSERT INTO place_state.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN null; +END; +$$ language plpgsql; + +CREATE OR REPLACE FUNCTION place_state.refresh() RETURNS trigger AS + $BODY$ + BEGIN + RAISE LOG 'Refresh place_state rank'; + PERFORM update_osm_state_point(); + DELETE FROM place_state.updates; + RETURN null; + END; + $BODY$ +language plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE ON osm_state_point + FOR EACH STATEMENT + EXECUTE PROCEDURE place_state.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT ON place_state.updates + INITIALLY DEFERRED + FOR EACH ROW + EXECUTE PROCEDURE place_state.refresh(); diff --git a/layers/poi/poi_polygon_update.sql b/layers/poi/poi_polygon_update.sql index 1e5073d..891ae2e 100644 --- a/layers/poi/poi_polygon_update.sql +++ b/layers/poi/poi_polygon_update.sql @@ -1,5 +1,47 @@ --- etldoc: osm_poi_polygon -> osm_poi_polygon -UPDATE osm_poi_polygon SET geometry=topoint(geometry) -WHERE ST_GeometryType(geometry) <> 'ST_Point'; +DROP TRIGGER IF EXISTS trigger_flag ON osm_poi_polygon; +DROP TRIGGER IF EXISTS trigger_refresh ON poi.updates; -ANALYZE osm_poi_polygon; +-- etldoc: osm_poi_polygon -> osm_poi_polygon + +CREATE OR REPLACE FUNCTION convert_poi_point() RETURNS VOID AS $$ +BEGIN + UPDATE osm_poi_polygon SET geometry=ST_PointOnSurface(geometry) WHERE ST_GeometryType(geometry) <> 'ST_Point'; + ANALYZE osm_poi_polygon; +END; +$$ LANGUAGE plpgsql; + +SELECT convert_poi_point(); + +-- Handle updates + +CREATE SCHEMA IF NOT EXISTS poi; + +CREATE TABLE IF NOT EXISTS poi.updates(id serial primary key, t text, unique (t)); +CREATE OR REPLACE FUNCTION poi.flag() RETURNS trigger AS $$ +BEGIN + INSERT INTO poi.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN null; +END; +$$ language plpgsql; + +CREATE OR REPLACE FUNCTION poi.refresh() RETURNS trigger AS + $BODY$ + BEGIN + RAISE LOG 'Refresh poi'; + PERFORM convert_poi_point(); + DELETE FROM poi.updates; + RETURN null; + END; + $BODY$ +language plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE ON osm_poi_polygon + FOR EACH STATEMENT + EXECUTE PROCEDURE poi.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT ON poi.updates + INITIALLY DEFERRED + FOR EACH ROW + EXECUTE PROCEDURE poi.refresh(); diff --git a/layers/transportation/layer.sql b/layers/transportation/layer.sql index 65e24df..2765a1e 100644 --- a/layers/transportation/layer.sql +++ b/layers/transportation/layer.sql @@ -96,6 +96,7 @@ RETURNS TABLE(osm_id bigint, geometry geometry, class text, ramp int, oneway int ) UNION ALL + -- etldoc: osm_railway_linestring -> layer_transportation:z12" -- etldoc: osm_railway_linestring -> layer_transportation:z13 -- etldoc: osm_railway_linestring -> layer_transportation:z14_ SELECT @@ -103,7 +104,7 @@ RETURNS TABLE(osm_id bigint, geometry geometry, class text, ramp int, oneway int service_value(service) AS service, is_bridge, is_tunnel, is_ford, is_ramp, is_oneway, z_order FROM osm_railway_linestring - WHERE zoom_level = 13 AND (railway='rail' AND service = '') + WHERE zoom_level BETWEEN 12 AND 13 AND (railway='rail' AND service = '') OR zoom_Level >= 14 UNION ALL diff --git a/layers/transportation_name/merge_highways.sql b/layers/transportation_name/merge_highways.sql index c834c17..1be7840 100644 --- a/layers/transportation_name/merge_highways.sql +++ b/layers/transportation_name/merge_highways.sql @@ -1,16 +1,18 @@ - -DROP TABLE IF EXISTS osm_transportation_name_linestring CASCADE; -DROP TABLE IF EXISTS osm_transportation_name_linestring_gen1 CASCADE; -DROP TABLE IF EXISTS osm_transportation_name_linestring_gen2 CASCADE; -DROP TABLE IF EXISTS osm_transportation_name_linestring_gen3 CASCADE; +DROP TRIGGER IF EXISTS trigger_flag 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 +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; + -- etldoc: osm_highway_linestring -> osm_transportation_name_linestring -CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring AS ( +CREATE MATERIALIZED VIEW osm_transportation_name_linestring AS ( SELECT (ST_Dump(geometry)).geom AS geometry, -- NOTE: The osm_id is no longer the original one which can make it difficult @@ -18,14 +20,14 @@ CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring AS ( member_osm_ids[0] AS osm_id, member_osm_ids, name, - ref, + ref, highway, z_order FROM ( SELECT - ST_LineMerge(ST_Union(geometry)) AS geometry, + ST_LineMerge(ST_Collect(geometry)) AS geometry, name, - ref, + ref, highway, min(z_order) AS z_order, array_agg(DISTINCT osm_id) AS member_osm_ids @@ -35,11 +37,10 @@ CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring AS ( GROUP BY name, highway, ref ) AS highway_union ); - CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_geometry_idx ON osm_transportation_name_linestring USING gist(geometry); -- etldoc: osm_transportation_name_linestring -> osm_transportation_name_linestring_gen1 -CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen1 AS ( +CREATE MATERIALIZED VIEW osm_transportation_name_linestring_gen1 AS ( SELECT ST_Simplify(geometry, 50) AS geometry, osm_id, member_osm_ids, name, ref, highway, z_order FROM osm_transportation_name_linestring WHERE highway IN ('motorway','trunk') AND ST_Length(geometry) > 8000 @@ -47,7 +48,7 @@ CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen1 AS ( CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen1_geometry_idx ON osm_transportation_name_linestring_gen1 USING gist(geometry); -- etldoc: osm_transportation_name_linestring_gen1 -> osm_transportation_name_linestring_gen2 -CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen2 AS ( +CREATE MATERIALIZED VIEW osm_transportation_name_linestring_gen2 AS ( SELECT ST_Simplify(geometry, 120) AS geometry, osm_id, member_osm_ids, name, ref, highway, z_order FROM osm_transportation_name_linestring_gen1 WHERE highway IN ('motorway','trunk') AND ST_Length(geometry) > 14000 @@ -55,9 +56,46 @@ CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen2 AS ( CREATE INDEX IF NOT EXISTS osm_transportation_name_linestring_gen2_geometry_idx ON osm_transportation_name_linestring_gen2 USING gist(geometry); -- etldoc: osm_transportation_name_linestring_gen2 -> osm_transportation_name_linestring_gen3 -CREATE TABLE IF NOT EXISTS osm_transportation_name_linestring_gen3 AS ( +CREATE MATERIALIZED VIEW osm_transportation_name_linestring_gen3 AS ( SELECT ST_Simplify(geometry, 120) AS geometry, osm_id, member_osm_ids, name, ref, highway, z_order FROM osm_transportation_name_linestring_gen2 WHERE highway = '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); + +-- 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 $$ +BEGIN + INSERT INTO transportation_name.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN null; +END; +$$ language plpgsql; + +CREATE OR REPLACE FUNCTION transportation_name.refresh() RETURNS trigger AS + $BODY$ + BEGIN + RAISE LOG 'Refresh transportation_name'; + 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; + DELETE FROM transportation_name.updates; + RETURN null; + END; + $BODY$ +language plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE ON osm_highway_linestring + FOR EACH STATEMENT + EXECUTE PROCEDURE transportation_name.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT ON transportation_name.updates + INITIALLY DEFERRED + FOR EACH ROW + EXECUTE PROCEDURE transportation_name.refresh(); diff --git a/layers/water/mapping.yaml b/layers/water/mapping.yaml index b0bf580..d1ba1a5 100644 --- a/layers/water/mapping.yaml +++ b/layers/water/mapping.yaml @@ -40,7 +40,7 @@ tables: - name: geometry type: validated_geometry - name: area - type: pseudoarea + type: webmerc_area - key: name name: name type: string diff --git a/layers/water_name/layer.sql b/layers/water_name/layer.sql index 0bfac8b..c79e70d 100644 --- a/layers/water_name/layer.sql +++ b/layers/water_name/layer.sql @@ -12,12 +12,21 @@ RETURNS TABLE(osm_id bigint, geometry geometry, name text, name_en text, class t AND ((zoom_level BETWEEN 9 AND 13 AND LineLabel(zoom_level, NULLIF(name, ''), geometry)) OR (zoom_level >= 14)) -- etldoc: osm_water_point -> layer_water_name:z9_13 - -- etldoc: osm_water_point -> layer_water_name:z14_ + -- etldoc: osm_water_point -> layer_water_name:z14_ UNION ALL SELECT osm_id, geometry, name, name_en, 'lake'::text AS class 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) + ) + -- etldoc: osm_marine_point -> layer_water_name:z0_14_ + UNION ALL + SELECT osm_id, geometry, name, name_en, place::text AS class + FROM osm_marine_point + WHERE geometry && bbox AND ( + place = 'ocean' + OR (zoom_level >= 1 AND zoom_level <= "rank" AND "rank" IS NOT NULL) + OR (zoom_level >= 8) ); $$ LANGUAGE SQL IMMUTABLE; diff --git a/layers/water_name/mapping.yaml b/layers/water_name/mapping.yaml index fbeaf39..ecab8bf 100644 --- a/layers/water_name/mapping.yaml +++ b/layers/water_name/mapping.yaml @@ -16,6 +16,9 @@ tables: - name: place key: place type: string + - name: rank + key: rank + type: integer filters: require: name: ["__any__"] diff --git a/layers/water_name/merge_marine_rank.sql b/layers/water_name/merge_marine_rank.sql new file mode 100644 index 0000000..45566cf --- /dev/null +++ b/layers/water_name/merge_marine_rank.sql @@ -0,0 +1,61 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_marine_point; +DROP TRIGGER IF EXISTS trigger_refresh ON water_name_marine.updates; + +CREATE EXTENSION IF NOT EXISTS unaccent; + +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 + FROM ne_10m_geography_marine_polys AS ne, osm_marine_point AS osm + WHERE ne.name ILIKE osm.name + ) + UPDATE osm_marine_point AS osm + SET "rank" = scalerank + FROM important_marine_point AS ne + WHERE osm.osm_id = ne.osm_id; +END; +$$ LANGUAGE plpgsql; + +SELECT update_osm_marine_point(); + +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 $$ +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 + RAISE LOG 'Refresh water_name_marine rank'; + PERFORM update_osm_marine_point(); + DELETE FROM water_name_marine.updates; + RETURN null; + END; + $BODY$ +language plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE ON osm_marine_point + FOR EACH STATEMENT + EXECUTE PROCEDURE water_name_marine.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT ON water_name_marine.updates + INITIALLY DEFERRED + FOR EACH ROW + EXECUTE PROCEDURE water_name_marine.refresh(); diff --git a/layers/water_name/water_lakeline.sql b/layers/water_name/water_lakeline.sql index 16522e8..d9c58ea 100644 --- a/layers/water_name/water_lakeline.sql +++ b/layers/water_name/water_lakeline.sql @@ -1,9 +1,11 @@ - -DROP TABLE IF EXISTS osm_water_lakeline CASCADE; +DROP TRIGGER IF EXISTS trigger_flag_line ON osm_water_polygon; +DROP TRIGGER IF EXISTS trigger_refresh ON water_lakeline.updates; -- etldoc: osm_water_polygon -> osm_water_lakeline -- etldoc: lake_centerline -> osm_water_lakeline -CREATE TABLE IF NOT EXISTS osm_water_lakeline AS ( +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, ST_Area(wp.geometry) AS area @@ -11,5 +13,38 @@ CREATE TABLE IF NOT EXISTS osm_water_lakeline AS ( INNER JOIN lake_centerline ll ON wp.osm_id = ll.osm_id WHERE wp.name <> '' ); - 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 $$ +BEGIN + INSERT INTO water_lakeline.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + 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 + FOR EACH ROW + EXECUTE PROCEDURE water_lakeline.refresh(); diff --git a/layers/water_name/water_name.yaml b/layers/water_name/water_name.yaml index a1bd76b..ab9da59 100644 --- a/layers/water_name/water_name.yaml +++ b/layers/water_name/water_name.yaml @@ -12,13 +12,14 @@ layer: At the moment only `lake` since no ocean parts are labelled. *Reserved for future use*. values: - lake - buffer_size: 8 + buffer_size: 64 srs: +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over datasource: geometry_field: geometry srid: 900913 query: (SELECT geometry, name, name_en, class FROM layer_water_name(!bbox!, z(!scale_denominator!))) AS t schema: + - ./merge_marine_rank.sql - ./water_lakeline.sql - ./water_point.sql - ./layer.sql diff --git a/layers/water_name/water_point.sql b/layers/water_name/water_point.sql index 498a6e8..9c46e7f 100644 --- a/layers/water_name/water_point.sql +++ b/layers/water_name/water_point.sql @@ -1,15 +1,50 @@ - -DROP TABLE IF EXISTS osm_water_point CASCADE; +DROP TRIGGER IF EXISTS trigger_flag_point ON osm_water_polygon; +DROP TRIGGER IF EXISTS trigger_refresh ON water_point.updates; -- etldoc: osm_water_polygon -> osm_water_point -- etldoc: lake_centerline -> osm_water_point -CREATE TABLE IF NOT EXISTS osm_water_point AS ( +DROP MATERIALIZED VIEW IF EXISTS osm_water_point CASCADE; + +CREATE MATERIALIZED VIEW osm_water_point AS ( SELECT - wp.osm_id, topoint(wp.geometry) AS geometry, + wp.osm_id, ST_PointOnSurface(wp.geometry) AS geometry, wp.name, wp.name_en, ST_Area(wp.geometry) AS area 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 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 $$ +BEGIN + INSERT INTO water_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + 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 + FOR EACH ROW + EXECUTE PROCEDURE water_point.refresh(); diff --git a/layers/waterway/merge_waterway.sql b/layers/waterway/merge_waterway.sql index 604e004..5cf6ab0 100644 --- a/layers/waterway/merge_waterway.sql +++ b/layers/waterway/merge_waterway.sql @@ -1,15 +1,17 @@ - -DROP TABLE IF EXISTS osm_important_waterway_linestring CASCADE; -DROP TABLE IF EXISTS osm_important_waterway_linestring_gen1 CASCADE; -DROP TABLE IF EXISTS osm_important_waterway_linestring_gen2 CASCADE; -DROP TABLE IF EXISTS osm_important_waterway_linestring_gen3 CASCADE; +DROP TRIGGER IF EXISTS trigger_flag ON osm_waterway_linestring; +DROP TRIGGER IF EXISTS trigger_refresh ON waterway.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 -- etldoc: osm_waterway_linestring -> osm_important_waterway_linestring -CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring AS ( +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; + +CREATE MATERIALIZED VIEW osm_important_waterway_linestring AS ( SELECT (ST_Dump(geometry)).geom AS geometry, name @@ -22,11 +24,10 @@ CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring AS ( GROUP BY name ) AS waterway_union ); - CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_geometry_idx ON osm_important_waterway_linestring USING gist(geometry); -- etldoc: osm_important_waterway_linestring -> osm_important_waterway_linestring_gen1 -CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring_gen1 AS ( +CREATE MATERIALIZED VIEW osm_important_waterway_linestring_gen1 AS ( SELECT ST_Simplify(geometry, 60) AS geometry, name FROM osm_important_waterway_linestring WHERE ST_Length(geometry) > 1000 @@ -34,7 +35,7 @@ CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring_gen1 AS ( CREATE INDEX IF NOT EXISTS osm_important_waterway_linestring_gen1_geometry_idx ON osm_important_waterway_linestring_gen1 USING gist(geometry); -- etldoc: osm_important_waterway_linestring_gen1 -> osm_important_waterway_linestring_gen2 -CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring_gen2 AS ( +CREATE MATERIALIZED VIEW osm_important_waterway_linestring_gen2 AS ( SELECT ST_Simplify(geometry, 100) AS geometry, name FROM osm_important_waterway_linestring_gen1 WHERE ST_Length(geometry) > 4000 @@ -42,9 +43,49 @@ CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring_gen2 AS ( 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_gen2 -> osm_important_waterway_linestring_gen3 -CREATE TABLE IF NOT EXISTS osm_important_waterway_linestring_gen3 AS ( +CREATE MATERIALIZED VIEW osm_important_waterway_linestring_gen3 AS ( SELECT ST_Simplify(geometry, 200) AS geometry, name 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; + +CREATE TABLE IF NOT EXISTS waterway.updates(id serial primary key, t text, unique (t)); +CREATE OR REPLACE FUNCTION waterway.flag() RETURNS trigger AS $$ +BEGIN + INSERT INTO waterway.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN null; +END; +$$ language plpgsql; + +CREATE OR REPLACE FUNCTION waterway.refresh() RETURNS trigger AS + $BODY$ + 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; + DELETE FROM waterway.updates; + RETURN null; + END; + $BODY$ +language plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE ON osm_waterway_linestring + FOR EACH STATEMENT + EXECUTE PROCEDURE waterway.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT ON waterway.updates + INITIALLY DEFERRED + FOR EACH ROW + EXECUTE PROCEDURE waterway.refresh(); + + + diff --git a/openmaptiles.yaml b/openmaptiles.yaml index ded8574..c207884 100644 --- a/openmaptiles.yaml +++ b/openmaptiles.yaml @@ -16,7 +16,7 @@ tileset: - layers/poi/poi.yaml name: OpenMapTiles version: 3.3.0 - id: openmaptiles + id: openmaptiles description: "A tileset showcasing all layers in OpenMapTiles. http://openmaptiles.org" attribution: '© OpenMapTiles © OpenStreetMap contributors' center: [-12.2168, 28.6135, 4] diff --git a/quickstart.sh b/quickstart.sh index 1330d02..899e138 100755 --- a/quickstart.sh +++ b/quickstart.sh @@ -194,6 +194,14 @@ echo " : Data license: http://openstreetmapdata.com/info/license " echo " : Thank you: http://openstreetmapdata.com/info/supporting " docker-compose run --rm import-water +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Start importing border data from http://openstreetmap.org into PostgreSQL " +echo " : Source code: https://github.com/openmaptiles/import-osmborder" +echo " : Data license: http://www.openstreetmap.org/copyright" +echo " : Thank you: https://github.com/pnorman/osmborder " +docker-compose run --rm import-osmborder + echo " " echo "-------------------------------------------------------------------------------------" echo "====> : Start importing http://www.naturalearthdata.com into PostgreSQL " @@ -241,6 +249,12 @@ echo " : like : Mapnik LOG> ... is deprecated and will be removed in M docker-compose -f docker-compose.yml -f ./data/docker-compose-config.yml run --rm generate-vectortiles +echo " " +echo "-------------------------------------------------------------------------------------" +echo "====> : Add special metadata to mbtiles! " +docker-compose run --rm openmaptiles-tools generate-metadata ./data/tiles.mbtiles +docker-compose run --rm openmaptiles-tools chmod 666 ./data/tiles.mbtiles + echo " " echo "-------------------------------------------------------------------------------------" echo "====> : Stop PostgreSQL service ( but we keep PostgreSQL data volume for debugging )"