Postgis应用---Polygon面切割

一、需求

        使用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;

Postgis应用---Polygon面切割_第1张图片

       2、使用ST_GeneratePoints方法将一个polygon转换成与面积成比例的一系列的点

       CREATE TABLE data_points AS SELECT (ST_Dump(ST_GeneratePoints(geom, 2000))).geom AS geom FROM data;

Postgis应用---Polygon面切割_第2张图片

       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;

Postgis应用---Polygon面切割_第3张图片

       5、使用ST_VoronoiPolygons方法,对均值中心生成voronoi面

       CREATE TABLE data_voronoi AS SELECT (ST_Dump(ST_VoronoiPolygons(ST_collect(geom)))).geom AS geom FROM data_centers;

Postgis应用---Polygon面切割_第4张图片

       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;

Postgis应用---Polygon面切割_第5张图片

        每一步都编写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;  

        最后,调用方法测试:

        原数据如下图:

Postgis应用---Polygon面切割_第6张图片

        执行面切割后如下图,每个面切割成10份,执如下语句,将 test_polygon表中每个面进行切割,将结果放到临时表test

        CREATE TABLE test AS select my_polygon_split(geom,10) from test_polygon

Postgis应用---Polygon面切割_第7张图片

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Postgis系列,postgis,gis,postgresql,java,数据库)