meface/docs/article/gis/geotools/03feature.md

339 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: GeoTools-创建要素Feature
date: 2023-11-19
author: ac
categries:
- GIS
tags:
- GeoTools
---
### GeoTools-创建要素
> csv2shp通过csv转点shp文件学习
>
> 1. 如何创建`FeatureType`、`FeatureCollection`和`Features`
> 2. 通过`GeometryFactory`构建`Points`点集;
> 3. 输出shp文件
> 4. 设置投影。
#### 1.数据准备
下载一份[csv文件](https://docs.geotools.org/latest/userguide/_downloads/d4bcf8751cc3f33a9fb673902a960e53/locations.csv) ,内容格式如下:
```text
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.添加依赖
```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>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>
```
#### 3. 示例
```java
package learning;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.feature.simple.SimpleFeatureType;
import org.geotools.data.DataUtilities;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
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 javax.swing.*;
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;
/**
* @author ac
* @date 2023/11/20 15:44
*/
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;
}
/*
* 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.
*
* 通过 DataUtilities 创建 FeatureType 类似定义shp文件的名称、几何类型、属性字段、空间参考等信息。
* createType (String typeName, String typeSpec)
* typeName:要素名称
* typeSpec是"name:Type,name2:Type2,..."格式的字符串用于定义要素的属性字段其中Type的类型有
* Intergerint、Interger
* DoubleDouble, double
* String"",String,string
* GeometryPoint,LineString,Polygon,MultiLineString,MultiPolygon,MultiPoint,GeometryCollection
* 还可以是 UUID、Date或是Java的类的全名含包名
* 示例:
* name:"",age:0,geom:Geometry,centroid:Point,url:java.io.URL"
* id:String,polygonProperty:Polygon:srid=32615
*/
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);
/*
* 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.
* 创建几何工厂实例,要素构建者实例
* 通过缓冲流一行行读取解析数据构建Point实例
*/
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 ! */
// 创建几何实例geometry - point
Point point = geometryFactory.createPoint(new Coordinate(longitude, latitude));
// 创建要素实例feature
// feature
// ↙ ↘
// geometry properties
featureBuilder.add(point);
featureBuilder.add(name);
featureBuilder.add(number);
//buildFeature(id)创建一个要素,ID可以是null,当ID为null时会由builder内部生成
SimpleFeature feature = featureBuilder.buildFeature(null);
features.add(feature);
}
}
}
/*
* Get an output file name and create the new shapefile
*
* 设置输出路径
*/
File newFile = getNewShapeFile(file);
// 创建shapefile类型的数据存储工厂实例
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);
/*
* Write the features to the shapefile
* 创建“create”类型的事务输出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);
}
}
/**
* Prompt the user for the name and path to use for the output shapefile
* 弹窗让用户选择新生成的shp文件的保存位置
*
* @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;
}
}
```
#### 4. 归纳
下面是Java中的一些对象与`Geospatial`中的类的对应关系:
| Java | Geospatial |
|:-------- |:------------- |
| `Object` | `Feature` |
| `Class` | `FeatureType` |
| `Field` | `Attribute` |
| `Method` | `Operation` |
1. 先创建 `SimpleFeatureType`,就像新建了一个`shpefile`文件一样定义了shp 文件名和属性字段。
2. 通过缓冲流解析文件中的坐标和属性信息,使用`GeometryFactory`几何工厂实例创建几何实例,再通过`featureBuilder`构建要素实例`feature`
##### 数据存储
![image-20231130111920373](./images/image-20231130111920373.png)
`DataStore`代表一份要素数据的物理的源,如`shpaefile`文件、数据库要素会转为SimpleFeature实例对象
##### 要素存储
`FeatureSource` 提供更易于操作feature data要素数据的API当使用数据源(如shapefile或数据库表)时,您将首先创建一个`DataStore`对象来连接到物理源,然后检索一个`FeatureSource`来处理要素数据。
![image-20231130143821490](./images/image-20231130143821490.png)
> `FeatureSource`是用来【读取】要素的类似`View`,而其子类`FeatureStore`是用来【读写】要素的类似于`Table`
```java
File file = ...
FileDataStore store = FileDataStoreFinder.getDataStore(file);
FeatureSource featureSource = store.getFeatureSource();
```
##### **事务**
`Transaction` 是有要素存储`FeatureStore`的事务控制器。
在这个接口的帮助下可以安全地修改Shapefiles、数据库等。事务还可以在使用锁定`Feature`要素时提供授权。
所有操作都被认为是在一个事务中进行的。`Transaction.AUTO_COMMIT`用于表示自动提交事务模式。
#### 参考文章
[1] Feature Tutorial https://docs.geotools.org/latest/userguide/tutorial/feature/csv2shp.html