0

时空查询之ECQL

 2 years ago
source link: https://cniter.github.io/posts/489fa7b3.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Shaun's Space

  ECQL 是 CQL 的扩展,CQL 是 OGC 标准查询语言,而 ECQL 是 GeoTools 为更好的方便查询,在编程实现时扩展了 CQL,主要扩展在于其移除了 CQL 的一些限制(属性必须在比较运算符的左边,不能创建 Id Filter 进行查询等限制),也和 SQL 更相似。所以可简单认为 CQL 是书面上的标准,而 ECQL 是事实上的标准。

时间查询主要有以下几个查询谓词:

谓词作用
T TEQUALS Time测试 T 和给定时间相等,相当于 T == Time。
T BEFORE Time测试 T 在给定时间之前,相当于 T < Time。
T BEFORE OR DURING Time Period测试 T 在给定时间段之前或其中,相当于 T <= TimePeriod[1]。
T DURING Time Period测试 T 在给定时间段其中,相当于 TimePeriod[0] <= T <= TimePeriod[1]。
T DURING OR AFTER Time Period测试 T 在给定时间段其中或之后,相当于 TimePeriod[0] <= T。
T AFTER Time测试 T 在给定时间之后,相当于 T > Time。

时间段以 / 分隔符区分前后两个时间,时间格式一般为 yyyy-MM-dd'T'HH:mm:ss.SSS'Z'。

空间查询主要有以下几个查询谓词:

谓词作用
INTERSECTS(A: Geometry, B: Geometry)测试 A 与 B 相交,与 DISJOINT 相反。
DISJOINT(A: Geometry, B: Geometry)测试 A 与 B 不相交,与 INTERSECTS 相反。
CONTAINS(A: Geometry, B: Geometry)测试 A 包含 B,与 WITHIN 相反。
WITHIN(A: Geometry, B: Geometry)测试 B 包含 A,即 A 在 B 中,与 CONTAINS 相反。
TOUCHES(A: Geometry, B: Geometry)测试 A 的边界是否与 B 的边界接触,但内部不相交。
CROSSES(A: Geometry, B: Geometry)测试 A 与 B 是否相交,但不存在包含关系。
OVERLAPS(A: Geometry, B: Geometry)测试 A 与 B 是否重叠,需满足 A 与 B 是同一类型(如都是 POLYGON),并且相交区域同样是 A 和 B 的类型(只能是 POLYGON,不能是 POINT)。
EQUALS(A: Geometry, B: Geometry)测试 A 与 B 完全相等。
RELATE(A: Geometry, B: Geometry, nineIntersectionModel: String)测试 A 与 B 是否满足 DE-9IM 模型,该模型可模拟上述所有情况。
DWITHIN(A: Geometry, B: Geometry, distance: double, units: String)测试 A 与 B 的最短距离是否不超过多少距离,单位有(feet, meters, statute miles, nautical miles, kilometers)。
BEYOND(A: Geometry, B: Geometry, distance: Double, units: String)测试 A 与 B 的最短距离是否超过多少距离。
BBOX(A: Geometry, leftBottomLng: Double, leftBottomLat: Double, rightTopLng: Double, rightTopLat: Double, crs="EPSG:4326")测试 A 是否与给定 box 相交。

Geometry 是指 WKT 格式的数据,主要有以下几种:

类型示例
POINTPOINT(6 10)
LINESTRINGLINESTRING(3 4,10 50,20 25)
POLYGONPOLYGON((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2))
MULTIPOINTMULTIPOINT(3.5 5.6, 4.8 10.5)
MULTILINESTRINGMULTILINESTRING((3 4,10 50,20 25),(-5 -8,-10 -8,-15 -4))
MULTIPOLYGONMULTIPOLYGON(((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2)),((6 3,9 2,9 4,6 3)))
GEOMETRYCOLLECTIONGEOMETRYCOLLECTION(POINT(4 6),LINESTRING(4 6,7 10))

※注: POLYGON 中的边界点必须闭合,即首尾点相同,若存在多个边界,则需要遵循 逆时针,顺时针,逆时针,顺时针... 的点排列顺序,逆时针封闭,顺时针开孔,以形成具有岛和洞的复杂多边形。

  由于 WKT 标准只支持二维的坐标,为支持三维坐标以及齐次线性计算,所以在 PostGIS 中又有 EWKT 标准实现,EWKT 扩展了 WKT,带 Z 结尾用来支持三维坐标,带 M 结尾用来支持齐次线性计算,如 POINTZ(6 10 3)POINTM(6 10 1)POINTZM(6 10 3 1),同时还支持坐标内嵌空间参考系,如 SRID=4326;LINESTRING(-134.921387 58.687767, -135.303391 59.092838)。GeoTools 19.0 之后也默认以 EWKT 进行解析和编码。

属性字段查询

// 查询属性 ATTR1 小于 7 的数据
Filter filter = ECQL.toFilter("ATTR1 < (1 + ((3 / 2) * 4))" );

// 查询属性 ATTR1 小于属性 ATTR2 绝对值的数据
Filter filter = ECQL.toFilter("ATTR1 < abs(ATTR2)" );

// 查询属性 ATTR1 为 test 字符串的数据
Filter filter = ECQL.toFilter("ATTR1 == 'test'" );

// 查询属性 ATTR1 在 10 和 20 之间的数据
Filter filter = ECQL.toFilter( "ATTR1 BETWEEN 10 AND 20" );
Filter filter = ECQL.toFilter( "ATTR1 >= 10 AND ATTR1 <= 20" );

// 多条件查询
Filter filter = ECQL.toFilter("ATTR1 < 10 AND ATTR2 < 2 OR ATTR3 > 10" );

// 查询属性 ATTR1 为 silver 或 oil 或 gold 的数据
Filter filter = ECQL.toFilter("ATTR1 IN ('silver','oil', 'gold' )");

// 以 ID 主键进行查询
Filter filter = ECQL.toFilter("IN ('river.1', 'river.2')");
Filter filter = ECQL.toFilter("IN (300, 301)");
// 查询属性 ATTR1 包含 abc 字符串的数据
Filter filter = ECQL.toFilter( "ATTR1 LIKE '%abc%'" );

// 查询属性 ATTR1 开头不为 abc 字符串的数据
Filter filter = ECQL.toFilter( "ATTR1 NOT LIKE 'abc%'" );

// 查询属性 cityName 开头为 new 的数据,忽略 new 的大小写
Filter filter = ECQL.toFilter("cityName ILIKE 'new%'");

// 测试字符串是否包含
Filter filter = ECQL.toFilter("'aabbcc' LIKE '%bb%'");

空属性查询

// 查询有属性 ATTR1 存在的数据
Filter filter = ECQL.toFilter( "ATTR1 EXISTS" );

// 查询属性 ATTR1 不存在的数据
Filter filter = ECQL.toFilter( "ATTR1 DOES-NOT-EXIST" );

// 查询 Name 为 NULL 的数据
Filter filter = ECQL.toFilter("Name IS NULL");
// 查询时间属性 dtg 等于的数据
Filter filter = ECQL.toFilter( "dtg TEQUALS 2006-11-30T01:30:00Z" );

// 查询时间属性 dtg 在之后的数据
Filter filter = ECQL.toFilter("dtg AFTER 2006-11-30T01:30:00Z");

// 查询时间属性 dtg 在之前的数据
Filter filter = ECQL.toFilter("dtg BEFORE 2006-11-30T01:30:00Z");

// 查询时间属性 dtg 在之间的数据,+3:00 代表 GMT 时间 +3 小时,以 Z 结尾的时间就是 GMT 时间
Filter filter = ECQL.toFilter( "dtg DURING 2006-11-30T00:30:00+03:00/2006-11-30T01:30:00+03:00 ");

// 查询时间属性 dtg 等于的数据
Filter filter = ECQL.toFilter("dtg = 1981-06-20");

// 查询时间属性 dtg 小于等于的数据
Filter filter = ECQL.toFilter("dtg <= 1981-06-20T12:30:01Z");
// 查询空间属性 geom 包含点的数据
Filter filter = ECQL.toFilter( "CONTAINS(geom, POINT(1 2))" );

// 查询空间属性 geom 与 box 相交的数据
Filter filter = ECQL.toFilter( "BBOX(geom, 10,20,30,40)" );

// 查询空间属性 geom 与点最短距离不超过 10 千米的数据
Filter filter = ECQL.toFilter( "DWITHIN(geom, POINT(1 2), 10, kilometers)" );

// 查询空间属性 geom 与线相交的数据(geom 也必须是线)
Filter filter = ECQL.toFilter( "CROSS(geom, LINESTRING(1 2, 10 15))" );

// 查询空间属性 geom 与 GEOMETRYCOLLECTION 相交的数据(geom 也必须是 GEOMETRYCOLLECTION)
Filter filter = ECQL.toFilter( "INTERSECT(geom, GEOMETRYCOLLECTION (POINT (10 10),POINT (30 30),LINESTRING (15 15, 20 20)) )" );

// 查询空间属性 geom 与线相交的数据
Filter filter = ECQL.toFilter( "CROSSES(geom, LINESTRING(1 2, 10 15))" );

// 查询空间属性 geom 与 GEOMETRYCOLLECTION 相交的数据
Filter filter = ECQL.toFilter( "INTERSECTS(geom, GEOMETRYCOLLECTION (POINT (10 10),POINT (30 30),LINESTRING (15 15, 20 20)) )" );

// 查询空间属性 geom 与包含线的数据
Filter filter = ECQL.toFilter("RELATE(geom, LINESTRING (-134.921387 58.687767, -135.303391 59.092838), T*****FF*)");

  在 GeoTools 中,可通过 FilterFactory 来构造 Filter,而不是直接写字符串,具体示例如下:

FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();

// 相当于 Filter filter1 = ECQL.toFilter("ATTR1 = 1 AND ATTR2 < 4" );
List<Filter> filterList = ECQL.toFilterList("ATTR1=1; ATTR2<4");
Filter filter1 = ff.and(filterList);

// 相当于 Filter filter2 = ECQL.toFilter( "BBOX(geom, 10,20,30,40)" );
Filter filter2 = ff.bbox("geom", 10, 20, 30, 40, "EPSG:4326");

// 相当于 Filter filter3 = ECQL.toFilter( "dtg DURING 2006-11-29T00:30:00Z/2006-11-30T00:30:00Z");
Date startTime = ZonedDateTime.of(2006, 11, 29, 0, 30, 0, 0, ZoneOffset.UTC);
Date endTime = Date.from(startTime.plusDays(1).toInstant());
Filter filter3 = ff.between(ff.property("dtg"), ff.literal(startTime), ff.literal(endTime));

  基本可认为 CQL 和 SQL 中查询条件差不多,虽然不支持分组查询等复杂 SQL 特性,但对于一般的时空查询基本够用,CQL 中还有些空间操作函数就不继续写了,如取面积,取缓冲区,取交集,取长度等等,有需要的可自行查询 uDig Common Query Language

GeoTools CQL

GeoTools ECQL

GeoServer ECQL Reference / GeoServer 属性查询和空间查询支持 CQL / ECQL过滤器语言

WKT解读

GEOS库学习之三:空间关系、DE-9IM和谓词


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK