GeoTools入门(六)-- 查询那点事

1. 查询概述

作为一名程序员,每天都在开发着大量的CRUD,而查询在CRUD中占有大多数。在前面的文章里介绍了对于shp的创建和修改i,今天我们就聊一聊GeoTools里的查询,准确的说是GeoTools对shp的查询。好了,我们先看一下GeoTools对于shp查询的基本语法结构:

SimpleFeatureSource source = dataStore.getFeatureSource(typeName);
FeatureType schema = source.getSchema();
Filter filter = CQL.toFilter(text.getText());
SimpleFeatureCollection features = source.getFeatures(filter);

上面的语句类似于select * from ...,如果我们只要shp的几个字段如何做呢?此时我们就需要借助Query对象。通过使用Query数据结构,您可以更好地控制您的请求,允许您只选择所需的属性;控制返回多少属性信息;并要求一些特定的处理步骤。通过下面的代码,我们发现只返回了默认的FeatureIdentiferthe_geom属性。

String typeName = (String) featureTypeCBox.getSelectedItem();
SimpleFeatureSource source = dataStore.getFeatureSource(typeName);
FeatureType schema = source.getSchema();
//name 一般为the_geom,表示记录shp的空间信息
String name = schema.getGeometryDescriptor().getLocalName();
Filter filter = CQL.toFilter(text.getText());
//关键是下面的代码
Query query = new Query(typeName, filter, new String[] {name});
SimpleFeatureCollection features = source.getFeatures(query);

不管是通过那种方法实现shp数据的查询,我们发现都离不开Filter。查询嘛,少不了where条件那么Filter有几种创建方式呢?我们可以回想一下操作数据库的框架。一类是类似于MyBatis的通过写SQL查询数据,而另一种方式类似于JPA,通过面向对象的方式查询数据。而对于GeoTools,也同样提供了两种创建Filter的方法,他们分别是CQLFilterFactory

2. CQL查询

2.1 概述

CQL工具类提供了一个很好的前端来从文本字符串生成过滤器和表达式,该工具类包含静态方法,您可以调用这些方法将文本字符串转换为表达式、过滤器或列表<过滤器>。它还能够采用这些项目并生成适当的文本表示。

CQL -CQL() +toFilter(String) : Filter +toFilter(String) : Filter +toExpression(String) : Expression +toExpression(String,FilterFactory) : Expression +toFilterList(String) : List<Filter> +toCQL(Filter) : String +toCQL(Expression) : String +toFilterList(String,FilterFactory) : List<Filter>

2.2 示例

2.2.1 简单例子
Filter filter = CQL.toFilter("attrName >= 5");
2.2.2 解析过滤器列表

可以一次解析过滤器列表。这对于像 GeoServer 这样的应用程序很有用,例如,它允许为 WMS GetMap 请求中的每一层定义过滤谓词。

List filters = CQL.toFilterList("att1 > 5;ogc:name = 'river'");

在这个例子中,你会得到两个过滤器,因为“;” 字符充当分隔符。

2.2.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" );
2.2.4 使用文本过滤
Filter result = CQL.toFilter( "ATTR1 LIKE 'abc%'" );
Filter result = CQL.toFilter( "ATTR1 NOT LIKE 'abc%'" );
2.2.5 过滤空值
Filter result = CQL.toFilter( "ATTR1 IS NULL" );
Filter result = CQL.toFilter( "ATTR1 IS NOT NULL" );
2.2.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 filter =(During)CQL.toFilter("lastEarthQuake DURING 1700-01-01T00:00:00/2011-01-01T00:00:00");
    
2.2.7 基于存在的过滤器
//检查是否存在
Filter result = CQL.toFilter("ATTR1 EXISTS" );
//检查某些东西是否不存在
Filter result = CQL.toFilter("ATTR1 DOES-NOT-EXIST" );
2.2.8 通过检查值是否介于之间来过滤:
Filter result = CQL.toFilter("ATTR1 BETWEEN 10 AND 20" );
2.2.9 使用复合属性:
Filter result = CQL.toFilter("gmd:MD_Metadata.gmd:identificationInfo.gmd:MD_DataIdentification.gmd:abstract LIKE  'abc%'" );
2.2.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)) )" );
2.2.11 九交模式判断过滤器
Filter filter =ECQL.toFilter("RELATE(geometry, LINESTRING (-134.921387 58.687767, -135.303391 59.092838), T*****FF*)");

2.3 CQL的扩展类–ECQL

ECQL 语言旨在作为 CQL 的扩展,因此您可以编写 CQL 支持的所有谓词并使用新语法规则中定义的新表达式可能性。

2.3.1 例子
2.3.1.1 按比较值过滤

CQL 语言将我们限制为propertyName针对更一般的表达式引用 a 。ECQL 允许您在任何地方使用完整的表达式:

 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.3.1.2 按功能 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')");
2.3.1.3 基于一组值的过滤器

以下过滤器选择以白银、石油或黄金为主要矿产资源的国家:

Filter filter = ECQL.toFilter("principalMineralResource IN ('silver','oil', 'gold' )");
2.3.1.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%'");
2.3.1.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)");

以下示例显示如何使用 RELATE 操作创建过滤器。在这种情况下,DE-9IM 模式对应于包含空间关系,如果第一个几何图形包含第二个,则为真。

  Filter filter =ECQL.toFilter("RELATE(geometry, LINESTRING (-134.921387 58.687767, -135.303391 59.092838), T*****FF*)");

以下变体显示相同,但使用 EWKT 约定为几何图形提供坐标参考系统,即在其前面加上“SRID=epsgCode;”:

 Filter filter =ECQL.toFilter("RELATE(geometry, SRID=4326;LINESTRING (-134.921387 58.687767, -135.303391 59.092838), T*****FF*)");
2.3.1.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");

在期间谓词中:

 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 ");
2.3.1.7 过滤空值
Filter filter = ECQL.toFilter(" Name IS NULL");
Filter filter = ECQL.toFilter("centroid( the_geom ) IS NULL");
2.3.1.8 属性存在谓词
Filter resultFilter = ECQL.toFilter("aProperty EXISTS");
2.3.1.9 表达式
Expression expr = ECQL.toExpression("X + 1");
2.3.1.10 过滤器列表
//过滤器列表仍然支持使用“;” 分隔条目:
List<Filter> list = ECQL.toFilterList("X=1; Y<4");
2.3.1.11 使用日期文字过滤
Filter filter = ECQL.toFilter("foo = 1981-06-20");
Filter filter = ECQL.toFilter("foo <= 1981-06-20T12:30:01Z");

3. FilterFactory

通过使用FilterFactory,您可以手动创建对象。该FilterFactory接口仅限于严格的规范合规性。

3.1 工厂过滤器基础

核心过滤器抽象在这里。这组接口是关闭的(你不能创建一个新的过滤器类并期望它工作)。

«interface» Filter +evaluate(Object) : boolean +accept(Filter,Object) : Object «interface» BinaryLogicOperator +getChildren() «interface» Not +getFilter() «interface» PropertyIsNull +getExpression() «interface» MultiValuedFilter +getMatchAction() «interface» Id +getIDs() +getIdentifiers() «interface» PropertyIsBetween +getExpression() +getLowerBoundary() +getUpperBoundary() «interface» BinaryComparisonOperator +getExpression1() +getExpression2() +isMatchingCase() «interface» PropertyIsLike +getExpression() +getLiteral() +getWildCard() +getSingleChar() +getEscape() +isMatchingCase() ExcludeFilter IncludeFilter And Or SpatialOperator BinaryTemporalOperator PropertyIsEqualTo PropertyIsLessThan PropertyIsLessThanOrEqualTo PropertyIsGreaterThan PropertyIsNotEqualTo PropertyIsGreaterThanOrEqualTo

3.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.3 空 vs 空

比较属性值的一个相关主题是测试属性是否具有值。在简单的情况下PropertyIsNull可用于检查属性是否存在;并且该值为空。我们也有允许一个属性出现零次或多次的情况;在这种情况下,我们需要一种清晰的方法来检查属性根本不存在(即发生 = 零)。

FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
Filter filter;
// 如果approved等于“null”,正如前面的测试示例
filter = ff.isNull(ff.property("approved"));
// 此示例检查是否存在approved
filter = ff.isNil(ff.property("approved"), "no approval available");

3.4 匹配动作

实现该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
    

    在几种情况下可能有多个值:使用应用程序模式时,或直接使用 java 对象时。当针对这种性质的丰富内容评估表达式时,子引用可能会返回多值属性。

    例如,此过滤器测试是否所有孩子都超过 12 岁:

    Filter filter = ff.greater(ff.property("child/age"), ff.literal(12), true, MatchAction.ALL);
    

3.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)));

3.6 标识符

在 GIS 意义上使用过滤器更多地作为“选择”的另一种有趣方式。在这种情况下,我们将直接匹配FeatureId.

«interface» Identifier +getID() : Object +matches(Object) : boolean +equals(Object) : boolean +hashCode() : int +toString() : String «interface» RecordId +getID() : String +matches(Object) : boolean «interface» ObjectId +getId() : Long +matches(Object) : boolean «interface» GmlObjectId +getID() : String +matches(Object) : boolean «interface» FeatureId +getID() : String +matches(Object) : boolean

最常见的测试是针对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 ridResourceId 可用于探索版本信息:

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")));

3.7 空间过滤器

«interface» Filter +evaluate(Object) : boolean +accept(FilterVisitor,Object) : Object «interface» MultiValuedFilter +getMatchAction() «interface» SpatialOperator «interface» BinarySpatialOpeator +getExpression1() +getExpression2() «interface» BoundedSpatialOperator «interface» Contains «interface» Crosses «interface» Equals «interface» Intersects «interface» Overlaps «interface» Touches «interface» WithIn «interface» DistanceBufferOperator +getDistance() +getDistanceUnits() «interface» DWithIn «interface» Beyond «interface» DisJoint «interface» BBOX +getPropertyName() +getSRS() +getMinX() +getMinY() +getMaxX() +getMaxY()

这是一个快速示例,展示了如何在边界框中请求特征。

FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
ReferencedEnvelope bbox = new ReferencedEnvelope(x1, x2, y1, y2, DefaultGeographicCRS.WGS84);
Filter filter = ff.bbox(ff.property("the_geom"), bbox);

3.8 时间过滤器

时间过滤器最近由过滤器 2.0 规范定义,是 GeoTools 8.0 的新增功能。

«interface» Filter +evaluate(Object) : boolean +accept(FilterVisitor,Object) : Object «interface» MultiValuedFilter +getMatchAction() «interface» BinaryTemporalOperator +getExpression1() +getExpression2() «interface» After «interface» Ends «interface» AnyInteracts «interface» Meets «interface» Before «interface» MetBy «interface» Begins «interface» OverlappedBy «interface» BegunBy «interface» TContains «interface» During «interface» TEquals «interface» EndedBy «interface» TOverlaps

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));

3.9 表达式

上面提到的许多过滤器都是作为两个(或多个)表达式之间的比较呈现的。表达式用于访问保存在 Feature(或 POJO、Record 或 …)中的数据。核心表达式抽象在这里 - 该集合是开放的,您可以定义新函数。

«interface» Expression +evaluate(Object) : Object +evaluate(Object,Class<T>) : T +accept(ExpressionVisitor,Object) : Object «interface» PropertyName +getPropertyName() +getNamespaceContext() «interface» Literal +getValue() : Object «interface» Function +getName() : String +getParameters() : List<Expression> +getFallbackValue() : Literal «interface» Add «interface» Divide «interface» Multiply «interface» Subtract «interface» BinaryExpression +getExpression1() : Expression +getExpression2() : Expression

表达式非常有用,您会在 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();
    
  • 职能

    您可以使用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));
    

3.10 过滤器访问者

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 );

4. 简单示例

本示例通过一个过滤器从Shape或者其他数据源选择一个要素集合。

4.1 引入pom

 <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <geotools.version>19.1</geotools.version>
</properties>
<dependencies>
    <!-- Provides map projections -->
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-epsg-hsql</artifactId>
        <version>${geotools.version}</version>
    </dependency>
    <!-- Provides support for shapefiles -->
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-shapefile</artifactId>
        <version>${geotools.version}</version>
    </dependency>
    <!-- Provides GUI components -->
    <dependency>
        <groupId>org.geotools</groupId>
        <artifactId>gt-swing</artifactId>
        <version>${geotools.version}</version>
    </dependency>
</dependencies>
<repositories>
    <repository>
        <id>osgeo</id>
        <name>OSGeo Release Repository</name>
        <url>https://repo.osgeo.org/repository/release/</url>
        <snapshots><enabled>false</enabled></snapshots>
        <releases><enabled>true</enabled></releases>
    </repository>
    <repository>
        <id>osgeo-snapshot</id>
        <name>OSGeo Snapshot Repository</name>
        <url>https://repo.osgeo.org/repository/snapshot/</url>
        <snapshots><enabled>true</enabled></snapshots>
        <releases><enabled>false</enabled></releases>
    </repository>
</repositories>

4.2 创建一个启动类

package cn.surpass.geotools.tutorial.query;

import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFactorySpi;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.Query;
import org.geotools.data.postgis.PostgisNGDataStoreFactory;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.swing.action.SafeAction;
import org.geotools.swing.data.JDataStoreWizard;
import org.geotools.swing.table.FeatureCollectionTableModel;
import org.geotools.swing.wizard.JWizard;
import org.opengis.feature.type.FeatureType;
import org.opengis.filter.Filter;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Map;

/**
 * @author SurpassLiang
 * @version 1.0
 * @ClassName cn.surpass.geotools.tutorial.query
 * @date 2021/6/25  22:56
 * @desc
 */
public class QueryLab extends JFrame {
    private DataStore dataStore;
    private JComboBox<String> featureTypeCBox;
    private JTable table;
    private JTextField text;

    public static void main(String[] args) throws Exception {
        JFrame frame = new QueryLab();
        frame.setVisible(true);
    }
}

4.3 初始化选择文件源的功能按钮

private void initFileAction(JMenu fileMenu){
    fileMenu.add(new SafeAction("Open shapefile...") {
        @Override
        public void action(ActionEvent e) throws Throwable {
            connect(new ShapefileDataStoreFactory());
        }
    });
    fileMenu.add(new SafeAction("Connect to PostGIS database...") {
        @Override
        public void action(ActionEvent e) throws Throwable {
            connect(new PostgisNGDataStoreFactory());
        }
    });
    fileMenu.add(new SafeAction("Connect to DataStore...") {
        public void action(ActionEvent e) throws Throwable {
            connect(null);
        }
    });
    fileMenu.addSeparator();
    fileMenu.add(new SafeAction("Exit") {
        public void action(ActionEvent e) throws Throwable {
            System.exit(0);
        }
    });
}

4.4 初始化数据选择功能按钮

private void initDataAction(JMenu dataMenu){
    dataMenu.add(new SafeAction("Get features") {
        public void action(ActionEvent e) throws Throwable {
            filterFeatures();
        }
    });
    dataMenu.add(new SafeAction("Count") {
        public void action(ActionEvent e) throws Throwable {
            countFeatures();
        }
    });
    dataMenu.add(new SafeAction("Geometry") {
        public void action(ActionEvent e) throws Throwable {
            queryFeatures();
        }
    });
}

4.5 初始化构造方法

public QueryLab() {
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    getContentPane().setLayout(new BorderLayout());

    text = new JTextField(80);
    text.setText("include"); // include selects everything!
    getContentPane().add(text, BorderLayout.NORTH);

    table = new JTable();
    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    table.setModel(new DefaultTableModel(5, 5));
    table.setPreferredScrollableViewportSize(new Dimension(500, 200));

    JScrollPane scrollPane = new JScrollPane(table);
    getContentPane().add(scrollPane, BorderLayout.CENTER);

    JMenuBar menubar = new JMenuBar();
    setJMenuBar(menubar);

    JMenu fileMenu = new JMenu("File");
    menubar.add(fileMenu);

    featureTypeCBox = new JComboBox<>();
    menubar.add(featureTypeCBox);

    JMenu dataMenu = new JMenu("Data");
    menubar.add(dataMenu);
    pack();

    initFileAction(fileMenu);
    initDataAction(dataMenu);
}

4.6 连接数据存储

在快速入门中,我们使用FileDataStoreFinder连接到特定文件。这次我们将使用更通用DataStoreFinder的连接参数映射。

请注意,相同的代码可用于连接到由DataStoreFactorySpi( Service Provider Interface ) 参数指定的完全不同类型的数据存储 。文件菜单操作使用ShapefileDataStoreFactoryor 或的实例调用此方法PostgisNGDataStoreFactory

JDataStoreWizard显示一个对话框,其中包含适用于 shapefile 或 PostGIS 数据库的输入字段。它需要比JFileDataStoreChooserQuickstart 中使用的代码多几行来提示用户输入 shapefile,但允许更好的控制。

  • 文件菜单操作调用此方法进行连接。

    private void connect(DataStoreFactorySpi format) throws Exception {
      JDataStoreWizard wizard = new JDataStoreWizard(format);
      int result = wizard.showModalDialog();
      if (result == JWizard.FINISH) {
          Map<String, Object> connectionParameters = wizard.getConnectionParameters();
          dataStore = DataStoreFinder.getDataStore(connectionParameters);
          if (dataStore == null) {
              JOptionPane.showMessageDialog(null, "Could not connect - check parameters");
          }
          updateUI();
      }
    }
    
  • 更新用于选择特征类型的组合框的辅助方法:

    private void updateUI() throws Exception {
      ComboBoxModel<String> cbm = new DefaultComboBoxModel<>(dataStore.getTypeNames());
      featureTypeCBox.setModel(cbm);
      table.setModel(new DefaultTableModel(5, 5));
    }
    

4.7 查询

下面是我们显示所选特征的策略:

  • 获取用户选择的特征类型名称,并FeatureSourceDataStore.
  • 获取在文本字段中输入的查询条件,并使用 CQL 类创建 Filter对象。
  • 将 传递FiltergetFeatures方法,该方法将与查询匹配的特征作为FeatureCollection.
  • FeatureCollectionTableModel为我们的对话框创建一个JTable. 这个 GeoTools 类接受FeatureCollection并检索每个要素的要素属性名称和数据。
4.7.1 使用featureSource.getFeatures(filter)获取要素数据
private void filterFeatures() throws Exception {
    String typeName = (String) featureTypeCBox.getSelectedItem();
    SimpleFeatureSource source = dataStore.getFeatureSource(typeName);

    Filter filter = CQL.toFilter(text.getText());
    SimpleFeatureCollection features = source.getFeatures(filter);
    FeatureCollectionTableModel model = new FeatureCollectionTableModel(features);
    table.setModel(model);
}
4.7.2 获取数据数目
 private void countFeatures() throws Exception {
    String typeName = (String) featureTypeCBox.getSelectedItem();
    SimpleFeatureSource source = dataStore.getFeatureSource(typeName);

    Filter filter = CQL.toFilter(text.getText());
    SimpleFeatureCollection features = source.getFeatures(filter);

    int count = features.size();
    JOptionPane.showMessageDialog(text, "Number of selected features:" + count);
}
4.7.3 重新映射查询结果信息

通过使用Query数据结构,您可以更好地控制您的请求,允许您只选择所需的属性;控制返回多少特征;并要求一些特定的处理步骤,例如重投影。

 private void queryFeatures() throws Exception {
    String typeName = (String) featureTypeCBox.getSelectedItem();
    SimpleFeatureSource source = dataStore.getFeatureSource(typeName);

    FeatureType schema = source.getSchema();
    String name = schema.getGeometryDescriptor().getLocalName();

    Filter filter = CQL.toFilter(text.getText());
    Query query = new Query(typeName, filter, new String[] {name});
    SimpleFeatureCollection features = source.getFeatures(query);
    FeatureCollectionTableModel model = new FeatureCollectionTableModel(features);
    table.setModel(model);
}
  • 13
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

surpassLiang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值