^ 关注我,带你一起学GIS ^
前言
❝
在GIS开发中,经常需要进行数据的转换处理,特别是Shapefile数据的投影转换更是重中之重,如何高效、准确的将源数据坐标系转换到目标坐标系是我们需要研究解决的问题。
本篇教程在之前文章的基础上讲解如何将使用GeoTools
工具实现Shapefile
数据重投影。
开发环境
本文使用如下开发环境,以供参考。
时间:2025年
GeoTools:34-SNAPSHOT
IDE:IDEA2025.1.2
JDK:17
1. 安装依赖
在pom.xml
文件中添加gt-shapefile
和gt-swing
两个依赖。其中gt-shapefile
用于shp
数据的转换处理,gt-swing
属于图形用户界面工具库,用于地理空间数据的可视化、交互操作和地图应用的快速开发。
<dependencies>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-swing</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>
2. 创建工具类
在开发工具中创建ShpReprojection
类,用于实现数据的重投影并导出。
在main
方法中调用显示地图方法。
public class ValidateGeometry {
// 定义Shp源数据
private File sourceFile;
// 定义要素源
private SimpleFeatureSource featureSource;
// 定义地图组件
private MapContent map;
public static void main(String[] args) throws Exception {
ShpReprojection shpReprojection = new ShpReprojection();
// 打开地图显示Shp文件
shpReprojection.displayShapefile();
}
定义displayShapefile
显示方法,用于将Shapefile
数据添加到地图窗口。通过在JMapFrame
的工具栏上添加一个按钮来导出Shapefile
文件。
// 显示Shp数据
private void displayShapefile() throws Exception{
sourceFile = JFileDataStoreChooser.showOpenFile("shp",null);
if(sourceFile==null){
System.out.println("Shp 文件未知错误,请重新选择!");
return;
}
// 数据仓库
FileDataStore dataStore = FileDataStoreFinder.getDataStore(sourceFile);
// 获取要素数据源
featureSource = dataStore.getFeatureSource();
// 创建地图
map = new MapContent();
Style style = SLD.createSimpleStyle(featureSource.getSchema());
Layer layer = new FeatureLayer(featureSource,style);
// 添加图层
map.layers().add(layer);
// 创建工具条
JMapFrame mapFrame = new JMapFrame(map);
mapFrame.enableToolBar(true);
mapFrame.enableStatusBar(true);
JToolBar toolBar = mapFrame.getToolBar();
toolBar.addSeparator();
toolBar.add(new JButton(new ExportShapefileAction()));
// 设置地图窗口大小
mapFrame.setSize(800,600);
mapFrame.setVisible(true);
}
在上面的代码中使用JMapFrame
创建了一个工具条,使用JButton
创建了一个按钮,并将其添加到工具栏中。点击该按钮,实现Shapefile
数据的导出操作。
3. 导出 Shapefile 文件
通过定义一个内部嵌套类ExportShapefileAction
来实现Shapefile
文件的导出工作。
// 定义内部嵌套类
class ExportShapefileAction extends SafeAction {
ExportShapefileAction() {
super("ExportShapefileAction");
putValue(Action.SHORT_DESCRIPTION, "Export using current crs");
}
public void action(ActionEvent e) throws Throwable{
exportToShapefile();
}
}
定义上文中的私有方法exportToShapefile
,在该方法中实现数据导出。
首先使用JFileDataStoreChooser
创建保存Shp
文件选择框,然后获取当前选择文件。例子中是通过写死坐标系进行转换,需要在导出Shp
时选择对应的投影坐标系导出。
// 导出重投影数据到Shapefile
private void exportToShapefile() throws Exception{
// 选择Shp文件
JFileDataStoreChooser chooser = new JFileDataStoreChooser("shp");
chooser.setDialogTitle("保存重投影 Shapefile 文件");
chooser.setSaveFile(sourceFile);
int returnVal = chooser.showSaveDialog(null);
if(returnVal != JFileDataStoreChooser.APPROVE_OPTION){
return;
}
File file = chooser.getSelectedFile();
if(file.equals(sourceFile)){
JOptionPane.showMessageDialog(null,"Can't replace " + file);
return;
}
// 定义坐标系
CoordinateReferenceSystem wgs84 = CRS.decode("EPSG:4326");
CoordinateReferenceSystem webMercator = CRS.decode("EPSG:3857");
boolean lenient = true;
MathTransform transform = CRS.findMathTransform(wgs84, mapCrs, lenient);
// 获取源数据结构
SimpleFeatureType schema = featureSource.getSchema();
SimpleFeatureCollection featureCollection = featureSource.getFeatures();
// 创建 Shapefile 工厂
ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
Map<String, Serializable> create = new HashMap<>();
create.put("url",file.toURI().toURL());
create.put("create spatial index",Boolean.TRUE);
create.put("charset","GBK"); // 设置中文字符集,以防乱码
ShapefileDataStore dataStore = (ShapefileDataStore) factory.createNewDataStore(create);
SimpleFeatureType type = SimpleFeatureTypeBuilder.retype(schema,webMercator);
dataStore.createSchema(type);
dataStore.forceSchemaCRS(CRS.decode("EPSG:3857")); // 明确CRS
// 获取数据名称
String createdName = dataStore.getTypeNames()[0];
// 创建重投影提交事务
Transaction transaction = new DefaultTransaction("Reproject");
try(FeatureWriter<SimpleFeatureType,SimpleFeature> writer = dataStore.getFeatureWriter(createdName,transaction)){
SimpleFeatureIterator iterator = featureCollection.features();
while (iterator.hasNext()) {
SimpleFeature feature = iterator.next();
SimpleFeature newFeature = writer.next();
// 写入目标属性
newFeature.setAttributes(feature.getAttributes());
// 写入重投影几何属性
Geometry geometry = (Geometry) feature.getDefaultGeometry();
Geometry newGeometry = JTS.transform(geometry, transform);
newFeature.setDefaultGeometry(newGeometry);
writer.write();
}
transaction.commit();
JOptionPane.showMessageDialog(null,"Export shapefile success ");
}catch(Exception e){
e.printStackTrace();
transaction.rollback();
JOptionPane.showMessageDialog(null,"Export shapefile failed ");
}finally{
transaction.close();
}
}
在复制源数据属性时,需要先调用iterator.next()
之后才能调用writer.next()
,然后就可以复制属性对象和几何对象数据。
SimpleFeature feature = iterator.next();
SimpleFeature newFeature = writer.next();
// 写入目标属性
newFeature.setAttributes(feature.getAttributes());
// 写入重投影几何属性
Geometry geometry = (Geometry) feature.getDefaultGeometry();
Geometry newGeometry = JTS.transform(geometry, transform);
newFeature.setDefaultGeometry(newGeometry);
writer.write();
注:使用此方式重投影Shapefile
数据时,.prj
文件已经生成,而且坐标也显示正确,但是在GIS
软件ArcMap
中打开时会提示缺少坐标参考信息。
属性表中确实是未能正确识别:
效果图片
❝
OpenLayers示例数据下载,请在公众号后台回复:ol数据
全国信息化工程师-GIS 应用水平考试资料,请在公众号后台回复:GIS考试
❝
GIS之路公众号已经接入了智能助手,欢迎大家前来提问。
欢迎访问我的博客网站-长谈GIS:
http://shanhaitalk.com
都看到这了,不要忘记点赞、收藏+关注 哦!
本号不定时更新有关 GIS开发 相关内容,欢迎关注