要素教程
欢迎
您应该在运行本工作簿之前完成GeoTools的快速入门。我们需要确保您有一个使用GeoTools jar及其所有依赖项的环境。对于那些只使用maven的,我们将在每个部分开始时提供所需的依赖项
本工作簿采用了一种新的“代码优先”方法。我们已经尽了一切努力使这些例子既可视化又以代码为中心。我们已经包括了一些背景材料,解释了概念和想法,如果你感兴趣
CSV2SHP
我们今年正在尝试引入新功能;我们将从从头开始构建一个shapefile开始,这样您就可以看到创建特性的每一个细节,而不是阅读一个shapefile并在一个人工练习中将其分解。
教程包含以下内容:
1.创建一个FeatureType, FeatureCollection 和Features
2.使用GeometryFactory来构建点
3.输出一个shapefile文件
4.投影
在本教程的最后,您将能够创建自己的自定义shapefile。
csv数据
1.创建一个txt文件并拷贝下面数据进去
LAT, LON, CITY, NUMBER
46.066667, 11.116667, Trento, 140
44.9441, -93.0852, St Paul, 125
13.752222, 100.493889, Bangkok, 150
45.420833, -75.69, Ottawa, 200
44.9801, -93.251867, Minneapolis, 350
46.519833, 6.6335, Lausanne, 560
48.428611, -123.365556, Victoria, 721
-33.925278, 18.423889, Cape Town, 550
-33.859972, 151.211111, Sydney, 436
41.383333, 2.183333, Barcelona, 914
39.739167, -104.984722, Denver, 869
52.95, -1.133333, Nottingham, 800
45.52, -122.681944, Portland, 840
37.5667,129.681944,Seoul,473
50.733992,7.099814,Bonn,700,2016
2.或者下载 locations.csv
.
3.添加一些其他的点,如你的家乡
依赖
请确保您的pom.xml包含以下内容:
<dependencies>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-epsg-hsql</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>
注意,上面提到的jar将引入大量其他依赖项(例如hsql数据库驱动程序)
主程序
1.请创建一个包org.geotools.tutorial.feature 和类 Csv2Shape.java .
2.粘贴复制下面代码
package org.geotools.tutorial.feature;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.UIManager;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.Transaction;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.swing.data.JFileDataStoreChooser;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
/**
* This example reads data for point locations and associated attributes from a comma separated text
* (CSV) file and exports them as a new shapefile. It illustrates how to build a feature type.
*
* <p>Note: to keep things simple in the code below the input file should not have additional spaces
* or tabs between fields.
*/
public class Csv2Shape {
public static void main(String[] args) throws Exception {
// Set cross-platform look & feel for compatability
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
File file = JFileDataStoreChooser.showOpenFile("csv", null);
if (file == null) {
return;
}
现在,我们将在小节中查看主要方法的其余部分
创建一个FeatureType
根据csv中的数据创建一个FeatureType并写入shapefile
这是DataUtilities 类
/*
* We use the DataUtilities class to create a FeatureType that will describe the data in our
* shapefile.
*
* See also the createFeatureType method below for another, more flexible approach.
*/
final SimpleFeatureType TYPE =
DataUtilities.createType(
"Location",
"the_geom:Point:srid=4326,"
+ // <- the geometry attribute: Point type
"name:String,"
+ // <- a String attribute
"number:Integer" // a number attribute
);
System.out.println("TYPE:" + TYPE);
创建features
我们现在可以读取CSV文件并为每个记录创建一个特性。请注意以下事项:
1.使用GeometryFactory创建新点
2.使用SimpleFeatureBuilder创建特性(SimpleFeature对象)
* A list to collect features as we create them.
*/
List<SimpleFeature> features = new ArrayList<>();
/*
* GeometryFactory will be used to create the geometry attribute of each feature,
* using a Point object for the location.
*/
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
/* First line of the data file is the header */
String line = reader.readLine();
System.out.println("Header: " + line);
for (line = reader.readLine(); line != null; line = reader.readLine()) {
if (line.trim().length() > 0) { // skip blank lines
String tokens[] = line.split("\\,");
double latitude = Double.parseDouble(tokens[0]);
double longitude = Double.parseDouble(tokens[1]);
String name = tokens[2].trim();
int number = Integer.parseInt(tokens[3].trim());
/* Longitude (= x coord) first ! */
Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude));
featureBuilder.add(point);
featureBuilder.add(name);
featureBuilder.add(number);
SimpleFeature feature = featureBuilder.buildFeature(null);
features.add(feature);
}
}
}
从要素集中创建一个shapefile
注意点:
1.使用DataStoreFactory和一个参数来表示我们需要一个空间索引
2.使用createSchema(SimpleFeatureType)方法来设置shapefile(我们将在下一节中创建getNewShapeFile方法)
/*
* Get an output file name and create the new shapefile
*/
File newFile = getNewShapeFile(file);
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
Map<String, Serializable> params = new HashMap<>();
params.put("url", newFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStore newDataStore =
(ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
/*
* TYPE is used as a template to describe the file contents
*/
newDataStore.createSchema(TYPE);
把要素写入shapefile文件中
注意事项:
1.通过确认FeatureSource对象实现了FeatureStore方法,我们可以检查是否具有读写访问权限
2.花点时间检查一下shapefile与我们的模板(SimpleFeatureType类型)的匹配程度。比较这个输出,看看它们有什么不同
3.我们使用的SimpleFeatureStore需要一个FeatureCollection 对象,因此我们将特性列表包装在ListFeatureCollection中
4.使用transaction.commit()可以一次安全地写出这些特性
/*
* Write the features to the shapefile
*/
Transaction transaction = new DefaultTransaction("create");
String typeName = newDataStore.getTypeNames()[0];
SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);
SimpleFeatureType SHAPE_TYPE = featureSource.getSchema();
/*
* The Shapefile format has a couple limitations:
* - "the_geom" is always first, and used for the geometry attribute name
* - "the_geom" must be of type Point, MultiPoint, MuiltiLineString, MultiPolygon
* - Attribute names are limited in length
* - Not all data types are supported (example Timestamp represented as Date)
*
* Each data store has different limitations so check the resulting SimpleFeatureType.
*/
System.out.println("SHAPE:" + SHAPE_TYPE);
if (featureSource instanceof SimpleFeatureStore) {
SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
/*
* SimpleFeatureStore has a method to add features from a
* SimpleFeatureCollection object, so we use the ListFeatureCollection
* class to wrap our list of features.
*/
SimpleFeatureCollection collection = new ListFeatureCollection(TYPE, features);
featureStore.setTransaction(transaction);
try {
featureStore.addFeatures(collection);
transaction.commit();
} catch (Exception problem) {
problem.printStackTrace();
transaction.rollback();
} finally {
transaction.close();
}
System.exit(0); // success!
} else {
System.out.println(typeName + " does not support read/write access");
System.exit(1);
}
}
这就完成了主方法。
提示输出shapefile
这个方法提示用户选择shapefile输出位置,文件名为csv的文件名
/**
* Prompt the user for the name and path to use for the output shapefile
*
* @param csvFile the input csv file used to create a default shapefile name
* @return name and path for the shapefile as a new File object
*/
private static File getNewShapeFile(File csvFile) {
String path = csvFile.getAbsolutePath();
String newPath = path.substring(0, path.length() - 4) + ".shp";
JFileDataStoreChooser chooser = new JFileDataStoreChooser("shp");
chooser.setDialogTitle("Save shapefile");
chooser.setSelectedFile(new File(newPath));
int returnVal = chooser.showSaveDialog(null);
if (returnVal != JFileDataStoreChooser.APPROVE_OPTION) {
// the user cancelled the dialog
System.exit(0);
}
File newFile = chooser.getSelectedFile();
if (newFile.equals(csvFile)) {
System.out.println("Error: cannot replace " + csvFile);
System.exit(0);
}
return newFile;
}
运行程序
当你运行程序时,会提醒你:
1.选择csv文件的位置
2.选择输出的shp文件的位置
另外尝试
另一种构建SimpleFeatureType的方法
尽管上面使用的DataUtilities 类提供了一种快速且简单的方法来构建一个简单的featuretype。对于任何实际的应用程序,您都希望使用更灵活的SimpleFeatureTypeBuilder。
下面是如何使用SimpleFeatureTypeBuilder来完成相同的结果:
/**
* Here is how you can use a SimpleFeatureType builder to create the schema for your shapefile
* dynamically.
*
* <p>This method is an improvement on the code used in the main method above (where we used
* DataUtilities.createFeatureType) because we can set a Coordinate Reference System for the
* FeatureType and a a maximum field length for the 'name' field dddd
*/
private static SimpleFeatureType createFeatureType() {
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.setName("Location");
builder.setCRS(DefaultGeographicCRS.WGS84); // <- Coordinate reference system
// add attributes in order
builder.add("the_geom", Point.class);
builder.length(15).add("Name", String.class); // <- 15 chars width for name field
builder.add("number", Integer.class);
// build the type
final SimpleFeatureType LOCATION = builder.buildFeatureType();
return LOCATION;
}
注意使用大写常量来保存SimpleFeatureType。因为SimpleFeatureType类是不可变的,所以将它们作为最终变量进行跟踪可以帮助您记住,一旦创建它们,就不能修改它们。
使用这个方法,我们的SimpleFeatureType包含一个CoordinateReferenceSystem,因此不需要调用forceSchemaCRS来生成.prj文件。另外,我们现在将Name字段限制为15个字符。
其他尝试的
1.修改代码从数据文件头读取特征属性名,而不是在应用程序中硬编码:
LAT, LON, CITY, NUMBER
您应该能够使用SimpleFeatureTypeBuilder。
2.使用几何缓冲区方法创建基于每个城市的人口规模的圆圈
Polygon polygon = location.buffer( 0.1 );
3.:编写一个快速的CSVReader很容易,就像我们在这里所做的;但要写出一篇能够正确处理引号的好文章就比较难了。JavaCSV是一个开放源码库,用于读取带有各种选项的CSV文件
4.为了快速找到相关依赖,我们可以访问 http://mvnrepository.com/.
这样的站点将直接提供一个maven依赖项,您可以将其剪切并粘贴到pom.xml中。
<dependency>
<groupId>net.sourceforge.javacsv</groupId>
<artifactId>javacsv</artifactId>
<version>2.0</version>
</dependency>
5.使用相同的技术从其他结构化文件格式(如GeoJson)的数据创建shapefile
6.地球刚刚经历了一场流星风暴——在全球范围内产生了100个不同大小的圆。你的城市看到了吗?
从模型或分析中生成一个shapefile是一种常见的用法。
7.读一下shapefile支持的其他几何类:线性特征的MultiLineString和区域特征的MultiPolygon,然后修改这个例子来使用它们
Feature
Feature是可以画在地图上的东西。严格的定义是,一个特征是现实世界中的东西——风景的特征——珠穆朗玛峰,埃菲尔铁塔,甚至是像你的大姨妈爱丽丝那样四处走动的东西。
向Java开发人员解释这个概念很容易——特性就是对象。
与java对象一样,特性可以包含一些关于它们所代表的真实世界的信息。这些信息被组织到属性中,就像Java信息被组织到字段中一样。
时你有两个有很多共同点的特征。洛杉矶的LAX机场和悉尼的SYD机场。因为这两个特性有一些共同点,所以最好将它们组合在一起——在Java中,我们将创建一个名为Airport的类。在地图上,我们将创建一个名为Airport的功能类型。
尽管Java不支持它,但早期的编程语言使用了一个原型系统(而不是类系统),它支持许多“一次性”对象。你会发现这种情况在绘制地图时相当普遍——因为埃菲尔铁塔有多少座?你偶尔也会发现相同的现实世界的事物以不同的方式表现出来(埃菲尔铁塔可以是一个地标,也可以是一座塔,这取决于背景)。
这里有一个方便的备忘单:
image.png
特性模型实际上比我们Java程序员习惯的要疯狂一点,因为它认为属性和操作都是特性的“属性”。也许,当Java获得闭包时,我们就可以迎头赶上了
对我来说,真正有趣的事情是,地图制作者们早在15世纪就开始整理这些东西了,而且他们也变得像现在的程序员一样极客。因此,尽管我们很乐意教他们关于面向对象编程的知识,但他们已经有了丰富的思想来描述这个世界。好的一面是,地图制作者开始使用UML图。
FeatureClass
在GeoTools中,我们有一个由GeoAPI项目提供的特性、特性类型和属性的接口。一般来说,GeoAPI提供了一个非常严格的接口,而GeoTools将提供一个类。
image.png
一个Feature 通常只有简单的属性(字符串、整数、日期等),而没有对其他特性或数据结构的引用,比如List<Date>。满足这一需求的Features 是如此常见,以至于我们创建了一个子类来表示它们,这个子类叫做SimpleFeature。
在Java级别,GeoTools提供的特性API与Java .util类似。使用Map——它是一个用于保存信息的Java数据结构。您可以通过键查找属性值;键的列表是由FeatureType提供的。
Geometry
feature和object的另一个区别是,feature具有某种形式的位置信息(如果没有,我们就不能在地图上绘制它)。位置信息将由存储在属性中的几何图形(或形状)捕获。
image.png我们利用JTS拓扑套件(JTS)来表示几何图形。JTS库提供了一种优秀的几何图形实现——并且因为拥有递归首字母缩写而获得极客的分数!JTS是一个令人惊奇的库,它提供了所有的硬图理论,让您以高效的方式处理几何。
下面是一个使用文本(WKT)格式创建点的示例
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory( null );
WKTReader reader = new WKTReader( geometryFactory );
Point point = (Point) reader.read("POINT (1 1)");
您还可以直接使用GeometryFactory手动创建一个点。
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory( null );
Coordinate coord = new Coordinate( 1, 1 );
Point point = geometryFactory.createPoint( coord );
DataStore
我们在《快速起步》中已经遇到了数据存储。DataStore API用于表示包含空间数据的文件、数据库或服务。该API有两个移动部分,如下所示。
image.png
FeatureSource用于读取features,而FeatureStore子类用于读写访问。
判断一个文件是否可以在GeoTools中写入的方法是使用instanceof 检查
String typeNames = dataStore.getTypeNames()[0];
SimpleFeatureSource source = store.getfeatureSource( typeName );
if( source instanceof SimpleFeatureStore){
SimpleFeatureStore store = (SimpleFeatureStore) source; // write access!
store.addFeatures( featureCollection );
store.removeFeatures( filter ); // filter is like SQL WHERE
store.modifyFeature( attribute, value, filter );
}
我们决定将写访问作为一个子类(而不是isWritable方法)来处理,以避免方法被使用。
网友评论