From fe6fed5a540aa568d95df43d4e565f5b586cdabf Mon Sep 17 00:00:00 2001 From: Sujan Adhikari Date: Wed, 18 Sep 2024 17:53:26 +0545 Subject: [PATCH 1/4] fix: merge holes with neighboring polygons --- fmtm_splitter/fmtm_algorithm.sql | 61 ++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/fmtm_splitter/fmtm_algorithm.sql b/fmtm_splitter/fmtm_algorithm.sql index 8b6d116..2070716 100644 --- a/fmtm_splitter/fmtm_algorithm.sql +++ b/fmtm_splitter/fmtm_algorithm.sql @@ -369,7 +369,8 @@ CREATE TABLE taskpolygons AS ( ), taskpolygonsnoindex AS ( - SELECT (ST_DUMP(ST_POLYGONIZE(s.geom))).geom AS geom + SELECT + (ST_DUMP(ST_POLYGONIZE(s.geom))).geom AS geom FROM simplifiedlines AS s ) @@ -380,13 +381,68 @@ CREATE TABLE taskpolygons AS ( ); -- ALTER TABLE taskpolygons ADD PRIMARY KEY(taskid); + +-- Step 1: Identify polygons without any buildings +DROP TABLE IF EXISTS no_building_polygons; +CREATE TABLE no_building_polygons AS ( + SELECT tp.geom AS no_building_geom, tp.* + FROM taskpolygons tp + LEFT JOIN buildings b ON ST_Intersects(tp.geom, b.geom) + WHERE b.geom IS NULL +); + +-- Step 2: Identify neighboring polygons +DROP TABLE IF EXISTS neighboring_polygons; +CREATE TABLE neighboring_polygons AS ( + SELECT nb.geom AS neighbor_geom, nb.*, nb_building_count, nbp.no_building_geom + FROM taskpolygons nb + JOIN no_building_polygons nbp + ON ST_Touches(nbp.no_building_geom, nb.geom) -- Finds polygons that touch each other + LEFT JOIN ( + -- Step 3: Count buildings in the neighboring polygons + SELECT nb.geom, COUNT(b.geom) AS nb_building_count + FROM taskpolygons nb + LEFT JOIN buildings b ON ST_Intersects(nb.geom, b.geom) + GROUP BY nb.geom + ) AS building_counts + ON nb.geom = building_counts.geom +); + +-- Step 4: Find the optimal neighboring polygon to avoid, +-- same polygons with the smallest number of buildings merging into multiple neighboring polygons +DROP TABLE IF EXISTS optimal_neighbors; +CREATE TABLE optimal_neighbors AS ( + SELECT nbp.no_building_geom, nbp.neighbor_geom + FROM neighboring_polygons nbp + WHERE nbp.nb_building_count = ( + SELECT MIN(nb_building_count) + FROM neighboring_polygons np + WHERE np.no_building_geom = nbp.no_building_geom + ) +); + +-- Step 5: Merge the small polygons with their optimal neighboring polygons +UPDATE taskpolygons tp +SET geom = ST_Union(tp.geom, nbp.no_building_geom) +FROM optimal_neighbors nbp +WHERE tp.geom = nbp.neighbor_geom; +DELETE FROM taskpolygons +WHERE geom IN ( + SELECT no_building_geom + FROM no_building_polygons +); + + +DROP TABLE IF EXISTS no_building_polygons; +DROP TABLE IF EXISTS neighboring_polygons; + SELECT POPULATE_GEOMETRY_COLUMNS('public.taskpolygons'::regclass); CREATE INDEX taskpolygons_idx ON taskpolygons USING gist (geom); -- VACUUM ANALYZE taskpolygons; - +-- Generate GeoJSON output SELECT JSONB_BUILD_OBJECT( 'type', 'FeatureCollection', @@ -401,3 +457,4 @@ FROM ( ) AS feature FROM taskpolygons ) AS features; + From 9f329014cdd60bea0801dc7ac1a799eb5960e19a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:17:42 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- fmtm_splitter/fmtm_algorithm.sql | 47 +++++++++++++++++++------------- fmtm_splitter/splitter.py | 2 +- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/fmtm_splitter/fmtm_algorithm.sql b/fmtm_splitter/fmtm_algorithm.sql index 2070716..16d9677 100644 --- a/fmtm_splitter/fmtm_algorithm.sql +++ b/fmtm_splitter/fmtm_algorithm.sql @@ -369,8 +369,7 @@ CREATE TABLE taskpolygons AS ( ), taskpolygonsnoindex AS ( - SELECT - (ST_DUMP(ST_POLYGONIZE(s.geom))).geom AS geom + SELECT (ST_DUMP(ST_POLYGONIZE(s.geom))).geom AS geom FROM simplifiedlines AS s ) @@ -385,46 +384,57 @@ CREATE TABLE taskpolygons AS ( -- Step 1: Identify polygons without any buildings DROP TABLE IF EXISTS no_building_polygons; CREATE TABLE no_building_polygons AS ( - SELECT tp.geom AS no_building_geom, tp.* - FROM taskpolygons tp - LEFT JOIN buildings b ON ST_Intersects(tp.geom, b.geom) + SELECT + tp.*, + tp.geom AS no_building_geom + FROM taskpolygons AS tp + LEFT JOIN buildings AS b ON ST_INTERSECTS(tp.geom, b.geom) WHERE b.geom IS NULL ); -- Step 2: Identify neighboring polygons DROP TABLE IF EXISTS neighboring_polygons; CREATE TABLE neighboring_polygons AS ( - SELECT nb.geom AS neighbor_geom, nb.*, nb_building_count, nbp.no_building_geom - FROM taskpolygons nb - JOIN no_building_polygons nbp - ON ST_Touches(nbp.no_building_geom, nb.geom) -- Finds polygons that touch each other + SELECT + nb.*, + nb.geom AS neighbor_geom, + nb_building_count, + nbp.no_building_geom + FROM taskpolygons AS nb + INNER JOIN no_building_polygons AS nbp + -- Finds polygons that touch each other + ON ST_TOUCHES(nbp.no_building_geom, nb.geom) LEFT JOIN ( -- Step 3: Count buildings in the neighboring polygons - SELECT nb.geom, COUNT(b.geom) AS nb_building_count - FROM taskpolygons nb - LEFT JOIN buildings b ON ST_Intersects(nb.geom, b.geom) + SELECT + nb.geom, + COUNT(b.geom) AS nb_building_count + FROM taskpolygons AS nb + LEFT JOIN buildings AS b ON ST_INTERSECTS(nb.geom, b.geom) GROUP BY nb.geom ) AS building_counts - ON nb.geom = building_counts.geom + ON nb.geom = building_counts.geom ); -- Step 4: Find the optimal neighboring polygon to avoid, -- same polygons with the smallest number of buildings merging into multiple neighboring polygons DROP TABLE IF EXISTS optimal_neighbors; CREATE TABLE optimal_neighbors AS ( - SELECT nbp.no_building_geom, nbp.neighbor_geom - FROM neighboring_polygons nbp + SELECT + nbp.no_building_geom, + nbp.neighbor_geom + FROM neighboring_polygons AS nbp WHERE nbp.nb_building_count = ( SELECT MIN(nb_building_count) - FROM neighboring_polygons np + FROM neighboring_polygons AS np WHERE np.no_building_geom = nbp.no_building_geom ) ); -- Step 5: Merge the small polygons with their optimal neighboring polygons UPDATE taskpolygons tp -SET geom = ST_Union(tp.geom, nbp.no_building_geom) -FROM optimal_neighbors nbp +SET geom = ST_UNION(tp.geom, nbp.no_building_geom) +FROM optimal_neighbors AS nbp WHERE tp.geom = nbp.neighbor_geom; DELETE FROM taskpolygons WHERE geom IN ( @@ -457,4 +467,3 @@ FROM ( ) AS feature FROM taskpolygons ) AS features; - diff --git a/fmtm_splitter/splitter.py b/fmtm_splitter/splitter.py index 1906fb0..9c28e16 100755 --- a/fmtm_splitter/splitter.py +++ b/fmtm_splitter/splitter.py @@ -29,6 +29,7 @@ import geojson import numpy as np from geojson import Feature, FeatureCollection, GeoJSON +from osm_rawdata.postgres import PostgresClient from psycopg2.extensions import connection from shapely.geometry import Polygon, shape from shapely.geometry.geo import mapping @@ -42,7 +43,6 @@ drop_tables, insert_geom, ) -from osm_rawdata.postgres import PostgresClient # Instantiate logger log = logging.getLogger(__name__) From a467a2b6401a336120b146fbccd2dfaefdd254fe Mon Sep 17 00:00:00 2001 From: spwoodcock Date: Sun, 22 Sep 2024 00:41:42 +0100 Subject: [PATCH 3/4] docs: tweak comment lengths --- fmtm_splitter/fmtm_algorithm.sql | 11 ++++++----- fmtm_splitter/splitter.py | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fmtm_splitter/fmtm_algorithm.sql b/fmtm_splitter/fmtm_algorithm.sql index 16d9677..d8760af 100644 --- a/fmtm_splitter/fmtm_algorithm.sql +++ b/fmtm_splitter/fmtm_algorithm.sql @@ -229,10 +229,10 @@ CREATE TABLE clusteredbuildings AS ( ), -- Cluster the buildings within each splitpolygon. The second term in the - -- call to the ST_ClusterKMeans function is the number of clusters to create, - -- so we're dividing the number of features by a constant (10 in this case) - -- to get the number of clusters required to get close to the right number - -- of features per cluster. + -- call to the ST_ClusterKMeans function is the number of clusters to + -- create, so we're dividing the number of features by a constant + -- (10 in this case) to get the number of clusters required to get close + -- to the right number of features per cluster. -- TODO: This should certainly not be a hardcoded, the number of features -- per cluster should come from a project configuration table buildingstocluster AS ( @@ -417,7 +417,8 @@ CREATE TABLE neighboring_polygons AS ( ); -- Step 4: Find the optimal neighboring polygon to avoid, --- same polygons with the smallest number of buildings merging into multiple neighboring polygons +-- same polygons with the smallest number of buildings merging into +-- multiple neighboring polygons DROP TABLE IF EXISTS optimal_neighbors; CREATE TABLE optimal_neighbors AS ( SELECT diff --git a/fmtm_splitter/splitter.py b/fmtm_splitter/splitter.py index 9c28e16..1906fb0 100755 --- a/fmtm_splitter/splitter.py +++ b/fmtm_splitter/splitter.py @@ -29,7 +29,6 @@ import geojson import numpy as np from geojson import Feature, FeatureCollection, GeoJSON -from osm_rawdata.postgres import PostgresClient from psycopg2.extensions import connection from shapely.geometry import Polygon, shape from shapely.geometry.geo import mapping @@ -43,6 +42,7 @@ drop_tables, insert_geom, ) +from osm_rawdata.postgres import PostgresClient # Instantiate logger log = logging.getLogger(__name__) From 9848c947424f347110587c2888a745f3489b6212 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 21 Sep 2024 23:41:54 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- fmtm_splitter/splitter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fmtm_splitter/splitter.py b/fmtm_splitter/splitter.py index 1906fb0..9c28e16 100755 --- a/fmtm_splitter/splitter.py +++ b/fmtm_splitter/splitter.py @@ -29,6 +29,7 @@ import geojson import numpy as np from geojson import Feature, FeatureCollection, GeoJSON +from osm_rawdata.postgres import PostgresClient from psycopg2.extensions import connection from shapely.geometry import Polygon, shape from shapely.geometry.geo import mapping @@ -42,7 +43,6 @@ drop_tables, insert_geom, ) -from osm_rawdata.postgres import PostgresClient # Instantiate logger log = logging.getLogger(__name__)