使用postgis函数,将一个Polygon面切割成若干份小的Polygon面,且每一份的面积差不多大
1、使用ST_GeneratePoints方法将一个polygon转换成与面积成比例的一系列的点 (点越多,效果越好,大约1000个点为宜)。
2、假设计划将Polygon切成k等份,则使用ST_ClusterKMeans方法将这些转换后的点聚合成k簇。
3、使用ST_Centroid方法求出每一簇的的均值中心。
4、将求出的均值中心作为ST_VoronoiPolygons方法的输入参数,可以计算出每个点映射出的Polygon面。
5、使用ST_Intersection将这些映射的面和初始化的Polygon做切割处理,得到结果。
1、绘制测试数据
create table data as select geom from city where id=1;
2、使用ST_GeneratePoints方法将一个polygon转换成与面积成比例的一系列的点
CREATE TABLE data_points AS SELECT (ST_Dump(ST_GeneratePoints(geom, 2000))).geom AS geom FROM data;
3、使用ST_ClusterKMeans方法将点进行聚类,计划将面切分k份,就聚类k簇。
CREATE TABLE data_clustered AS SELECT geom, ST_ClusterKMeans(geom, 10) over () AS cluster FROM data_points;
4、使用ST_Centroid方法求出每一簇的的均值中心
CREATE TABLE data_centers AS SELECT cluster, ST_Centroid(ST_collect(geom)) AS geom FROM data_clustered GROUP BY cluster;
5、使用ST_VoronoiPolygons方法,对均值中心生成voronoi面
CREATE TABLE data_voronoi AS SELECT (ST_Dump(ST_VoronoiPolygons(ST_collect(geom)))).geom AS geom FROM data_centers;
6、使用ST_Intersection方法,对voronoi面和初始的polygon面进行相交运算
CREATE TABLE data_divided AS SELECT ST_Intersection(a.geom, b.geom) AS geom FROM data a CROSS JOIN data_voronoi b;
每一步都编写sql,将数据存放到表中,以便查看结果数据。但为了方便使用,将以上sql整合到function中处理,函数中使用了临时表,避免事务并发冲突,使用了uuid,防止临时表名称冲突,需要执行create extension "uuid-ossp";--创建下uuid的扩展。
create or replace function my_polygon_split( in split_geom geometry(Polygon),--输入的面 in split_num int,--分割的数量 out geom geometry(Polygon)--输出切割的面 ) returns setof geometry as $$ declare rec record; temp_points text; temp_ClusterKMeans text; temp_ClusterCentroid text; temp_VoronoiPolygons text; begin --防止并发的时候,临时表名称冲突 temp_points:='temp_points'||uuid_generate_v4(); temp_ClusterKMeans:='temp_ClusterKMeans'||uuid_generate_v4(); temp_ClusterCentroid:='temp_ClusterCentroid'||uuid_generate_v4(); temp_VoronoiPolygons:='temp_VoronoiPolygons'||uuid_generate_v4(); --生成点 execute format('create temp table "%s" on commit drop as SELECT row_number() over() as gid,(ST_Dump(ST_GeneratePoints($1, 2000))).geom',temp_points) using split_geom; --点成簇 execute format('create temp table "%s" on commit drop as SELECT t.geom, ST_ClusterKMeans(t.geom, $1) over () AS cluster from "%s" t',temp_ClusterKMeans,temp_points) using split_num; --簇的中心点 execute format('create temp table "%s" on commit drop as SELECT t.cluster, ST_Centroid(ST_collect(t.geom)) AS geom FROM "%s" t GROUP BY t.cluster',temp_ClusterCentroid,temp_ClusterKMeans); --voronoi构造面 execute format('create temp table "%s" on commit drop as SELECT (ST_Dump(ST_VoronoiPolygons(ST_collect(t.geom)))).geom AS geom FROM "%s" t',temp_VoronoiPolygons,temp_ClusterCentroid); --intersection切割 for rec in execute format('SELECT ST_Intersection($1, b.geom) AS geom FROM "%s" b',temp_VoronoiPolygons) using split_geom loop geom:=rec.geom; return next; end loop; return; end; $$ language plpgsql strict;
最后,调用方法测试:
原数据如下图:
执行面切割后如下图,每个面切割成10份,执如下语句,将 test_polygon表中每个面进行切割,将结果放到临时表test
CREATE TABLE test AS select my_polygon_split(geom,10) from test_polygon