前言
❝
在GIS开发中,经常需要进行数据的转换处理。在之前的文章中讲了如何使用GeoTools读取Shapefile数据,并且展示了【五种】将Shapefile数据导入PostGIS空间数据库的方式,但是还缺少Shapefile数据转换来源的操作。
本篇教程在之前文章的基础上讲解如何将GeoJSON
数据转换为我们熟悉的Shapefile
文件格式。
开发环境
本文使用如下开发环境,以供参考。
时间:2025年
GeoTools:34-SNAPSHOT
IDE:IDEA2025.1.2
JDK:17
1. 准备 GeoJSON 文件
GeoJSON
是一种用于编码各种地理数据结构的格式,采用JSON
方式表示。在WebGIS
开发中,被广泛应用于数据传输和共享交换。
有关GeoJSON数据的详细介绍,请参考往期文章:GeoJSON 数据简介
如下是本文选取的一些地震点GeoJSON数据结构:
json
{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": {"name":"洛杉矶"}, "geometry": { "type": "Point", "coordinates": [ 149.042007, -35.349998 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶" }, "geometry": { "type": "Point", "coordinates": [ -71.660004, -35.423000 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶"}, "geometry": { "type": "Point", "coordinates": [ 174.315994, -35.721001 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶"}, "geometry": { "type": "Point", "coordinates": [ -64.288002, -36.627998 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶"}, "geometry": { "type": "Point", "coordinates": [ -73.055000, -36.826000 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶"}, "geometry": { "type": "Point", "coordinates": [ 174.761002, -36.903999 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶"}, "geometry": { "type": "Point", "coordinates": [ 176.154999, -37.700001 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶"}, "geometry": { "type": "Point", "coordinates": [ 175.272995, -37.776001 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶" }, "geometry": { "type": "Point", "coordinates": [ 145.075104, -37.852959 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶" }, "geometry": { "type": "Point", "coordinates": [ 178.016006, -38.659000 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶" }, "geometry": { "type": "Point", "coordinates": [ -62.274067, -38.725273 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶"}, "geometry": { "type": "Point", "coordinates": [ -72.575996, -38.729000 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶" }, "geometry": { "type": "Point", "coordinates": [ -68.283333, -38.944908 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶"}, "geometry": { "type": "Point", "coordinates": [ 174.078003, -39.070000 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶"}, "geometry": { "type": "Point", "coordinates": [ 176.841003, -39.639999 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶"}, "geometry": { "type": "Point", "coordinates": [ 175.035004, -39.930000 ] } },
{ "type": "Feature", "properties": {"name":"洛杉矶"}, "geometry": { "type": "Point", "coordinates": [ -62.995026, -40.815025 ] } },
]
}
注:properties
属性字段name
原数据为空,本文为了测试将其修改为"洛杉矶",仅供参考。
2. 安装依赖
pom.xml
文件在之前开发的基础上增加gt-geojson
和jts-core
两个依赖包。
xml
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geojson</artifactId>
<version>${geotools.version}</version>
</dependency>
<!-- JTS 几何库 -->
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>1.18.2</version>
</dependency>
3. 创建 Shp 要素结构
使用showOpenFile
方法打开文件选择框,然后使用createType
构造要素结构,第一个参数"HeatPoint"
为要素类型,第二个参数为要素属性。the_geom
字段表明数据几何类型为Point
;srid
表明数据坐标系为4326
以及后面的字段名称和对应字段类型,此处根据GeoJSON
文件结构修改。
java
// 设置外观
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
// 选择json文件
File file = JFileDataStoreChooser.showOpenFile(".json",null);
if(file == null ){
System.out.println("GeoJSON 文件不正确,请检查?");
return;
}
// 创建要素结构
final SimpleFeatureType TYPE = DataUtilities.createType(
"HeatPoint",
"the_geom:Point:srid=4326,"+
"name:String"
);
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);
除了使用DataUtilities
类createType
方法构造要素结构之外,还可以使用SimpleFeatureTypeBuilder
构造器。使用SimpleFeatureTypeBuilder
可以更灵活的设置要素属性,如坐标系统和字段长度。
csharp
// 构造要素结构方法
private static SimpleFeatureType createFeatureType() {
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.setName("HeatPoint"); // 类型名称
builder.setCRS(DefaultGeographicCRS.WGS84); // 坐标系统
// 按要素结构顺序添加要素属性
builder.add("the_geom", Point.class);
builder.length(15).add("name", String.class); // 限制字段长度
// 构造类型
return builder.buildFeatureType();
}
4. 创建 Shapefile
使用ShapefileDataStoreFactory
工厂方法生产Shp
,在createDataStore
参数中将属性"create spatial index"
设置为true
表示为Shp
数据创建空间索引。
ini
// 选择shp路径
File shpFile = getNewShapeFile(file);
// 创建Shapefile
ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
Map<String, Serializable> params = new HashMap<>();
params.put("url", shpFile.toURI().toURL());
params.put("charset", "GBK");
params.put("create spatial index",Boolean.TRUE);
ShapefileDataStore dataStore = (ShapefileDataStore) factory.createNewDataStore(params);
dataStore.createSchema(featureCollection.getSchema()); // 应用定义好的Schema
dataStore.forceSchemaCRS(CRS.decode("EPSG:4326")); // 明确CRS
对于中文字段,需要设置字符集,以防出现乱码,此处将"charset"
设置为"GBK"
,对于中文最好将字符集设置为"GBK"
,设置为"UTF-8"
还是有可能难以正确解析。
在创建Shp
对象时,还是需要指定坐标信息,否则转换结果将缺少坐标系统。在GIS软件中打开数据可以查看元素据信息,下图是未指定坐标系时在ArcMap
中的打开情况。
指定坐标系为4326
后转换结果元数据信息,使用GIS
软件ArcMap
打开如下,可以看到此时显示正确。
5. 读取 GeoJSON 并写入 Shp
使用readFeatureCollection
方法将GeoJSON
文件转换为SimpleFeatureCollection
对象。
ini
// 存储要素
List<SimpleFeature> features = new ArrayList<>();
// 读取 GeoJSON
FeatureJSON featureJSON = new FeatureJSON();
SimpleFeatureCollection featureCollection;
try(FileInputStream fis = new FileInputStream(file)){
featureCollection = (SimpleFeatureCollection) featureJSON.readFeatureCollection(fis);
}
之后根据要素结构解析Feature
对象。对于geometry对象需要对其数据类型进行判断,如果为字符串需要使用WKTReader
类进行读取转换,然后使用buildFeature
方法创建一个新要素,将其放入新的集合列表中。
ini
// 创建事务对象,准备提交数据
Transaction transaction = Transaction.AUTO_COMMIT;
// 获取存储特征对象
String typeName = dataStore.getTypeNames()[0];
SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName);
// 写入数据到shp文件
if(featureSource instanceof SimpleFeatureStore){
SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
// 准备要写入的特征集合
ListFeatureCollection collection = new ListFeatureCollection(TYPE,features);
// 复制要素特征
try(FeatureIterator<SimpleFeature> it = featureCollection.features()){
while (it.hasNext()) {
SimpleFeature feature = it.next();
// 处理几何字段
Object geom = feature.getDefaultGeometry();
Geometry geometry = null;
if(geom instanceof String){
// 解析WKT字符串(如 "POINT (116 39)")
geometry = new WKTReader().read((String) geom);
}else if(geom instanceof Geometry){
geometry = (Geometry) geom;
}
String name = (String) feature.getAttribute("name");
featureBuilder.add(geometry);
featureBuilder.add(name);
// 创建目标要素
SimpleFeature tarFeature = featureBuilder.buildFeature(null);
collection.add(tarFeature);
}
}
try{
// 写入特征到Shapefile
featureStore.addFeatures(collection);
transaction.commit();
}catch (Exception e){
e.printStackTrace();
transaction.rollback();
}finally {
transaction.close();
}
System.exit(0);
}
// 释放资源
dataStore.dispose();
转换完成后使用GIS
软件ArcMap
打开属性表结构如下。
注:使用GeoTools构造GIS数据时有些特殊,在添加要素属性时,一定要按照要素结构类型属性顺序添加,不然会读取错乱
如下要素结构中要先添加the_geom
几何字段,然后再添加name
字段。
java
// 创建要素结构
final SimpleFeatureType TYPE = DataUtilities.createType(
"HeatPoint",
"the_geom:Point:srid=4326,"+
"name:String"
6. Shapefile 输出位置
使用getNewShapeFile
方法选择Shp
输出位置,最后输出的结果就是GIS
开发中常用的Shp
格式了。
ini
// 提示输出Shapefilew文件位置
private static File getNewShapeFile(File jsonFile){
String path = jsonFile.getAbsolutePath();
String newPath = path.substring(0,path.length()-5)+".shp";
JFileDataStoreChooser chooser = new JFileDataStoreChooser(".shp");
chooser.setDialogTitle("保存ShapeFile");
chooser.setSelectedFile(new File(newPath));
int returnVal = chooser.showSaveDialog(null);
if(returnVal != JFileDataStoreChooser.APPROVE_OPTION){
System.exit(0);
}
File newFile = chooser.getSelectedFile();
if(newFile.equals(jsonFile)){
System.out.println("Error:不能替换" + jsonFile);
System.exit(0);
}
return newFile;
}
7. 错误信息
错误1:class java.lang.String cannot be cast to class org.locationtech.jts.geom.Geometry
未正确转换Geometry字段,需要进行类型转换。
错误2:Unable to delete the file:shpheatNew4.dbf when attempting to replace with temporary copy
文件存在多处读取,检查代码逻辑
错误3:Current fid index is null, next must be called before write()
在读取数据时 未正确调用 next()
方法就直接尝试写入数据。
图片效果
❝
OpenLayers示例数据下载,请回复关键字:ol数据
全国信息化工程师-GIS 应用水平考试资料,请回复关键字:GIS考试
❝【GIS之路】 已经接入了智能助手,欢迎关注,欢迎提问。
欢迎访问我的博客网站-长谈GIS :
http://shanhaitalk.com
都看到这了,不要忘记点赞、收藏 + 关注 哦 !
本号不定时更新有关 GIS开发 相关内容,欢迎关注 !