Small. Fast. Reliable.
Choose any three.
SQLite R * Tree模块的Geopoly接口

1.概述

Geopoly模块是R-Tree扩展的替代接口,该扩展使用GeoJSON表示法(RFC-7946)描述二维多边形。Geopoly包括以下功能:检测一个多边形何时包含在另一个多边形中或与另一个多边形重叠,计算多边形所包围的面积,对多边形进行线性变换,将多边形渲染为 SVG以及其他类似操作。

Geopoly的源代码包含在合并中,但没有包含在库中,除非使用-DSQLITE_ENABLE_GEOPOLY编译时选项。

地理多边形在“简单”多边形上运行-也就是说,其边界自身不相交的多边形。因此,Geopoly扩展了R-Tree扩展的功能,该功能仅可处理矩形区域。另一方面,R-Tree扩展能够处理1到5个坐标维度,而Geopoly仅限于二维形状。

Geopoly模块中的每个多边形都可以与任意数量的辅助数据字段关联。

1.1。GeoJSON

GeoJSON的标准是用于交换使用JSON地理空间信息的语法。GeoJSON是一个丰富的标准,可以描述几乎任何种类的地理空间内容。

Geopoly模块仅了解GeoJSON的一小部分,但了解关键的子集。特别是,GeoJSON理解描述简单多边形的JSON顶点数组。

多边形由其顶点定义。每个顶点是一个由两个数值组成的JSON数组,两个数值分别是顶点的X和Y坐标。多边形是这些顶点中至少四个顶点的JSON数组,因此是数组的数组。数组中的第一个和最后一个顶点必须相同。多边形遵循右手法则:当从一个顶点到下一个顶点追踪一条线时,该线右边的区域在多边形的外部,而左边的区域在多边形的内部。换句话说,顶点的净旋转是逆时针方向。

例如,以下JSON描述了一个等腰三角形,它位于X轴上,面积为0.5:

[[0,0],[1,0],[0.5,1],[0,0]]

一个三角形具有三个顶点,但是该三角形的GeoJSON描述具有4个顶点,因为第一个和最后一个顶点是重复的。

1.2。二进制存储格式

在内部,Geopoly以二进制格式(SQL BLOB)存储多边形。二进制格式的详细信息在下面给出。所有的Geopoly接口都可以接受GeoJSON格式或二进制格式的多边形。

2.使用Geopoly扩展

地理多边形表的创建如下:

使用geopoly(a,b,c)创建虚拟表newtab

上面的语句创建了一个名为“ newtab”的新地域表。每个地理多边形表都包含一个内置的整数“ rowid”列和一个“ _shape”列,其中包含与该表的该行关联的多边形。上面的示例还定义了三个名为“ a”,“ b”和“ c”的辅助数据列,它们可以存储应用程序需要与每个多边形关联的任何其他信息。如果不需要存储辅助信息,则可以省略辅助列的列表。

使用普通的INSERT语句将新的多边形存储在表中:

插入newtab(_shape)VALUES('[[[0,0],[1,0],[0.5,1],[0,0]]');

UPDATE和DELETE语句的工作方式相似。

2.1。查询

要使用索引地理空间搜索查询地理多边形表,请使用函数geopoly_overlap()或geopoly_within()作为WHERE子句中的布尔函数,并将“ _shape”列作为该函数的第一个参数。例如:

SELECT * FROM newtab在哪里geopoly_overlap(_shape,$ query_polygon);

上一个示例将返回_shape与$ query_polygon参数中的多边形重叠的每一行。geopoly_within()函数的工作原理类似,但是仅返回_shape完全包含在$ query_polygon中的行。

WHERE子句中包含裸露的geopoly_overlap()或geopoly_within()函数的查询(以及DELETE和UPDATE语句)利用基础的R * Tree数据结构进行快速查找,而该快速查找仅需检查行中的子集即可。桌子。当然,检查的行数取决于$ query_polygon的大小。大型$ query_polygons通常需要比小的行查看更多的行。

甚至对于具有大量行的表,也可以非常快速地查询对地理多边形表的rowid的查询。但是,辅助数据列都不是索引,因此对辅助数据列的查询将涉及全表扫描。

3.特殊功能

地理多边形模块定义了几个新的SQL函数,这些函数可用于处理多边形。这些函数的所有面参数都可以是GeoJSON格式或内部二进制格式。

3.1。geopoly_overlap(P1,P2)函数

如果P1和P2都是多边形,则如果P1和P2之间存在任何重叠,则geopoly_overlap(P1,P2)函数将返回非零整数;如果P1和P2完全不相交,则它将返回零。如果P1或P2不是多边形,则此例程返回NULL。

geopoly_overlap(P1,P2)函数的特殊之处在于geopoly虚拟表知道如何使用R * Tree索引来优化查询,其中WHERE子句使用geopoly_overlap()作为布尔函数。仅geopoly_overlap(P1,P2)和geopoly_within(P1,P2)函数具有此功能。

3.2。geopoly_within(P1,P2)函数

如果P1和P2都是多边形,则如果P1完全包含在P2中,则geopoly_within(P1,P2)函数将返回非零整数;如果P1的任何部分位于P2之外,则它将返回零。如果P1和P2是相同的多边形,则此例程返回非零值。如果P1或P2不是多边形,则此例程返回NULL。

geopoly_within(P1,P2)函数的特殊之处在于geopoly虚拟表知道如何使用R * Tree索引来优化查询,其中WHERE子句使用geopoly_within()作为布尔函数。仅geopoly_within(P1,P2)和geopoly_overlap(P1,P2)函数具有此功能。

3.3。geopoly_area(P)函数

如果P是多边形,则geopoly_area(P)返回该多边形包围的区域。如果P不是多边形,则geopoly_area(P)返回NULL。

3.4。geopoly_blob(P)函数

如果P是多边形,则geopoly_blob(P)将该多边形的二进制编码返回为BLOB。如果P不是多边形,则geopoly_blob(P)返回NULL。

3.5。geopoly_json(P)函数

如果P是多边形,则geopoly_json(P)以TEXT字符串形式返回该多边形的GeoJSON表示形式。如果P不是多边形,则geopoly_json(P)返回NULL。

3.6。geopoly_svg(P,...)函数

如果P是多边形,则geopoly_svg(P,...)返回一个文本字符串,该字符串是 该多边形的 可缩放矢量图形(SVG)表示形式。如果有多个自变量,则将第二个和后续自变量作为属性添加到每个SVG字形。例如:

SELECT geopoly_svg($ polygon,'class =“ poly”','style =“ fill:blue;”');

如果P不是多边形,则geopoly_svg(P,...)返回NULL。

请注意,geopoly使用传统的右手笛卡尔坐标系,其原点位于左下角,而SVG使用左手坐标系,其原点位于左上角。geopoly_svg()例程不尝试变换坐标系,因此显示的图像以镜像形式显示并旋转。如果不希望这样做,则可以在将多边形传递到geopoly_svg()之前,使用geopoly_xform()例程将笛卡尔坐标的输出转换为SVG坐标。

3.7。geopoly_bbox(P)和geopoly_group_bbox(P)函数

如果P是多边形,则geopoly_bbox(P)返回一个新的多边形,该多边形是完全包围P的最小(与轴对齐)矩形。如果P不是多边形,则geopoly_bbox(P)返回NULL。

geopoly_group_bbox(P)函数是geopoly_bbox(P)的聚合版本。geopoly_group_bbox(P)函数返回最小的矩形,该矩形将包围在聚合过程中看到的所有P值。

3.8。geopoly_contains_point(P,X,Y)函数

如果P是多边形,则且仅当坐标X,Y在多边形P的内部或边界上时,geopoly_contains_point(P,X,Y)才返回非零整数。如果P不是多边形,则geopoly_contains_point( P,X,Y)返回NULL。

3.9。geopoly_xform(P,A,B,C,D,E,F)函数

geopoly_xform(P,A,B,C,D,E,F)函数返回一个新的多边形,它是多边形P的仿射变换,并且该变换由值A,B,C,D,E,F定义。如果P不是有效的多边形,则此例程返回NULL。

转换根据以下公式转换多边形的每个顶点:

x1 = A * x0 + B * y0 + E
y1 = C * x0 + D * y0 + F

因此,例如,要在不更改其形状的情况下将多边形移动DX,DY一定程度,请使用:

geopoly_xform($ polygon,1,0,0,1,$ DX,$ DY)

要将多边形绕R弧度绕点0、0旋转:

geopoly_xform($ polygon,cos($ R),sin($ R),-sin($ R),cos($ R),0,0)

请注意,翻转多边形的变换可能会导致顶点顺序颠倒。换句话说,该变换可能导致顶点以顺时针方向而不是逆时针方向循环。可以通过在转换后通过geopoly_ccw()函数发送结果来纠正此问题。

3.10。geopoly_regular(X,Y,R,N)函数

geopoly_regular(X,Y,R,N)函数返回一个凸的,简单的,规则的,等边的,等角的多边形,其N边位于X,Y的中心,其外接半径为R。或者,如果R为负或N小于3,则函数返回NULL。N值的上限为1000,因此即使N值大于1000,例程也不会渲染面超过1000的多边形。

例如,以下图形:

3 4 5 6 7 8 10 12 16 20

是由以下脚本生成的:

选择'<svg width =“ 600” height =“ 300”>';
使用t1(x,y,n,color)AS(VALUES)
   (100,100,3,'红色'),
   (200,100,4,'orange'),
   (300,100,5,'绿色'),
   (400,100,6,'blue'),
   (500,100,7,'紫色'),
   (100,200,8,'红色'),
   (200,200,10,'橙色'),
   (300,200,12,'绿色'),
   (400,200,16,'蓝色'),
   (500,200,20,“紫色”)
)
选择
   geopoly_svg(geopoly_regular(x,y,40,n),
        printf('style =“ fill:none; stroke:%s; stroke-width:2”',color))
   || printf('<text x =“%d” y =“%d” alignment-baseline =“ central” text-anchor =“ middle”>%d </ text>',x,y + 6,n)
  从t1开始;
SELECT'</ svg>';

3.11。geopoly_ccw(J)函数

geopoly_ccw(J)函数返回逆时针(CCW)旋转的面J。

RFC-7946要求多边形使用CCW旋转。但该规范还指出,许多旧版GeoJSON文件未遵循该规范,而是包含具有顺时针(CW)旋转的多边形。geopoly_ccw()函数对于正在读取旧版GeoJSON脚本的应用程序很有用。如果geopoly_ccw()的输入是格式正确的多边形,则不会进行任何更改。但是,如果输入多边形的循环是反向的,则geopoly_ccw()会反转循环顺序,使其符合规范,从而可以与Geopoly模块一起正常工作。

4.实施细节

geopoly模块是R-Tree扩展的扩展。Geopoly使用与R-Tree扩展相同的基础逻辑和影子表。Geopoly仅提供了一个不同的接口,并提供了一些额外的逻辑来计算多边形解码,重叠和包含。

4.1。多边形的二进制编码

Geopoly使用二进制格式在内部存储所有多边形。二进制多边形由一个4字节的标头组成,后跟一个坐标对数组,其中每个坐标的每个维都是32位浮点数。

标头的第一个字节是标志字节。标志字节的最低有效位确定头之后的坐标对是存储的是大端还是小端。最低有效位的值为0表示大端,而1的值为小端。标头中第一个字节的其他位保留以供将来扩展。

标头中的后三个字节将多边形中的顶点数记录为大端整数。因此,每个多边形有大约1600万个顶点的上限。

标头之后是坐标对数组。每个坐标是一个32位浮点数。使用32位浮点值作为坐标意味着可以以大约2.5米的分辨率映射地球表面上的任何点。如果将地图限制在单个大洲或某个国家/地区,则当然可以使用更高的分辨率。请注意,由于潮汐力,地理多边形模块中的坐标分辨率在大小上与地表上点的每日运动相似。

二进制格式的坐标列表不包含冗余。最后一个坐标不像GeoJSON那样是第一个坐标的重复。因此,在多边形的二进制表示形式中,与GeoJSON表示形式相比,坐标对总是少一个。

4.2。影子表

地理多边形模块建立在R-Tree扩展的顶部,并使用相同的基础阴影表和算法。出于索引目的,每个多边形在阴影表中均表示为矩形边界框。底层的R-Tree实现使用边界框来限制搜索空间。然后,geoploy_overlap()和/或geopoly_within()例程将搜索进一步优化为确切的答案。