Initial attempt at moving forests with names into their own 'landmarks' layer. (this is heavely copied and pasted from POI)

This commit is contained in:
Miel Truyen 2020-07-03 15:23:51 +02:00
parent 58d997b3ea
commit d638b3ef17
13 changed files with 570 additions and 1 deletions

View File

@ -0,0 +1,10 @@
## lm
### Docs
Read the layer documentation at **http://openmaptiles.org/schema#lm**
### Mapping Diagram
![Mapping diagram for lm](mapping_diagram.png?raw=true)
### ETL diagram
![ETL diagram for lm](etl_diagram.png?raw=true)

View File

@ -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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,156 @@
layer:
id: "lm"
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 POIs. If there is no more general `class` for the `subclass`
this field will contain the same value as `subclass`.
But for example for schools you only need to style the class `school` to filter the subclasses `school`
and `kindergarten`. Or use the class `shop` to style all shops.
values:
shop:
subclass: ['accessories', 'antiques', 'beauty', 'bed', 'boutique', 'camera', 'carpet', 'charity', 'chemist',
'coffee', 'computer', 'convenience', 'copyshop', 'cosmetics', 'garden_centre', 'doityourself',
'erotic', 'electronics', 'fabric', 'florist', 'frozen_food', 'furniture', 'video_games', 'video',
'general', 'gift', 'hardware', 'hearing_aids', 'hifi', 'ice_cream', 'interior_decoration',
'jewelry', 'kiosk', '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.
agg_stop:
description: |
Experimental feature! Indicates main platform of public transport
stops (buses, trams, and subways). Grouping of platforms is
implemented using
[`uic_ref`](http://wiki.openstreetmap.org/wiki/Key:uic_ref) tag that
is not used worldwide.
values: [1]
level:
description: |
Original value of [`level`](http://wiki.openstreetmap.org/wiki/Key:level) tag.
layer:
description: |
Original value of [`layer`](http://wiki.openstreetmap.org/wiki/Key:layer) tag.
indoor:
description: |
Original value of [`indoor`](http://wiki.openstreetmap.org/wiki/Key:indoor) tag.
values:
- 1
datasource:
geometry_field: geometry
key_field: osm_id
key_field_as_attribute: no
srid: 900913
query: (SELECT osm_id, geometry, name, name_en, name_de, {name_languages}, class, subclass, agg_stop, layer, level, indoor, rank FROM layer_lm(!bbox!, z(!scale_denominator!), !pixel_width!)) AS t
schema:
- ./public_transport_stop_type.sql
- ./class.sql
- ./lm_stop_agg.sql
- ./update_lm_polygon.sql
- ./update_lm_point.sql
- ./layer.sql
datasources:
- type: imposm3
mapping_file: ./mapping.yaml

View File

@ -0,0 +1,90 @@
-- etldoc: layer_lm[shape=record fillcolor=lightpink, style="rounded,filled",
-- etldoc: label="layer_lm | <z12> z12 | <z13> z13 | <z14_> 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,
agg_stop integer,
layer integer,
level integer,
indoor integer,
"rank" int
)
AS
$$
SELECT osm_id_hash AS osm_id,
geometry,
NULLIF(name, '') AS name,
COALESCE(NULLIF(name_en, ''), name) AS name_en,
COALESCE(NULLIF(name_de, ''), name, name_en) AS name_de,
tags,
lm_class(subclass, mapping_key) AS class,
subclass AS subclass,
agg_stop,
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 *,
NULL::integer AS agg_stop,
CASE
WHEN osm_id < 0 THEN -osm_id * 10 + 4
ELSE osm_id * 10 + 1
END AS osm_id_hash
FROM osm_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 *,
NULL::integer AS agg_stop,
CASE
WHEN osm_id < 0 THEN -osm_id * 10 + 4
ELSE osm_id * 10 + 1
END AS osm_id_hash
FROM osm_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

View File

@ -0,0 +1,31 @@
DROP MATERIALIZED VIEW IF EXISTS osm_lm_stop_centroid CASCADE;
CREATE MATERIALIZED VIEW osm_lm_stop_centroid AS
(
SELECT uic_ref,
count(*) AS count,
CASE WHEN count(*) > 2 THEN ST_Centroid(ST_UNION(geometry)) END AS centroid
FROM osm_lm_point
WHERE nullif(uic_ref, '') IS NOT NULL
AND subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway')
GROUP BY uic_ref
HAVING count(*) > 1
) /* DELAY_MATERIALIZED_VIEW_CREATION */;
DROP MATERIALIZED VIEW IF EXISTS osm_lm_stop_rank CASCADE;
CREATE MATERIALIZED VIEW osm_lm_stop_rank AS
(
SELECT p.osm_id,
-- p.uic_ref,
-- p.subclass,
ROW_NUMBER()
OVER (
PARTITION BY p.uic_ref
ORDER BY
p.subclass :: public_transport_stop_type NULLS LAST,
ST_Distance(c.centroid, p.geometry)
) AS rk
FROM osm_lm_point p
INNER JOIN osm_lm_stop_centroid c ON (p.uic_ref = c.uic_ref)
WHERE subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway')
ORDER BY p.uic_ref, rk
) /* DELAY_MATERIALIZED_VIEW_CREATION */;

View File

@ -0,0 +1,77 @@
# 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: indoor
key: indoor
type: bool
- 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -0,0 +1,12 @@
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1
FROM pg_type
WHERE typname = 'public_transport_stop_type') THEN
CREATE TYPE public_transport_stop_type AS enum (
'subway', 'tram_stop', 'bus_station', 'bus_stop'
);
END IF;
END
$$;

View File

@ -0,0 +1,96 @@
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();
CREATE OR REPLACE FUNCTION update_osm_lm_point_agg() RETURNS void AS
$$
BEGIN
UPDATE osm_lm_point p
SET agg_stop = CASE
WHEN p.subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway')
THEN 1
END;
UPDATE osm_lm_point p
SET agg_stop = (
CASE
WHEN p.subclass IN ('bus_stop', 'bus_station', 'tram_stop', 'subway')
AND r.rk IS NULL OR r.rk = 1
THEN 1
END)
FROM osm_lm_stop_rank r
WHERE p.osm_id = r.osm_id;
END;
$$ LANGUAGE plpgsql;
ALTER TABLE osm_lm_point
ADD COLUMN IF NOT EXISTS agg_stop integer DEFAULT NULL;
SELECT update_osm_lm_point_agg();
-- 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;
PERFORM update_osm_lm_point_agg();
-- 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();

View File

@ -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();

View File

@ -95,7 +95,6 @@ def_poi_mapping_landuse: &poi_mapping_landuse
- cemetery
- reservoir
- winter_sports
- forest
# leisure values , see http://taginfo.openstreetmap.org/keys/leisure#values
def_poi_mapping_leisure: &poi_mapping_leisure

View File

@ -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.11.0