查询应用
java示例将通过使用Filter 从shapefile或其他DataStore中选择FeatureCollection 。
这次我们将使用连接参数来连接到我们的数据存储;在本例的最后,您将有机会使用PostGIS或Web功能服务器进行测试
1.请确认pom.xml中包含了以下内容
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<geotools.version>23-SNAPSHOT</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 PostGIS. Note the different groupId -->
<dependency>
<groupId>org.geotools.jdbc</groupId>
<artifactId>gt-jdbc-postgis</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>maven2-repository.dev.java.net</id>
<name>Java.net repository</name>
<url>http://download.java.net/maven/2</url>
</repository>
<repository>
<id>osgeo</id>
<name>Open Source Geospatial Foundation Repository</name>
<url>http://download.osgeo.org/webdav/geotools/</url>
</repository>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>boundless</id>
<name>Boundless Maven Repository</name>
<url>http://repo.boundlessgeo.com/main</url>
</repository>
</repositories>
2.创建org.geotools.tutorial.filter和类QueryLab类和复制和粘贴以下内容:
/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2006-2008, Open Source Geospatial Foundation (OSGeo)
*
* This file is hereby placed into the Public Domain. This means anyone is
* free to do whatever they wish with this file. Use it well and enjoy!
*/
package org.geotools.tutorial.filter;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.util.Map;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;
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.SimpleFeatureIterator;
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.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Point;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.type.FeatureType;
import org.opengis.filter.Filter;
/**
* The Query Lab is an excuse to try out Filters and Expressions on your own data with a table to
* show the results.
*
* <p>Remember when programming that you have other options then the CQL parser, you can directly
* make a Filter using CommonFactoryFinder.getFilterFactory2().
*/
@SuppressWarnings("serial")
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);
}
应用程序GUI
接下来,我们将创建应用程序用户界面,其中包括用于输入查询的文本字段和用于显示查询选择的特性的数据的表。
下面是创建控件的代码:
1.添加以下构造函数:
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();
2.接下来,我们添加菜单项和行动的文件菜单连接到shapefile或PostGIS数据库:
每个操作都调用相同的方法,但是传递不同的数据存储工厂
fileMenu.add(
new SafeAction("Open shapefile...") {
public void action(ActionEvent e) throws Throwable {
connect(new ShapefileDataStoreFactory());
}
});
fileMenu.add(
new SafeAction("Connect to PostGIS database...") {
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);
}
});
3.现在让我们看看数据Data 项和Actions
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();
}
});
}
连接到数据存储
在《快速起步》中,我们使用了FileDataStoreFinder来连接到一个特定的文件。这次我们将使用更通用的DataStoreFinder,它获取连接参数的映射
注意,可以使用相同的代码连接到DataStoreFactorySpi(服务提供者接口)参数指定的不同类型的数据存储。文件菜单动作通过ShapefileDataStoreFactory或PostgisNGDataStoreFactory的实例调用此方法。
JDataStoreWizard显示一个对话框,其中的输入字段适合于shapefile或PostGIS数据库。它需要比JFileDataStoreChooser多几行代码,JFileDataStoreChooser在《快速起步》中用于提示用户输入一个shapefile,但允许更大的控制
1.文件菜单操作调用此方法进行连接
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();
}
}
2.帮助方法更新组合框用于选择一个功能类型:
private void updateUI() throws Exception {
ComboBoxModel<String> cbm = new DefaultComboBoxModel<>(dataStore.getTypeNames());
featureTypeCBox.setModel(cbm);
table.setModel(new DefaultTableModel(5, 5));
}
查询
过滤器类似于SQL语句的where子句;定义每个特性需要满足的条件,以便选择。
以下是我们的策略,显示选定的功能:
1.获取用户选择的功能类型名称,并从DataStore中检索相应的功能资源。
2.获取在文本字段中输入的查询条件,并使用CQL类创建筛选器对象。
3.将Filter 传递给getFeatures方法,该方法将与查询匹配的功能作为FeatureCollection.返回。
4.为对话框的JTable创建一个FeatureCollectionTableModel。这个GeoTools类接受一个FeatureCollection 并检索每个特性的特性属性名称和数据。
考虑到这个策略,这里是实现:
1.使用featureSource.getFeatures(过滤器)获取特性数据
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);
}
2.FeatureCollection 表现为预定义的查询或结果集,不将数据加载到内存中。
您可以使用可用的方法来询问整个FeatureCollection 的问题。
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);
}
3.通过使用查询数据结构,您可以更好地控制您的请求,允许您只选择所需的属性;控制返回多少特性;并要求一些特定的处理步骤,如重新投影。
下面是一个仅选择几何属性并将其显示在表中的示例。
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);
}
运行程序
现在我们可以运行应用程序,并尝试其中一些想法:
1.启动应用程序并从“文件”菜单中选择“打开shapefile…”。
JDataStoreWizard将提示您输入一个文件。请选择cities.shp shapefile可作为前面教程中使用的uDig示例数据集的一部分。
image.png
2.按Next进入带有可选参数的页面。对于本例,请按Finish继续通过这些选项。 image.png
3.成功连接到shapefile后,菜单栏中的组合框将显示可用功能类型的名称。shapefile的单一类型并不那么令人兴奋,但是当您使用PostGIS时,您应该能够选择在这里使用哪个表。
4.查询字段将表明我们希望使用通用查询语言选择所有功能:
include
选择数据‣功能菜单项和数据表将显示功能。
image.png
5.通用查询语言支持简单的测试,比如选择CNTRY_NAME属性为“France”的特性:
CNTRY_NAME = 'France'
并选择要显示的Get要素
image.png
6.支持比较,例如POP_RANK属性的值>= 5的特性:
POP_RANK >= 5
7.支持布尔逻辑,允许您组合多个测试
CNTRY_NAME = 'Australia' AND POP_RANK > 5
8.也支持空间查询:
BBOX(the_geom, 110, -45, 155, -10)
这是一个边界框查询,将选择110 - 155°W, 10 - 45°S范围内的所有功能(澳大利亚周围的一个松散框)。
注意,我们将几何属性命名为Point类型,对于cities shapefile来说。
Shapefile的几何结构总是被称为the_geom,对于其他数据存储,我们需要查找几何属性的名称。
其他尝试
尝试连接到公共PostGIS实例。
1.从“文件”菜单中选择“连接到PostGIS数据库…”,并填写以下参数
image.png
如果你没有PostGIS数据库,你可以尝试使用以下凭证连接到公共在线数据库:
image.png
接下来,向导将显示可选参数的第二页。对于本例,您可以保留此空白,然后单击Finish按钮。
2.我们已经看到了如何使用CQL表示过滤器。还有web功能服务器使用的原始XML表示。
Configuration configuration = new org.geotools.filter.v1_0.OGCConfiguration();
Parser parser = new Parser(configuration);
...
Filter filter = (Filter) parser.parse(inputstream);
如果需要从XML文件开始,可以使用。
Configuration = new org.geotools.filter.v1_0.OGCConfiguration();
Encoder encoder = new org.geotools.xsd.Encoder(configuration);
encoder.encode(filter, org.geotools.filter.v1_0.OGC.FILTER, outputstream);
要使这些示例工作,您需要依赖gt-xml。
3.前面我们介绍了使用FeatureIterator 筛选 FeatureCollection的内容。使用此思想与查询允许您在确定功能集合的中心时仅使用几何图形。
private void centerFeatures() throws Exception {
String typeName = (String) featureTypeCBox.getSelectedItem();
SimpleFeatureSource source = dataStore.getFeatureSource(typeName);
Filter filter = CQL.toFilter(text.getText());
FeatureType schema = source.getSchema();
String name = schema.getGeometryDescriptor().getLocalName();
Query query = new Query(typeName, filter, new String[] {name});
SimpleFeatureCollection features = source.getFeatures(query);
double totalX = 0.0;
double totalY = 0.0;
long count = 0;
try (SimpleFeatureIterator iterator = features.features()) {
while (iterator.hasNext()) {
SimpleFeature feature = iterator.next();
Geometry geom = (Geometry) feature.getDefaultGeometry();
Point centroid = geom.getCentroid();
totalX += centroid.getX();
totalY += centroid.getY();
count++;
}
}
double averageX = totalX / (double) count;
double averageY = totalY / (double) count;
Coordinate center = new Coordinate(averageX, averageY);
JOptionPane.showMessageDialog(text, "Center of selected features:" + center);
}
Filter
要从特性资源请求信息,我们需要描述(或选择)我们想要返回的信息。我们用于此目的的数据结构称为筛选器。
我们在地理工具中有一个很好的解析器,它可以用来创建一个人类可读形式的过滤器:
Filter filter = CQL.toFilter("POPULATION > 30000");
我们还可以使用CQL来制作空间过滤器——几何图形使用之前用于JTS几何图形的wkt格式来表示:
Filter pointInPolygon = CQL.toFilter("CONTAINS(THE_GEOM, POINT(1 2))");
Filter clickedOn = CQL.toFilter("BBOX(ATTR1, 151.12, 151.14, -33.5, -33.51)";
你也可以跳过CQL,直接使用FilterFactory:
FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
Filter filter = ff.propertyGreaterThan(ff.property("POPULATION"), ff.literal(12));
您的IDE应该提供命令完成功能,允许您快速查看FilterFactory提供了哪些功能。
注意,filter是一个真实的java对象,你可以用它来工作:
if(filter.evaluate(feature)){
System.out.println("Selected "+ feature.getId();
}
注意
您可能已经注意到Filter实际上是一个接口。由于筛选器数据结构是由规范定义的,因此我们不
能支持定义新的筛选器对象,也不能期望与之通信的外部服务能够理解它们。
GeoTools中的实现非常灵活,能够处理特性、hashmap和javabean。
好消息是过滤器可以扩展新的功能;我们的实现可以通过使用PropertyAccessors来学习如何处理新类型的数据。
Expression
你可能在上一节漏掉了;但是我们还描述了如何使用表达式访问数据。
以下是一些例子:
ff.property("POPULATION"); // expression used to access the attribute POPULATION from a feature
ff.literal(12); // the number 12
您还可以使用表达式库进行函数调用。
以下是一些例子:
CQL.toExpression("buffer(THE_GEOM)");
CQL.toExpression("strConcat(CITY_NAME, POPULATION)");
CQL.toExpression("distance(THE_GEOM, POINT(151.14,-33.51))");
Query
查询数据结构用于对返回的结果提供更细粒度的控制。下面的查询将请求THE_GEOM和人口从一个功能资源“城市”:
Query query = new Query("cities", filter, new String[]{ "THE_GEOM", "POPULATION" });
FeatureCollection
在前面的CSV2SHP示例中,我们向FeatureCollection添加了一些特性。这是很容易的,因为FeatureCollection是在内存中时。在处理空间数据时,我们试图在内存中不使用FeatureCollection,因为空间数据会在短时间内变得很大。
当使用FeatureCollection with a FeatureIterator遍历内存的内容时,需要特别小心。FeatureIterator实际上会将数据从磁盘上传输出去,我们需要记住当我们完成时关闭流。
尽管FeatureCollection是一个集合,但它非常懒,在开始遍历内容之前不会加载任何东西。
与featurecall和featuator最接近的Java概念来自JDBC,如下所示。
image.png
如果这太多了,请记住——当你完成时,请关闭你的特性迭代器。否则,你将会泄漏资源并陷入麻烦。
网友评论