作者:黄宁
摘要:参数化对象在规划行业中使用较为广泛,能够更精确地表达图形的面积、周长等。PostGIS提供了圆弧对象CIRCULARSTRING,可以参与线或面的构造,参见 PostGIS的geometry类型及使用方法。Yukon在PostGIS的矢量存储模型中扩展实现了椭圆弧对象,并提供了精确计算参数化对象/组合对象周长和面积的能力。本文对其使用方法进行说明。
椭圆弧对象构造
WKT 数据格式为 ELLIPTICALSTRING(xstart ystart,xend yend ,xcenter ycenter,minor,clockwise,rotation,axis,ratio);
- xstart,ystart :起始点坐标
- xend,yend: 终止点坐标
- xcenter,ycenter: 中心点坐标
- minor: 椭圆弧方向,只能是 0 或者 1,任何非 0 的数都会变为 1
- clockwise 保留参数未使用
- rotation :椭圆弧旋转角度
- axis 长半轴长度
- ratio 短轴与长轴之比
注意:椭圆的起始点和终止点必须要在椭圆上,否则会报错:
ERROR: the parameters of the ellipse must be valid
可以通过: select 'ELLIPTICALSTRING(-2 0,2 0,0 0,0,0,0,2,0.5)'::geometry;
来构造一个如下图所示的椭圆弧:
WKB 数据格式说明
select 'ELLIPTICALSTRING(-2 0,2 0,0 0,1,0,0,2,0.5)'::geometry;
01 -- 小端
12000000 -- 椭圆弧类型值 0x12(int)
03000000 -- 点的个数 0x03 (int)
00000000000000C0 -- xstart (double) -2
0000000000000000 -- ystart (double) 0
0000000000000040 -- xend (double) 2
0000000000000000 -- yend (double) 0
0000000000000000 -- xcenter (double) 0
0000000000000000 -- ycenter (double) 0
000000000000F03F -- minor (double) 1
0000000000000000 -- clockwise(double) 0
0000000000000000 -- rotation (double) 0
0000000000000040 -- axis (double) 2
000000000000E03F -- ratio (double) 0.5
创建数据表
ELLIPTICALSTRING 支持 Z 和 M 属性。
create table ellipsetest(id serial,geom geometry(ELLIPTICALSTRING ,4326));
create table ellipsetest1(id serial,geom geometry(ELLIPTICALSTRINGZ ,4326));
create table ellipsetest2(id serial,geom geometry(ELLIPTICALSTRINGM ,4326));
插入数据
-- 插入不包含 Z M 属性的数据
insert into ellipsetest (geom) values ('ELLIPTICALSTRING(-2 0,2 0,0 0,0,0,0,2,0.5)');
-- 向不带有 Z 属性的表中插入带有 Z 属性的数据会报错:ERROR: Geometry has Z dimension but column does not
insert into ellipsetest (geom) values ('ELLIPTICALSTRINGZ(-2 0 0,2 0 0,0 0 0,0,0,0,2,0.5)');
-- 插入带有 Z 属性的数据
insert into ellipsetest1 (geom) values ('ELLIPTICALSTRINGZ(-2 0 0,2 0 0,0 0 0,0,0,0,2,0.5)');
-- 插入带有 M 属性的数据
insert into ellipsetest2 (geom) values ('ELLIPTICALSTRINGM(-2 0 0,2 0 0,0 0 0,0,0,0,2,0.5)');
查询数据
select geom from ellipsetest;
-- 结果
0112000020E61000000300000000000000000000C0000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000E03F
-- 使用 ST_AsText 查看 WKT 数据
select st_astext(geom) from ellipsetest;
-- 结果
ELLIPTICALSTRING(-2 0,2 0,0 0,0,0,0,2,0.5)
-- 带有 Z 属性
select st_astext(geom) from ellipsetest1;
-- 结果
ELLIPTICALSTRING Z (-2 0 0,2 0 0,0 0 0,0,0,0,2,0.5)
-- 带有 M 属性
select st_astext(geom) from ellipsetest2;
-- 结果
ELLIPTICALSTRING M (-2 0 0,2 0 0,0 0 0,0,0,0,2,0.5)
删除数据
delete from ellipsetest;
参与构造 COMPOUNDCURVE
select 'COMPOUNDCURVE((1 2,2 0),ELLIPTICALSTRING(2 0,-2 0,0 0,0,0,0,2,0.5))'::geometry;
-- 结果
010900000002000000010200000002000000000000000000F03F0000000000000040000000000000004000000000000000000112000000030000000000000000000040000000000000000000000000000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000E03F
select st_astext('COMPOUNDCURVE((1 2,2 0),ELLIPTICALSTRING(2 0,-2 0,0 0,0,0,0,2,0.5))'::geometry);
-- 结果
COMPOUNDCURVE((1 2,2 0),ELLIPTICALSTRING(2 0,-2 0,0 0,0,0,0,2,0.5))
注意 COMPOUNDCURVE 中上一个对象与新添加的对象必须是连接在一起的。
同时支持 st_curvetoline
函数,转换为 LINESTRING 后可以在 QGIS 中显示如下:
select ST_AsText(st_curvetoline('COMPOUNDCURVE((1 0,2 0),ELLIPTICALSTRING(2 0 ,4 0, 3 0 ,1,0,0,1,0.5))'));
将两个椭圆连在一起,转换为线后在 QGIS 中的显示
select ST_AsText(st_curvetoline('COMPOUNDCURVE(ELLIPTICALSTRING(-2 0,1.1602773 0.8145177,0 0,0,0,0,2,0.5),ELLIPTICALSTRING(1.1602773 0.8145177,4.3205547 0,2.3205547 0,0,0,0,2,0.5))'));
参与构造 CURVEPOLYGON
select st_curvetoline(st_astext('CURVEPOLYGON(ELLIPTICALSTRING(2 0,2 0,0 0,0,0,0,2,0.5),CIRCULARSTRING(-0.5 0,0.5 0,-0.5 0))'));
注意 CURVEPOLYGON 中的各个子对象必须是闭合的,例如其中的 ELLIPTICALSTRING 对象,起始点和终止点必须是一样的。
绘制图形如果所示:
面积计算
--- COMPOUNDCURVE 中包含椭圆时,面积的计算
select st_area('COMPOUNDCURVE(ELLIPTICALSTRING(-1 0, -1 0, 0 0,1,0,0,1,0.5))');
-- 结果
1.5707963267948966
--- CURVEPOLYGON 中包含椭圆时,面积的计算
select st_area('CURVEPOLYGON(ELLIPTICALSTRING(-1 0, -1 0, 0 0,1,0,0,1,0.5))');
-- 结果
1.5688033694578472
由于 PostGIS 3.1.2 中计算面积使用的是拟合的方式,因此我们增加了 ST_AreaParam 方法来计算精确的面积
-- 完整圆
select st_areaparam('CURVEPOLYGON(CIRCULARSTRING(-1 5, 1 5 ,-1 5))');
-- 结果
--3.141592653589793
-- 圆弧和线组合
select st_areaparam('CURVEPOLYGON(COMPOUNDCURVE(CIRCULARSTRING(10 11,12 11,11 10),LINESTRING(11 10,11 11,10 11)))');
-- 结果
--2.356194490192344
-- 椭圆弧和线组合
select st_areaparam('CURVEPOLYGON(COMPOUNDCURVE(ELLIPTICALSTRING(-2 0,0 -1,0 0,1,0,0,2,0.5),(0 -1,0 0 ,-2 0)))');
-- 结果
--1.5707963267948974
周长计算
-- 完整的椭圆
select st_length('ELLIPTICALSTRING(0 0,0 0,2 0,0,1,0,2,0.5)'); -- 1 ellipse
-- 结果
9.685374273494212
-- 1/2 椭圆
select st_length('ELLIPTICALSTRING(0 0,4 0,2 0,0,1,0,2,0.5)'); -- 1/2 ellipse
-- 结果
4.843839839458154
-- 1/4 椭圆
select st_length('ELLIPTICALSTRING(2 2,4 1,2 1,0,0,0,2,0.5)'); -- 1/4 ellipse
-- 结果
2.4220640204276096
-- 3/4 椭圆
select st_length('ELLIPTICALSTRING(2 2,4 1,2 1,1,0,0,2,0.5)'); -- 3/4 ellipse
-- 结果
7.266192061282826
-- 线和椭圆组合
select st_length('COMPOUNDCURVE(LINESTRING(-4 0, -2 0),ELLIPTICALSTRING(-2 0,2 0,0 0,0,1,0,2,0.5))');
-- 结果
6.843839839458153
-- 线和椭圆组合
select st_length('COMPOUNDCURVE((-4 0, -2 0),ELLIPTICALSTRING(-2 0,2 0,0 0,0,1,0,2,0.5))');
-- 结果
6.843839839458153
-- 圆弧和椭圆组合
select st_length('COMPOUNDCURVE(CIRCULARSTRING(1 -1,0.2929 -0.7071,0 0),ELLIPTICALSTRING(0 0,4 0,2 0,0,1,0,2,0.5))');
-- 结果
6.414626229293022
-- 圆弧,椭圆和线组合
select st_length('COMPOUNDCURVE(CIRCULARSTRING(1 -1,0.2929 -0.7071,0 0),ELLIPTICALSTRING(0 0,4 0,2 0,0,1,0,2,0.5),LINESTRING(4 0,4 8))');
-- 结果
14.414626229293022