前言
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 格式的数据,主要有以下几种:
POINT | POINT(6 10) |
LINESTRING | LINESTRING(3 4,10 50,20 25) |
POLYGON | POLYGON((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2)) |
MULTIPOINT | MULTIPOINT(3.5 5.6, 4.8 10.5) |
MULTILINESTRING | MULTILINESTRING((3 4,10 50,20 25),(-5 -8,-10 -8,-15 -4)) |
MULTIPOLYGON | MULTIPOLYGON(((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))) |
GEOMETRYCOLLECTION | GEOMETRYCOLLECTION(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 进行解析和编码。
查询篇
属性字段查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| // 查询属性 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)");
|
模糊查询
1 2 3 4 5 6 7 8 9 10 11
| // 查询属性 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%'");
|
空属性查询
1 2 3 4 5 6 7 8
| // 查询有属性 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");
|
时间查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| // 查询时间属性 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");
|
空间查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| // 查询空间属性 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,而不是直接写字符串,具体示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
List<Filter> filterList = ECQL.toFilterList("ATTR1=1; ATTR2<4"); Filter filter1 = ff.and(filterList);
Filter filter2 = ff.bbox("geom", 10, 20, 30, 40, "EPSG:4326");
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和谓词