diff --git a/layers/building/building.sql b/layers/building/building.sql index e27093101..d1a92e86c 100644 --- a/layers/building/building.sql +++ b/layers/building/building.sql @@ -1,3 +1,5 @@ +--\timing on + -- etldoc: layer_building[shape=record fillcolor=lightpink, style="rounded,filled", -- etldoc: label="layer_building | z13 | z14+ " ] ; @@ -75,11 +77,11 @@ CREATE OR REPLACE FUNCTION layer_building(bbox geometry, zoom_level int) RETURNS TABLE(geometry geometry, osm_id bigint, render_height int, render_min_height int) AS $$ SELECT geometry, osm_id, render_height, render_min_height FROM ( - -- etldoc: osm_building_polygon_gen1 -> layer_building:z13 + -- etldoc: osm_building_block_gen1 -> layer_building:z13 SELECT osm_id, geometry, NULL::int AS render_height, NULL::int AS render_min_height - FROM osm_building_polygon_gen1 + FROM osm_building_block_gen1 WHERE zoom_level = 13 AND geometry && bbox UNION ALL -- etldoc: osm_building_polygon -> layer_building:z14_ diff --git a/layers/building/building.yaml b/layers/building/building.yaml index 7e391b59a..9461168f2 100644 --- a/layers/building/building.yaml +++ b/layers/building/building.yaml @@ -12,6 +12,7 @@ layer: render_min_height: | An approximated height from levels and height of the bottom of the building or building:part after the method of Paul Norman in [OSM Clear](https://github.com/ClearTables/osm-clear). For future 3D rendering of buildings. schema: + - ./update_building.sql - ./building.sql datasources: - type: imposm3 diff --git a/layers/building/mapping.yaml b/layers/building/mapping.yaml index b12169d45..ca9d2b644 100644 --- a/layers/building/mapping.yaml +++ b/layers/building/mapping.yaml @@ -1,9 +1,9 @@ -generalized_tables: - # etldoc: imposm3 -> osm_building_polygon_gen1 - building_polygon_gen1: - source: building_polygon - sql_filter: area>power(ZRES12,2) AND ST_IsValid(geometry) - tolerance: ZRES14 +#generalized_tables: +# # etldoc: imposm3 -> osm_building_polygon_gen1 +# building_polygon_gen1: +# source: building_polygon +# sql_filter: area>power(ZRES12,2) AND ST_IsValid(geometry) +# tolerance: ZRES14 tables: # etldoc: imposm3 -> osm_building_polygon diff --git a/layers/building/update_building.sql b/layers/building/update_building.sql new file mode 100644 index 000000000..a67c24ddf --- /dev/null +++ b/layers/building/update_building.sql @@ -0,0 +1,136 @@ +DROP TRIGGER IF EXISTS trigger_refresh ON buildings.updates; +DROP TRIGGER IF EXISTS trigger_flag ON osm_building_polygon; + +--creating aggregated building blocks with removed small polzgons and small +--holes. Aggregated polzgons are simplified. + +--function returning recordset for matview +--returning recordset of buildings aggregates by zres 14, with removed small +--holes and with removed small buildings/blocks +-- + +CREATE OR REPLACE FUNCTION osm_building_block_gen1( ) +RETURNS table( + osm_id bigint + , geometry geometry +) +LANGUAGE plpgsql +VOLATILE + -- common options: IMMUTABLE STABLE STRICT SECURITY DEFINER +AS $function$ +DECLARE +zres14 float := Zres(14); +zres12 float := Zres(12); +BEGIN + FOR osm_id, geometry IN + WITH dta AS ( --CTE is used because of optimalization + SELECT o.osm_id, o.geometry + , ST_ClusterDBSCAN(o.geometry, eps := zres14, minpoints := 1) over() cid + FROM osm_building_polygon o + ) + SELECT + (array_agg(dta.osm_id))[1] + , ST_Buffer( + ST_MemUnion( + ST_Buffer( + dta.geometry + , zres14 + , 'join=mitre' + ) + ) + , -zres14 + , 'join=mitre' + ) geometry + FROM dta + GROUP BY cid + LOOP + --removing holes smaller than + IF ST_NumInteriorRings(geometry) > 0 THEN--only from geometries wih holes + geometry := ( + SELECT ST_Collect( --there are some multigeometries in this layer + gn + ) + FROM ( + SELECT + COALESCE( --in some cases are "holes" NULL, because all holes are smaller than + ST_MakePolygon( + ST_ExteriorRing(dmp.geom) --exterior ring + , holes + ) + , + ST_MakePolygon( + ST_ExteriorRing(dmp.geom) + ) + ) gn + FROM + ST_Dump(geometry) dmp --1 dump polygons + , LATERAL ( + SELECT + ST_Accum( + ST_Boundary(rg.geom) --2 create array + ) holes + FROM + ST_DumpRings(dmp.geom) rg --3 from rings + WHERE rg.path[1] > 0 --5 except inner ring + AND ST_Area(rg.geom) >= power(zres12,2) --4 bigger than + ) holes + ) new_geom + ); + END IF; + + IF ST_Area(geometry) < power(zres12,2) THEN + CONTINUE; + END IF; + + --simplify + geometry := ST_SimplifyPreserveTopology(geometry, zres14::float); + + RETURN NEXT; + END LOOP; +END; + +$function$; + +DROP MATERIALIZED VIEW IF EXISTS osm_building_block_gen1; --drop table, if exists + + +CREATE MATERIALIZED VIEW osm_building_block_gen1 AS +SELECT * FROM osm_building_block_gen1(); + +CREATE INDEX on osm_building_block_gen1 USING gist(geometry); +CREATE UNIQUE INDEX on osm_building_block_gen1 USING btree(osm_id); + + +-- Handle updates + +CREATE SCHEMA IF NOT EXISTS buildings; + +CREATE TABLE IF NOT EXISTS buildings.updates(id serial primary key, t text, unique (t)); +CREATE OR REPLACE FUNCTION buildings.flag() RETURNS trigger AS $$ +BEGIN + INSERT INTO buildings.updates(t) VALUES ('y') ON CONFLICT(t) DO NOTHING; + RETURN null; +END; +$$ language plpgsql; + +CREATE OR REPLACE FUNCTION buildings.refresh() RETURNS trigger AS + $BODY$ + BEGIN + RAISE LOG 'Refresh buildings block'; + REFRESH MATERIALIZED VIEW osm_building_block_gen1; + DELETE FROM buildings.updates; + RETURN null; + END; + $BODY$ +language plpgsql; + +CREATE TRIGGER trigger_flag + AFTER INSERT OR UPDATE OR DELETE ON osm_building_polygon + FOR EACH STATEMENT + EXECUTE PROCEDURE buildings.flag(); + +CREATE CONSTRAINT TRIGGER trigger_refresh + AFTER INSERT ON buildings.updates + INITIALLY DEFERRED + FOR EACH ROW + EXECUTE PROCEDURE buildings.refresh();