不管是通过哪种方法实现shp数据的查询,我们发现都离不开Filter,对于GeoTools,提供了两种创建Filter的方法,他们分别是CQL
和FilterFactory
一、CQL
参考:
CQL — GeoTools 32-SNAPSHOT User Guide
CQL 实用工具类由静态方法组成,您可以调用这些方法来将文本 String 转换为 Expression、Filter 或 List<Filter>。它还能够获取这些项目并生成适当的文本表示。
1、单个filter
Filter filter = CQL.toFilter("attName >= 5");
2、解析filter列表
使用“;”字符分隔出每个filter:
List filters = CQL.toFilterList("att1 > 5;ogc:name = 'river'");
3、通过比较值进行筛选
Filter result = CQL.toFilter("ATTR1 < (1 + ((2 / 3) * 4))" );
Filter result = CQL.toFilter("ATTR1 < abs(ATTR2)" );
Filter result = CQL.toFilter("ATTR1 < 10 AND ATTR2 < 2 OR ATTR3 > 10" );
4、使用文本进行筛选
Filter result = CQL.toFilter( "ATTR1 LIKE 'abc%'" );
Filter result = CQL.toFilter( "ATTR1 NOT LIKE 'abc%'" );
5、过滤空值
Filter result = CQL.toFilter( "ATTR1 IS NULL" );
Filter result = CQL.toFilter( "ATTR1 IS NOT NULL" );
Filter by Comparing Time values
6、通过比较时间值进行筛选
//等于
Filter result = CQL.toFilter( "ATTR1 TEQUALS 2006-11-30T01:30:00Z" );
//日期之前
Before filter = (Before) CQL.toFilter("lastEarthQuake BEFORE 2006-11-30T01:30:00Z");
//周期之前
Filter result = CQL.toFilter( "ATTR1 BEFORE 2006-11-30T01:30:00Z/2006-12-31T01:30:00Z" );
//日期之后
After filter = (After) CQL.toFilter("lastEarthQuake AFTER 2006-11-30T01:30:00Z");
//使用GMT+3区的之后一个时间
After filter = (After) CQL.toFilter("lastEarthQuake AFTER 2006-11-30T01:30:00+03:00");
//周期之后
Filter result = CQL.toFilter( "ATTR1 AFTER 2006-11-30T01:30:00Z/2006-12-31T01:30:00Z" );
//具有持续时间的时间谓词(2006-11-30T01:30:00Z 之后的十天)
Filter result = CQL.toFilter( "ATTR1 AFTER 2006-11-30T01:30:00Z/P10D" );
Filter result = CQL.toFilter( "ATTR1 AFTER 2006-11-30T01:30:00Z/T10H" );
//During predicate
During filter =
(During)
CQL.toFilter(
"lastEarthQuake DURING 1700-01-01T00:00:00/2011-01-01T00:00:00");
7、基于存在性进行筛选
Filter result = CQL.toFilter( "ATTR1 EXISTS" );
ilter result = CQL.toFilter( "ATTR1 DOES-NOT-EXIST" );
8、通过检查值是否介于之间来过滤
Filter result = CQL.toFilter( "ATTR1 BETWEEN 10 AND 20" );
9、使用复合属性
Filter result = CQL.toFilter( "gmd:MD_Metadata.gmd:identificationInfo.gmd:MD_DataIdentification.gmd:abstract LIKE 'abc%'" );
10、使用几何关系进行筛选
Filter result = CQL.toFilter( "CONTAINS(ATTR1, POINT(1 2))" );
Filter result = CQL.toFilter( "BBOX(ATTR1, 10,20,30,40)" );
Filter result = CQL.toFilter( "DWITHIN(ATTR1, POINT(1 2), 10, kilometers)" );
Filter result = CQL.toFilter( "CROSS(ATTR1, LINESTRING(1 2, 10 15))" );
Filter result = CQL.toFilter( "INTERSECT(ATTR1, GEOMETRYCOLLECTION (POINT (10 10),POINT (30 30),LINESTRING (15 15, 20 20)) )" );
Filter result = CQL.toFilter( "CROSSES(ATTR1, LINESTRING(1 2, 10 15))" );
Filter result = CQL.toFilter( "INTERSECTS(ATTR1, GEOMETRYCOLLECTION (POINT (10 10),POINT (30 30),LINESTRING (15 15, 20 20)) )" );
11、九交模式判断过滤器
Filter filter =ECQL.toFilter("RELATE(geometry, LINESTRING (-134.921387 58.687767, -135.303391 59.092838), T*****FF*)");
二、ECQL
参考:ECQL — GeoTools 32-SNAPSHOT User Guide
ECQL 语言旨在作为 CQL 的扩展,因此您可以编写 CQL 支持的所有谓词,并使用新语法规则中定义的新表达式可能性。
ECQL 实用程序类与方法兼容,允许您将其用作 CQL 的直接替代品。
1、 按比较值过滤
Filter filter = ECQL.toFilter("1000 <= population");
Filter filter =ECQL.toFilter("(under18YearsOld * 19541453 / 100 ) < (over65YearsOld * 19541453 / 100 )");
Filter filter = ECQL.toFilter("population BETWEEN 10000000 and 20000000");
Filter filter =ECQL.toFilter("area(Polygon((10 10, 20 10, 20 20, 10 10))) BETWEEN 10000 AND 30000");
2、按Features’ ID列表筛选
筛选器 XML 格式允许定义捕获一组 FeatureID(通常表示所选内容)的 Id 筛选器。
//使用字符串作为 id:
Filter filter = ECQL.toFilter("IN ('river.1', 'river.2')");
//使用整数作为 id:
Filter filter = ECQL.toFilter("IN (300, 301)");
//我们尝试了几个实验,但并非所有实验都有效,留下了以下已弃用的语法:
Filter filter = ECQL.toFilter("ID IN ('river.1', 'river.2')");
3、基于一组值的筛选器
以下过滤器选择以银、石油或黄金为主要矿产资源的国家:
Filter filter = ECQL.toFilter("principalMineralResource IN ('silver','oil', 'gold' )");
4、使用文本模式进行筛选
//使用LIKE关键字过滤文本模式:
Filter filter = ECQL.toFilter("cityName LIKE 'New%'");
//带有ILIKE关键字的不区分大小写示例
Filter filter = ECQL.toFilter("cityName ILIKE 'new%'");
//ECQL 允许您测试任何两个表达式,包括文字:
Filter filter = ECQL.toFilter("'aabbcc' LIKE '%bb%'");
5、按空间关系过滤
Filter filter = ECQL.toFilter("DISJOINT(the_geom, POINT(1 2))");
Filter filter = ECQL.toFilter("DISJOINT(buffer(the_geom, 10) , POINT(1 2))");
Filter filter = ECQL.toFilter("DWITHIN(buffer(the_geom,5), POINT(1 2), 10, kilometers)");
6、按时间关系过滤
时间谓词允许在两个给定的时间瞬间之间建立关系,或者在瞬间和时间间隔之间建立关系。在下一个示例中,during 谓词用于筛选在指定日期之间发生地震的城市
During filter =(During)ECQL.toFilter("lastEarthQuake DURING 1700-01-01T00:00:00Z/2011-01-01T00:00:00Z");
在 ECQL 中,您可以在时间谓词的左侧编写日期时间表达式:
Filter filter = ECQL.toFilter("2006-11-30T01:00:00Z AFTER 2006-11-30T01:30:00Z");
在 Before 谓词中:
Filter filter = ECQL.toFilter("2006-11-30T01:00:00Z BEFORE 2006-11-30T01:30:00Z");
在During谓词中:
Filter filter =ECQL.toFilter("2006-11-30T01:00:00Z DURING 2006-11-30T00:30:00Z/2006-11-30T01:30:00Z ");
以下示例显示了一个时间谓词,该谓词在日期时间表达式中包含 UTC 时区 (GMT +3):
Filter filter =ECQL.toFilter("2006-11-30T01:00:00+03:00 DURING 2006-11-30T00:30:00+03:00/2006-11-30T01:30:00+03:00 ");
7、过滤空值
Filter filter = ECQL.toFilter(" Name IS NULL");
Filter filter = ECQL.toFilter("centroid( the_geom ) IS NULL");
8、属性存在谓词
Filter resultFilter = ECQL.toFilter("aProperty EXISTS");
9、表达式
Expression expr = ECQL.toExpression("X + 1");
10、filter 列表
//过滤器列表仍然支持使用“;” 分隔条目:
List<Filter> list = ECQL.toFilterList("X=1; Y<4");
11、使用日期文字过滤
Filter filter = ECQL.toFilter("foo = 1981-06-20");
Filter filter = ECQL.toFilter("foo <= 1981-06-20T12:30:01Z");
三、 FilterFactory
参考:Filter — GeoTools 32-SNAPSHOT User Guide
通过使用 FilterFactory
,您可以手动创建对象。 FilterFactory
接口仅限于严格遵守规范。
1、FilterFactory基础
核心过滤器抽象在这里。这组接口是封闭的(你不能创建一个新的筛选器类并期望它工作)。
2、比较
筛选器数据模型的核心是属性比较;通过这些过滤器,您可以测试要素的属性,并仅选择与以下要素匹配的要素:
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();Filter filter;
// 与equals相同功能的查询
ff.equal(ff.property("land_use"), ff.literal("URBAN"));
// 判断值是否为null
filter = ff.isNull(ff.property("approved"));
filter = ff.less(ff.property("depth"), ff.literal(300));
filter = ff.lessOrEqual(ff.property("risk"), ff.literal(3.7));
filter = ff.greater(ff.property("name"), ff.literal("Smith"));
filter = ff.greaterOrEqual(ff.property("schedule"), ff.literal(new Date()));
// 判断两个数值之前的结果
filter = ff.between(ff.property("age"), ff.literal(20), ff.literal("29"));
filter = ff.between(ff.property("group"), ff.literal("A"), ff.literal("D"));
// 不等于的简写
filter = ff.notEqual(ff.property("type"), ff.literal("draft"));
// like模式的过滤器
filter = ff.like(ff.property("code"), "2300%");
// 您可以使用自定义的通配符
filter = ff.like(ff.property("code"), "2300?", "*", "?", "\\");
3、Null vs Nil
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
Filter filter;
// 如果approved等于“null”,正如前面的测试示例
filter = ff.isNull(ff.property("approved"));
// 此示例检查是否存在approved
filter = ff.isNil(ff.property("approved"), "no approval available");
4、MatchAction(匹配操作)
实现该MultiValuedFilter
接口的所有过滤器都支持对在评估时返回多个值的操作数进行过滤。可以通过MatchAction
属性修改这些过滤器处理多个值的方式。可以通过一个简单的 getter 来检索该属性:
filter.getMatchAction()
MatchAction
有三个可能的值:
MatchAction.ANY
—当没有MatchAction
指定时,它被设置为默认值MatchAction.ANY
。如果任何可能的操作数组合的计算结果为真,则计算结果为真:
List<Integer> ages = Arrays.asList(new Integer[] {7, 8, 10, 15});
Filter filter = ff.greater(ff.literal(ages), ff.literal(12), false, MatchAction.ANY);
System.out.println("Any: " + filter.evaluate(null)); // prints Any: true
MatchAction.ALL
— 如果所有可能的操作数组合的计算结果为真,则计算结果为真。:
List<Integer> ages = Arrays.asList(new Integer[] {7, 8, 10, 15});
Filter filter = ff.greater(ff.literal(ages), ff.literal(12), false, MatchAction.ALL);
System.out.println("All: " + filter.evaluate(null));
// prints All: false
MatchAction.ONE
— 如果恰好一种可能的值组合评估为真,则评估为真:
List<Integer> ages = Arrays.asList(new Integer[] {7, 8, 10, 15});
Filter filter = ff.greater(ff.literal(ages), ff.literal(12), false, MatchAction.ONE);
System.out.println("One: " + filter.evaluate(null));
// prints One: true
5、逻辑过滤器
可以使用通常的 AND、OR 和 NOT 二进制逻辑组合过滤器。
filter = ff.not(ff.like(ff.property("code"), "230%"));
//您还可以组合筛选器以缩小返回的结果范围
filter = ff.and(ff.greater(ff.property("rainfall"), ff.literal(70)),ff.equal(ff.property("land_use"), ff.literal("urban"), false));
filter = ff.or(ff.equal(ff.property("code"), ff.literal("approved")),ff.greater(ff.property("funding"), ff.literal(23000)));
6、Identifier(标识符)
另一种有趣的方法是使用过滤器来更多地作为 GIS 意义上的“选择”。在这种情况下,我们将直接匹配 FeatureId
而不是计算属性。
最常见的测试是针对 FeatureId
:
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
Filter filter = ff.id(ff.featureId("CITY.98734597823459687235"), ff.featureId("CITY.98734592345235823474"));
从形式上讲,这种 Id 匹配风格不应与传统的基于属性的评估(例如边界框过滤器)混合使用。您还可以使用Set<FeatureId>
:
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
Set<FeatureId> selected = new HashSet<>();
selected.add(ff.featureId("CITY.98734597823459687235"));
selected.add(ff.featureId("CITY.98734592345235823474"));
Filter filter = ff.id(selected);
另一个使用标识符的地方是在处理版本信息时。在这种情况下ResourceId
,使用由 afid
和 a组成的a rid
。ResourceId
可用于探索版本信息:
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
Filter filter;
// 获取特定修订版本
filter = ff.id(ff.featureId("CITY.98734597823459687235", "A457"));
// 您还可以使用ResourceId获取特定的修订版本
filter = ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version()));
//获取符合条件的上一个
filter =ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version(Action.PREVIOUS)));
// 获取符合条件的下一个
filter =ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version(Action.NEXT)));
// 获取第一个
filter =ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version(Action.FIRST)));
// 获取第一个(即索引=1)
filter = ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version(1)));
// 获取序列中的第12条记录(即索引=12)
filter = ff.id(ff.resourceId("CITY.98734597823459687235", "A457", new Version(12)));
// 获取接近1985年1月数据
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);df.setTimeZone(TimeZone.getTimeZone("GMT"));
filter =ff.id(ff.resourceId("CITY.98734597823459687235","A457",new Version(df.parse("1985-1-1"))));
// 获取90年代所有的实体对象
filter =ff.id(ff.resourceId("CITY.98734597823459687235",df.parse("1990-1-1"),df.parse("2000-1-1")));
7、Spatial(空间过滤器)
这是一个快速示例,展示了如何在边界框中请求特征。
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
ReferencedEnvelope bbox = new ReferencedEnvelope(x1, x2, y1, y2, DefaultGeographicCRS.WGS84);
Filter filter = ff.bbox(ff.property("the_geom"), bbox);
8、Temporal(时态过滤器)
gt-main 模块提供了一些我们需要的实现类:
DefaultIntant
:这是用于表示单个时间点的 Instant 的实现。DefaultPeriod
:这是Period的一个实现,用于表示时间范围。
这是一个示例,说明它们的构造和与时间过滤器的使用:
// 使用gt main的默认实现
DateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
Date date1 = FORMAT.parse("2001-07-05T12:08:56.235-0700");
Instant temporalInstant = new DefaultInstant(new DefaultPosition(date1));
// 简单检查属性是否在
Filter after = ff.after(ff.property("date"), ff.literal(temporalInstant));
// 也可以在一定期限内检查属性
Date date2 = FORMAT.parse("2001-07-04T12:08:56.235-0700");
Instant temporalInstant2 = new DefaultInstant(new DefaultPosition(date2));
Period period = new DefaultPeriod(temporalInstant, temporalInstant2);
Filter within = ff.toverlaps(ff.property("constructed_date"), ff.literal(period));
8、Expression(表达式)
上面提到的许多过滤器都是作为两个(或多个)表达式之间的比较呈现的。表达式用于访问保存在 Feature(或 POJO、Record 或 …)中的数据。核心表达式抽象在这里 - 该集合是开放的,您可以定义新函数。
表达式非常有用,您会在 GeoTools 的许多部分中看到它们弹出。样式使用它们来选择用于描绘的数据等等。
-
PropertyName
该
PropertyName
表达式用于从您的数据模型中提取信息。最常见的用途是访问特征属性。FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2( GeoTools.getDefaultHints() ); Expression expr = ff.property("name"); //计算 Object value = expr.evaluate( feature ); if( value instanceof String){ name = (String)value; }else{ name = "(invalid name)"; }
您还可以将值具体要求为字符串,如果无法将该值强制为字符串,则将返回 null:
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2( GeoTools.getDefaultHints() ); Expression expr = ff.property("name"); String name = expr.evaluate( feature, String ); //evaluate if( name == null ){ name = "(invalid name)"; }
-
X 路径和命名空间
可以在过滤器中使用 XPath 表达式。这对于针对复杂特征评估嵌套属性特别有用。要评估 XPath 表达式,org.xml.sax.helpers.NamespaceSupport需要一个 对象来将前缀与名称空间 URI 相关联。FilterFactory2支持创建PropertyName具有关联命名空间上下文信息的表达式:
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2( GeoTools.getDefaultHints() ); NamespaceSupport namespaceSupport = new NamespaceSupport(); namespaceSupport.declarePrefix("foo", "urn:cgi:xmlns:CGI:GeoSciML:2.0" ); Filter filter = ff.greater(ff.property("foo:city/foo:size",namespaceSupport),ff.literal(300000));
可以从现有
PropertyName
表达式中检索命名空间上下文信息:PropertyName propertyName = ff.property("foo:city/foo:size", namespaceSupport); NamespaceSupport namespaceSupport2 = propertyName.getNamespaceContext();
-
Functions(功能)
您可以使用
FilterFactory2
以下方法创建函数:FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2( GeoTools.getDefaultHints() ); PropertyName a = ff.property("testInteger"); Literal b = ff.literal( 1004.0 ); Function min = ff.function("min", a, b );
对于需要多个参数的函数,您需要使用数组:
FilterFactory ff = CommonFactoryFinder.getFilterFactory(null); PropertyName property = ff.property("name"); Literal search = ff.literal("foo"); Literal replace = ff.literal("bar"); Literal all = ff.literal( true ); Function f = ff.function("strReplace", new Expression[]{property,search,replace,all});
找不到函数时该怎么办 - 创建函数将失败!Symbology Encoding 2.0 规范的概念是
fallbackValue
- 虽然我们还没有通过工厂提供它,但您可以使用FunctionFinder
.FunctionFinder finder = new FunctionFinder(null); finder.findFunction("pi", Collections.emptyList(), ff.literal(Math.PI));
9、FilterVisitor(过滤器访问者)
FilterVisitor用于遍历过滤器数据结构。常见用途包括:
- 询问有关过滤器内容的问题
- 对过滤器执行分析和优化(比如用“2”替换“1+1”)
- 转换过滤器(想想搜索和替换)
当使用 XSLT 处理遍历树时,对 XML 文档(也形成树)使用类似的方法。
所有这些活动都有一个共同点:
需要检查过滤器的内容
需要建立结果或答案
这是一个快速代码示例,显示了使用访问者遍历数据结构:
class FindNames extends DefaultFilterVisitor {
public Set<String> found = new HashSet<String>();
//我们只感兴趣属性名称表达式
public Object visit( PropertyName expression, Object data ) {
found.add( expression.getPropertyName() );
return found;
}
}
// 将访问者传递到过滤器以开始遍历
FindNames visitor = new FindNames();
filter.accept( visitor, null );
System.out.println("Property Names found "+visitor.found );