diff --git a/Makefile b/Makefile index 0c46c83..31dc6c8 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,9 @@ TILESET_FILE ?= openmaptiles.yaml # 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) +DC_USER?=openmaptiles +DC_PASSWORD?=openmaptiles + # If set to a non-empty value, will use postgis-preloaded instead of postgis docker image USE_PRELOADED_IMAGE ?= @@ -23,7 +26,7 @@ USE_PRELOADED_IMAGE ?= PPORT ?= 8090 export PPORT # Local port to use with tileserver -TPORT ?= 8080 +TPORT ?= 8081 export TPORT # Allow a custom docker-compose project name @@ -239,7 +242,7 @@ init-dirs: build/openmaptiles.tm2source/data.yml: init-dirs ifeq (,$(wildcard build/openmaptiles.tm2source/data.yml)) - $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools generate-tm2source $(TILESET_FILE) --host="postgres" --port=5432 --database="openmaptiles" --user="openmaptiles" --password="openmaptiles" > $@ + $(DOCKER_COMPOSE) run $(DC_OPTS) openmaptiles-tools generate-tm2source $(TILESET_FILE) --host="postgres" --port=5432 --database="openmaptiles" --user="$(DC_USER)" --password="$(DC_PASSWORD)" > $@ endif build/mapping.yaml: init-dirs diff --git a/QUICKSTART.md b/QUICKSTART.md index 07c3a30..1532ed0 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -380,7 +380,7 @@ and the generated maps are going to be available in browser on [localhost:8090/t start: * ` make start-tileserver` -and the generated maps are going to be available in webbrowser on [localhost:8080](http://localhost:8080/). +and the generated maps are going to be available in webbrowser on [localhost:8081](http://localhost:8081/). This is only a quick preview, because your mbtiles only generated to zoom level 7 ! @@ -422,7 +422,7 @@ Hints for testing areas Hints for designers: make start-postserve # start Postserver + Maputnik Editor [ see localhost:8088 ] - make start-tileserver # start maptiler/tileserver-gl [ see localhost:8080 ] + make start-tileserver # start maptiler/tileserver-gl [ see localhost:8081 ] Hints for developers: make # build source code diff --git a/layers/landmarks/README.md b/layers/landmarks/README.md new file mode 100644 index 0000000..96f85c9 --- /dev/null +++ b/layers/landmarks/README.md @@ -0,0 +1,10 @@ +## Landmarks + +### Docs +This is a custom layer including landmarks (named forests) that can not be classified as a POI + +### Mapping Diagram + + +### ETL diagram + diff --git a/layers/landmarks/class.sql b/layers/landmarks/class.sql new file mode 100644 index 0000000..6f8f747 --- /dev/null +++ b/layers/landmarks/class.sql @@ -0,0 +1,19 @@ +CREATE OR REPLACE FUNCTION lm_class_rank(class text) + RETURNS int AS +$$ +SELECT CASE class + WHEN 'forest' THEN 120 + ELSE 1000 + END; +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; + +CREATE OR REPLACE FUNCTION lm_class(subclass text, mapping_key text) + RETURNS text AS +$$ +SELECT CASE + %%FIELD_MAPPING: class %% + ELSE subclass + END; +$$ LANGUAGE SQL IMMUTABLE + PARALLEL SAFE; diff --git a/layers/landmarks/etl_diagram.png b/layers/landmarks/etl_diagram.png new file mode 100644 index 0000000..bbabf80 Binary files /dev/null and b/layers/landmarks/etl_diagram.png differ diff --git a/layers/landmarks/landmark.yaml b/layers/landmarks/landmark.yaml new file mode 100644 index 0000000..db9444f --- /dev/null +++ b/layers/landmarks/landmark.yaml @@ -0,0 +1,139 @@ +layer: + id: "landmarks" + description: | + [Points of interests](http://wiki.openstreetmap.org/wiki/Points_of_interest) containing + a of a variety of OpenStreetMap tags. Mostly contains amenities, sport, shop and tourist POIs. + buffer_size: 64 + srs: +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over + fields: + name: The OSM [`name`](http://wiki.openstreetmap.org/wiki/Key:name) value of the POI. + name_en: English name `name:en` if available, otherwise `name`. + name_de: German name `name:de` if available, otherwise `name` or `name:en`. + class: + description: | + More general classes of landmarks. If there is no more general `class` for the `subclass` + this field will contain the same value as `subclass`. + 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', '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'] + 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'] + subclass: + description: | + Original value of either the + [`amenity`](http://wiki.openstreetmap.org/wiki/Key:amenity), + [`barrier`](http://wiki.openstreetmap.org/wiki/Key:barrier), + [`historic`](http://wiki.openstreetmap.org/wiki/Key:historic), + [`information`](http://wiki.openstreetmap.org/wiki/Key:information), + [`landuse`](http://wiki.openstreetmap.org/wiki/Key:landuse), + [`leisure`](http://wiki.openstreetmap.org/wiki/Key:leisure), + [`railway`](http://wiki.openstreetmap.org/wiki/Key:railway), + [`shop`](http://wiki.openstreetmap.org/wiki/Key:shop), + [`sport`](http://wiki.openstreetmap.org/wiki/Key:sport), + [`station`](http://wiki.openstreetmap.org/wiki/Key:station), + [`religion`](http://wiki.openstreetmap.org/wiki/Key:religion), + [`tourism`](http://wiki.openstreetmap.org/wiki/Key:tourism), + [`aerialway`](http://wiki.openstreetmap.org/wiki/Key:aerialway), + [`building`](http://wiki.openstreetmap.org/wiki/Key:building), + [`highway`](http://wiki.openstreetmap.org/wiki/Key:highway) + or [`waterway`](http://wiki.openstreetmap.org/wiki/Key:waterway) + tag. Use this to do more precise styling. + rank: | + The POIs are ranked ascending according to their importance within a grid. The `rank` value shows the + local relative importance of a POI within it's cell in the grid. This can be used to reduce label density at *z14*. + Since all POIs already need to be contained at *z14* you can use `less than rank=10` epxression to limit + LMs. At some point like *z17* you can show all LMs. + level: + description: | + Original value of [`level`](http://wiki.openstreetmap.org/wiki/Key:level) tag. + layer: + description: | + Original value of [`layer`](http://wiki.openstreetmap.org/wiki/Key:layer) tag. + datasource: + geometry_field: geometry + key_field: osm_id + key_field_as_attribute: no + srid: 900913 + query: (SELECT osm_id, geometry, name, name_en, name_de, {name_languages}, class, subclass, layer, level, rank FROM layer_lm(!bbox!, z(!scale_denominator!), !pixel_width!)) AS t +schema: + - ./class.sql + - ./update_lm_polygon.sql + - ./update_lm_point.sql + - ./layer.sql +datasources: + - type: imposm3 + mapping_file: ./mapping.yaml diff --git a/layers/landmarks/layer.sql b/layers/landmarks/layer.sql new file mode 100644 index 0000000..e68dd27 --- /dev/null +++ b/layers/landmarks/layer.sql @@ -0,0 +1,85 @@ +-- etldoc: layer_lm[shape=record fillcolor=lightpink, style="rounded,filled", +-- etldoc: label="layer_lm | z12 | z13 | z14+" ] ; + +CREATE OR REPLACE FUNCTION layer_lm(bbox geometry, zoom_level integer, pixel_width numeric) + RETURNS TABLE + ( + osm_id bigint, + geometry geometry, + name text, + name_en text, + name_de text, + tags hstore, + class text, + subclass text, + layer integer, + level integer, + "rank" int + ) +AS +$$ +SELECT osm_id_hash AS osm_id, + geometry, + NULLIF(name, '') AS name, + COALESCE(NULLIF(name_en, ''), name) AS name_en, + COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de, + tags, + lm_class(subclass, mapping_key) AS class, + subclass AS subclass, + NULLIF(layer, 0) AS layer, + "level", + row_number() OVER ( + PARTITION BY LabelGrid(geometry, 100 * pixel_width) + ORDER BY CASE WHEN name = '' THEN 2000 ELSE lm_class_rank(lm_class(subclass, mapping_key)) END ASC + )::int AS "rank" +FROM ( + -- etldoc: osm_lm_point -> layer_lm:z12 + -- etldoc: osm_lm_point -> layer_lm:z13 + SELECT *, + osm_id * 10 AS osm_id_hash + FROM osm_lm_point + WHERE geometry && bbox + AND zoom_level BETWEEN 12 AND 13 + AND ((subclass = 'station' AND mapping_key = 'railway') + OR subclass IN ('halt', 'ferry_terminal')) + + UNION ALL + + -- etldoc: osm_lm_point -> layer_lm:z14_ + SELECT *, + osm_id * 10 AS osm_id_hash + FROM osm_lm_point + WHERE geometry && bbox + AND zoom_level >= 14 + + UNION ALL + + -- etldoc: osm_lm_polygon -> layer_lm:z12 + -- etldoc: osm_lm_polygon -> layer_lm:z13 + SELECT *, + CASE + WHEN osm_id < 0 THEN -osm_id * 10 + 4 + ELSE osm_id * 10 + 1 + END AS osm_id_hash + FROM osm_lm_polygon + WHERE geometry && bbox + AND zoom_level BETWEEN 12 AND 13 + AND ((subclass = 'station' AND mapping_key = 'railway') + OR subclass IN ('halt', 'ferry_terminal')) + + UNION ALL + + -- etldoc: osm_lm_polygon -> layer_lm:z14_ + SELECT *, + CASE + WHEN osm_id < 0 THEN -osm_id * 10 + 4 + ELSE osm_id * 10 + 1 + END AS osm_id_hash + FROM osm_lm_polygon + WHERE geometry && bbox + AND zoom_level >= 14 + ) AS lm_union +ORDER BY "rank" +$$ LANGUAGE SQL STABLE + PARALLEL SAFE; +-- TODO: Check if the above can be made STRICT -- i.e. if pixel_width could be NULL diff --git a/layers/landmarks/mapping.yaml b/layers/landmarks/mapping.yaml new file mode 100644 index 0000000..4fe2c74 --- /dev/null +++ b/layers/landmarks/mapping.yaml @@ -0,0 +1,74 @@ + +# imposm3 mapping file for https://github.com/osm2vectortiles/imposm3 +# Warning: this is not the official imposm3 + +# landuse values , see http://taginfo.openstreetmap.org/keys/landuse#values +def_lm_mapping_landuse: &lm_mapping_landuse + - forest + +def_poi_fields: &lm_fields + - 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: subclass + type: mapping_value + - name: mapping_key + type: mapping_key + - name: station + key: station + type: string + - name: funicular + key: funicular + type: string + - name: information + key: information + type: string + - name: uic_ref + key: uic_ref + type: string + - name: religion + key: religion + type: string + - name: level + key: level + type: integer + - name: layer + key: layer + type: integer + - name: sport + key: sport + type: string + +def_lm_mapping: &lm_mapping + landuse: *lm_mapping_landuse + +tables: + # etldoc: imposm3 -> osm_lm_point + lm_point: + type: point + columns: *lm_fields + filters: + require: + name: ["__any__"] + mapping: *lm_mapping + + # etldoc: imposm3 -> osm_lm_polygon + lm_polygon: + type: polygon + columns: *lm_fields + filters: + require: + name: ["__any__"] + mapping: *lm_mapping diff --git a/layers/landmarks/mapping_diagram.png b/layers/landmarks/mapping_diagram.png new file mode 100644 index 0000000..682751c Binary files /dev/null and b/layers/landmarks/mapping_diagram.png differ diff --git a/layers/landmarks/update_lm_point.sql b/layers/landmarks/update_lm_point.sql new file mode 100644 index 0000000..66d3eb3 --- /dev/null +++ b/layers/landmarks/update_lm_point.sql @@ -0,0 +1,69 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_lm_point; +DROP TRIGGER IF EXISTS trigger_refresh ON lm_point.updates; + +-- etldoc: osm_lm_point -> osm_lm_point +CREATE OR REPLACE FUNCTION update_osm_lm_point() RETURNS void AS +$$ +BEGIN + UPDATE osm_lm_point + SET subclass = 'subway' + WHERE station = 'subway' + AND subclass = 'station'; + + UPDATE osm_lm_point + SET subclass = 'halt' + WHERE funicular = 'yes' + AND subclass = 'station'; + + UPDATE osm_lm_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_lm_point(); + +-- Handle updates + +CREATE SCHEMA IF NOT EXISTS lm_point; + +CREATE TABLE IF NOT EXISTS lm_point.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION lm_point.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO lm_point.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION lm_point.refresh() RETURNS trigger AS +$$ +BEGIN + RAISE LOG 'Refresh lm_point'; + PERFORM update_osm_lm_point(); + REFRESH MATERIALIZED VIEW osm_lm_stop_centroid; + REFRESH MATERIALIZED VIEW osm_lm_stop_rank; + -- noinspection SqlWithoutWhere + DELETE FROM lm_point.updates; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE + ON osm_lm_point + FOR EACH STATEMENT +EXECUTE PROCEDURE lm_point.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON lm_point.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE lm_point.refresh(); diff --git a/layers/landmarks/update_lm_polygon.sql b/layers/landmarks/update_lm_polygon.sql new file mode 100644 index 0000000..2eb81ae --- /dev/null +++ b/layers/landmarks/update_lm_polygon.sql @@ -0,0 +1,78 @@ +DROP TRIGGER IF EXISTS trigger_flag ON osm_lm_polygon; +DROP TRIGGER IF EXISTS trigger_refresh ON lm_polygon.updates; + +-- etldoc: osm_lm_polygon -> osm_lm_polygon + +CREATE OR REPLACE FUNCTION update_lm_polygon() RETURNS void AS +$$ +BEGIN + UPDATE osm_lm_polygon + SET geometry = + CASE + WHEN ST_NPoints(ST_ConvexHull(geometry)) = ST_NPoints(geometry) + THEN ST_Centroid(geometry) + ELSE ST_PointOnSurface(geometry) + END + WHERE ST_GeometryType(geometry) <> 'ST_Point'; + + UPDATE osm_lm_polygon + SET subclass = 'subway' + WHERE station = 'subway' + AND subclass = 'station'; + + UPDATE osm_lm_polygon + SET subclass = 'halt' + WHERE funicular = 'yes' + AND subclass = 'station'; + + UPDATE osm_lm_polygon + SET tags = update_tags(tags, geometry) + WHERE COALESCE(tags->'name:latin', tags->'name:nonlatin', tags->'name_int') IS NULL; + + ANALYZE osm_lm_polygon; +END; +$$ LANGUAGE plpgsql; + +SELECT update_lm_polygon(); + +-- Handle updates + +CREATE SCHEMA IF NOT EXISTS lm_polygon; + +CREATE TABLE IF NOT EXISTS lm_polygon.updates +( + id serial PRIMARY KEY, + t text, + UNIQUE (t) +); +CREATE OR REPLACE FUNCTION lm_polygon.flag() RETURNS trigger AS +$$ +BEGIN + INSERT INTO lm_polygon.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION lm_polygon.refresh() RETURNS trigger AS +$$ +BEGIN + RAISE LOG 'Refresh lm_polygon'; + PERFORM update_lm_polygon(); + -- noinspection SqlWithoutWhere + DELETE FROM lm_polygon.updates; + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE + ON osm_lm_polygon + FOR EACH STATEMENT +EXECUTE PROCEDURE lm_polygon.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT + ON lm_polygon.updates + INITIALLY DEFERRED + FOR EACH ROW +EXECUTE PROCEDURE lm_polygon.refresh(); diff --git a/layers/landuse/landuse.sql b/layers/landuse/landuse.sql index f37c65b..d578a85 100644 --- a/layers/landuse/landuse.sql +++ b/layers/landuse/landuse.sql @@ -12,6 +12,7 @@ SELECT NULL::text AS tourism, NULL::text AS place, NULL::text AS waterway, + NULL::text AS man_made, scalerank FROM ne_50m_urban_areas ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; @@ -29,7 +30,8 @@ SELECT leisure, tourism, place, - waterway + waterway, + man_made FROM ne_50m_urban_areas_gen_z5 WHERE scalerank <= 2 ) /* DELAY_MATERIALIZED_VIEW_CREATION */ ; @@ -55,7 +57,8 @@ SELECT osm_id, NULLIF(leisure, ''), NULLIF(tourism, ''), NULLIF(place, ''), - NULLIF(waterway, '') + NULLIF(waterway, ''), + NULLIF(man_made, '') ) AS class FROM ( -- etldoc: ne_50m_urban_areas_gen_z4 -> layer_landuse:z4 @@ -66,7 +69,8 @@ FROM ( leisure, tourism, place, - waterway + waterway, + man_made FROM ne_50m_urban_areas_gen_z4 WHERE zoom_level = 4 UNION ALL @@ -78,7 +82,8 @@ FROM ( leisure, tourism, place, - waterway + waterway, + man_made FROM ne_50m_urban_areas_gen_z5 WHERE zoom_level = 5 UNION ALL @@ -90,7 +95,8 @@ FROM ( leisure, tourism, place, - waterway + waterway, + man_made FROM osm_landuse_polygon_gen_z6 WHERE zoom_level = 6 UNION ALL @@ -102,7 +108,8 @@ FROM ( leisure, tourism, place, - waterway + waterway, + man_made FROM osm_landuse_polygon_gen_z7 WHERE zoom_level = 7 UNION ALL @@ -114,7 +121,8 @@ FROM ( leisure, tourism, place, - waterway + waterway, + man_made FROM osm_landuse_polygon_gen_z8 WHERE zoom_level = 8 UNION ALL @@ -126,7 +134,8 @@ FROM ( leisure, tourism, place, - waterway + waterway, + man_made FROM osm_landuse_polygon_gen_z9 WHERE zoom_level = 9 UNION ALL @@ -138,7 +147,8 @@ FROM ( leisure, tourism, place, - waterway + waterway, + man_made FROM osm_landuse_polygon_gen_z10 WHERE zoom_level = 10 UNION ALL @@ -150,7 +160,8 @@ FROM ( leisure, tourism, place, - waterway + waterway, + man_made FROM osm_landuse_polygon_gen_z11 WHERE zoom_level = 11 UNION ALL @@ -162,7 +173,8 @@ FROM ( leisure, tourism, place, - waterway + waterway, + man_made FROM osm_landuse_polygon_gen_z12 WHERE zoom_level = 12 UNION ALL @@ -174,7 +186,8 @@ FROM ( leisure, tourism, place, - waterway + waterway, + man_made FROM osm_landuse_polygon_gen_z13 WHERE zoom_level = 13 UNION ALL @@ -186,7 +199,8 @@ FROM ( leisure, tourism, place, - waterway + waterway, + man_made FROM osm_landuse_polygon WHERE zoom_level >= 14 ) AS zoom_levels diff --git a/layers/landuse/landuse.yaml b/layers/landuse/landuse.yaml index 7a6bf74..bec0f6c 100644 --- a/layers/landuse/landuse.yaml +++ b/layers/landuse/landuse.yaml @@ -41,6 +41,14 @@ layer: - quarter - neighbourhood - dam + - sports_centre + - parking + - motorcycle_parking + - bicycle_parking + - religious + - prison + - wastewater_plant + - water_works datasource: geometry_field: geometry query: (SELECT geometry, class FROM layer_landuse(!bbox!, z(!scale_denominator!))) AS t diff --git a/layers/landuse/mapping.yaml b/layers/landuse/mapping.yaml index b3b7621..4811850 100644 --- a/layers/landuse/mapping.yaml +++ b/layers/landuse/mapping.yaml @@ -69,6 +69,9 @@ tables: type: string - name: area type: area + - name: man_made + key: man_made + type: string mapping: landuse: - railway @@ -80,6 +83,8 @@ tables: - industrial - garages - retail + - religious + - construction amenity: - bus_station - school @@ -88,17 +93,27 @@ tables: - college - library - hospital + - parking + - prison + - motorcycle_parking + - bicycle_parking + - animal_training leisure: - stadium - pitch - playground - track + - sports_centre tourism: - theme_park - zoo + - picnic_site place: - suburb - quarter - neighbourhood waterway: - dam + man_made: + - wastewater_plant + - water_works diff --git a/layers/poi/class.sql b/layers/poi/class.sql index a36fbdf..b26c69c 100644 --- a/layers/poi/class.sql +++ b/layers/poi/class.sql @@ -1,12 +1,21 @@ -CREATE OR REPLACE FUNCTION poi_class_rank(class text) +CREATE OR REPLACE FUNCTION poi_class_rank(class text, subclass text) RETURNS int AS $$ SELECT CASE class WHEN 'hospital' THEN 20 + WHEN 'airport' THEN 30 WHEN 'railway' THEN 40 - WHEN 'bus' THEN 50 - WHEN 'attraction' THEN 70 - WHEN 'harbor' THEN 75 + WHEN 'aerialway' THEN 40 + WHEN 'heliport' THEN 45 + WHEN 'taxi' THEN 50 + WHEN 'harbor' THEN 55 + WHEN 'library' THEN 60 + WHEN 'bus' THEN + CASE subclass + WHEN 'bus_station' THEN 70 + ELSE 72 + END + WHEN 'attraction' THEN 75 WHEN 'college' THEN 80 WHEN 'school' THEN 85 WHEN 'stadium' THEN 90 @@ -19,17 +28,51 @@ SELECT CASE class 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 + WHEN 'entrance' THEN 250 + WHEN 'parking' THEN 300 + WHEN 'car_parking' THEN 300 + WHEN 'fuel' THEN 350 + WHEN 'charging_station' THEN 355 + WHEN 'bicycle_parking' THEN 360 + WHEN 'motorcycle_parking' THEN 360 + WHEN 'bank' THEN 380 + WHEN 'art_gallery' THEN 400 + WHEN 'information' THEN 420 + WHEN 'fast_food' THEN 430 + WHEN 'ice_cream' THEN 430 + WHEN 'bar' THEN 450 + WHEN 'cafe' THEN 450 + WHEN 'grocery' THEN 450 + WHEN 'bakery' THEN 475 + WHEN 'community_centre' THEN 500 + WHEN 'shop' THEN 600 + WHEN 'optician' THEN 600 + WHEN 'furniture' THEN 600 + WHEN 'jewelry' THEN 600 + WHEN 'toys' THEN 600 + WHEN 'newsagent' THEN 600 + WHEN 'paint' THEN 600 + WHEN 'electronics' THEN 600 + WHEN 'garden_centre' THEN 600 + WHEN 'mobile_phone' THEN 600 + WHEN 'shoes' THEN 600 + WHEN 'clothing_store' THEN 600 + WHEN 'florist' THEN 600 + WHEN 'laundry' THEN 700 + WHEN 'dog_park' THEN 800 + WHEN 'pitch' THEN 800 + WHEN 'power_tower' then 900 + WHEN 'wind_mill' then 900 + WHEN 'water_tower' then 900 + WHEN 'communications_tower' then 900 + WHEN 'wind_turbine' then 900 + WHEN 'shelter' then 3000 ELSE 1000 END; $$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE; -CREATE OR REPLACE FUNCTION poi_class(subclass text, mapping_key text) +CREATE OR REPLACE FUNCTION poi_class(subclass text, mapping_key text, subtype text) RETURNS text AS $$ SELECT CASE diff --git a/layers/poi/mapping.yaml b/layers/poi/mapping.yaml index 094486d..f99769a 100644 --- a/layers/poi/mapping.yaml +++ b/layers/poi/mapping.yaml @@ -21,6 +21,7 @@ def_poi_mapping_amenity: &poi_mapping_amenity - clinic - college - community_centre + - social_facility - courthouse - dentist - doctors @@ -62,6 +63,7 @@ def_poi_mapping_amenity: &poi_mapping_amenity - university - veterinary - waste_basket + - charging_station # barrier values , see http://taginfo.openstreetmap.org/keys/barrier#values def_poi_mapping_barrier: &poi_mapping_barrier @@ -77,10 +79,13 @@ def_poi_mapping_barrier: &poi_mapping_barrier # building values , see http://taginfo.openstreetmap.org/keys/building#values def_poi_mapping_building: &poi_mapping_building - dormitory + - office + - industrial # highway values , see http://taginfo.openstreetmap.org/keys/highway#values def_poi_mapping_highway: &poi_mapping_highway - bus_stop + - speed_camera # historic values , see http://taginfo.openstreetmap.org/keys/historic#values def_poi_mapping_historic: &poi_mapping_historic @@ -114,6 +119,7 @@ def_poi_mapping_leisure: &poi_mapping_leisure - swimming_area - swimming_pool - water_park + - nature_reserve # railway values , see http://taginfo.openstreetmap.org/keys/railway#values def_poi_mapping_railway: &poi_mapping_railway @@ -316,6 +322,24 @@ def_poi_mapping_tourism: &poi_mapping_tourism def_poi_mapping_waterway: &poi_mapping_waterway - dock +# aeroway values , see http://taginfo.openstreetmap.org/keys/aeroway#values +def_poi_mapping_aeroway: &poi_mapping_aeroway + - helipad + - aerodrome + +# aeroway values , see http://taginfo.openstreetmap.org/keys/aeroway#values +def_poi_mapping_power: &poi_mapping_power + - generator + - tower + +def_poi_mapping_man_made: &poi_mapping_man_made + - communications_tower + - water_tower + - wind_mill + +def_poi_mapping_emergency: &poi_mapping_emergency + - defibrillator + def_poi_fields: &poi_fields - name: osm_id type: id @@ -363,6 +387,12 @@ def_poi_fields: &poi_fields - name: sport key: sport type: string + - name: power + key: power + type: string + - name: source + key: "generator:source" + type: string def_poi_mapping: &poi_mapping aerialway: *poi_mapping_aerialway @@ -378,6 +408,10 @@ def_poi_mapping: &poi_mapping sport: *poi_mapping_sport tourism: *poi_mapping_tourism waterway: *poi_mapping_waterway + aeroway: *poi_mapping_aeroway + power: *poi_mapping_power + man_made: *poi_mapping_man_made + emergency: *poi_mapping_emergency tables: # etldoc: imposm3 -> osm_poi_point diff --git a/layers/poi/poi.sql b/layers/poi/poi.sql index aecd23f..8aa92ca 100644 --- a/layers/poi/poi.sql +++ b/layers/poi/poi.sql @@ -26,7 +26,7 @@ SELECT osm_id_hash AS osm_id, 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, + poi_class(subclass, mapping_key, subtype) AS class, CASE WHEN subclass = 'information' THEN NULLIF(information, '') @@ -34,6 +34,8 @@ SELECT osm_id_hash AS osm_id, THEN NULLIF(religion, '') WHEN subclass = 'pitch' THEN NULLIF(sport, '') + WHEN subclass = 'sports_centre' + THEN NULLIF(sport, subclass) ELSE subclass END AS subclass, agg_stop, @@ -42,9 +44,33 @@ SELECT osm_id_hash AS osm_id, CASE WHEN indoor = TRUE THEN 1 END AS indoor, row_number() OVER ( PARTITION BY LabelGrid(geometry, 100 * pixel_width) - ORDER BY CASE WHEN name = '' THEN 2000 ELSE poi_class_rank(poi_class(subclass, mapping_key)) END ASC - )::int AS "rank" + ORDER BY + CASE + WHEN name = '' + THEN 2000 + ELSE poi_class_rank(poi_class( + subclass, + mapping_key, + subtype + ), subclass) END ASC + )::int AS "rank" FROM ( + -- Intermediate mapping for subtype and filtering out nameless industrial/office buildings + SELECT *, + CASE + WHEN subclass = 'information' + THEN NULLIF(information, '') + WHEN subclass = 'place_of_worship' + THEN NULLIF(religion, '') + WHEN subclass = 'pitch' + THEN NULLIF(sport, '') + WHEN subclass = 'sports_centre' + THEN NULLIF(sport, '') + WHEN subclass = 'generator' AND mapping_key = 'power' + THEN NULLIF(source, '') + ELSE subclass + END as subtype + FROM ( -- etldoc: osm_poi_point -> layer_poi:z12 -- etldoc: osm_poi_point -> layer_poi:z13 SELECT *, @@ -92,7 +118,9 @@ FROM ( FROM osm_poi_polygon WHERE geometry && bbox AND zoom_level >= 14 - ) AS poi_union + ) AS poi_union_raw + WHERE NOT (mapping_key = 'building' AND (subclass = 'office' OR subclass = 'industrial') AND coalesce(name, name_en, '') = '') +) AS poi_union ORDER BY "rank" $$ LANGUAGE SQL STABLE PARALLEL SAFE; diff --git a/layers/poi/poi.yaml b/layers/poi/poi.yaml index e6597b1..e4770aa 100644 --- a/layers/poi/poi.yaml +++ b/layers/poi/poi.yaml @@ -18,15 +18,41 @@ layer: 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', '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'] + 'coffee', 'computer', 'convenience', 'copyshop', 'cosmetics', + 'erotic', 'fabric', 'frozen_food', 'video_games', 'video', 'general', 'gift', + 'hearing_aids', 'hifi', 'interior_decoration', 'kiosk', 'lamps', 'mall', 'massage','outdoor', + 'perfumery', 'perfume', 'pet', 'photo', 'second_hand', 'sports', 'stationery', 'tailor', 'tattoo', + 'ticket', 'tobacco', 'travel_agency', 'watches', 'weapons', 'wholesale'] + optician: + subclass: ['optician'] + toys: + subclass: ['toys'] + jewelry: + subclass: ['jewelry'] + furniture: + subclass: ['furniture'] + newsagent: + subclass: ['newsagent'] + paint: + subclass: ['paint'] + beverages: + subclass: ['beverages'] + electronics: + subclass: ['electronics'] + garden_centre: + subclass: ['garden_centre'] + mobile_phone: + subclass: ['mobile_phone'] + shoes: + subclass: ['shoes'] + hardware: + subclass: ['hardware', 'doityourself'] + florist: + subclass: ['florist'] town_hall: - subclass: ['townhall', 'public_building', 'courthouse', 'community_centre'] + subclass: ['townhall', 'public_building', 'courthouse'] + community_centre: + subclass: ['community_centre', 'social_facility'] golf: subclass: ['golf', 'golf_course', 'miniature_golf'] fast_food: @@ -59,7 +85,7 @@ layer: lodging: subclass: ['hotel', 'motel', 'bed_and_breakfast', 'guest_house', 'hostel', 'chalet', 'alpine_hut', 'dormitory'] ice_cream: - subclass: ['chocolate', 'confectionery'] + subclass: ['chocolate', 'confectionery', 'ice_cream'] post: subclass: ['post_box', 'post_office'] cafe: @@ -72,8 +98,14 @@ layer: subclass: ['bar', 'nightclub'] harbor: subclass: ['marina', 'dock'] - car: - subclass: ['car', 'car_repair', 'car_parts', 'taxi'] + taxi: + subclass: ['taxi'] + motorcycle_dealer: + subclass: ['motorcycle'] + car_dealer: + subclass: ['car', 'car_parts'] + car_repair: + subclass: ['car_repair'] hospital: subclass: ['hospital', 'nursing_home', 'clinic'] cemetery: @@ -94,6 +126,25 @@ layer: subclass: ['swimming_area', 'swimming'] castle: subclass: ['castle', 'ruins'] + airport: + subclass: ['aerodrome'] + heliport: + subclass: ['helipad'] + wind_turbine: + __AND__: + subclass: ['generator'] + subtype: ['wind'] + mapping_key: ['power'] + communications_tower: + subclass: ['communications_tower'] + water_tower: + subclass: ['water_tower'] + wind_mill: + subclass: ['wind_mill'] + power_tower: + subclass: ['tower'] + industry: + subclass: ['industrial'] subclass: description: | Original value of either the diff --git a/openmaptiles.yaml b/openmaptiles.yaml index 21df9e1..094bd8f 100644 --- a/openmaptiles.yaml +++ b/openmaptiles.yaml @@ -15,6 +15,7 @@ tileset: - layers/place/place.yaml - layers/housenumber/housenumber.yaml - layers/poi/poi.yaml + - layers/landmarks/landmark.yaml - layers/aerodrome_label/aerodrome_label.yaml name: OpenMapTiles version: 3.12.1 diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..0452440 --- /dev/null +++ b/test.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -e + +# Test script for generating tiles for Lummen and region (e.g. including Hasselt) +export SOURCE=osmfr +export AREA=europe/belgium/flanders +export BBOX=4.964926,50.882471,5.411252,51.071236 +# Which zooms to generate in make generate-tiles +export MIN_ZOOM=0 +export MAX_ZOOM=16 + +# Update the .env to match +sed -i "s/MIN_ZOOM=.*/MIN_ZOOM=${MIN_ZOOM}/" .env +sed -i "s/MAX_ZOOM=.*/MAX_ZOOM=${MAX_ZOOM}/" .env +sed -i "s/BBOX=.*/BBOX=${BBOX}/" .env + + +# Setup +make clean +make DC_OPTS=--rm + +# Start from a clean db +make start-db +make destroy-db +make import-data +# ALTERNATIVE would be this +#make start-db-preloaded + +# Download +make download-${SOURCE} area="${AREA}" + +# Import (yes we're doing import borders twice, it crashes on first run, oddly enough) +make import-osm +make import-borders || true +make import-borders +make import-wikidata +make import-sql +make analyze-db + +# (This potentially screws stuff up?!) +#rm -rf data/${AREA}.dc-config.yml +#make generate-dc-config + +# Generate +make generate-tiles + +make stop-db \ No newline at end of file