GeoTools 入门实战(一):Shapefile 读取与写入全解析
目录一、前言二、环境准备三、GeoTools 核心概念四、读取 Shapefile五、创建新 Shapefile六、完整可运行代码七、常见坑位与注意事项八、工程实践建议九、小结一、前言GeoTools 是 Java 生态中最重要的开源 GIS 库它基于 JTS 提供了完整的空间数据读写能力。其中 Shapefile 作为 GIS 行业最通用的数据格式是每个 GIS 开发者都会接触的第一个格式。本文将从工程视角出发系统讲解 GeoTools 读取和写入 Shapefile 的全过程包含 DataStore 体系、Feature 模型、中文编码处理以及 GeoTools 29.x 版本的 API。二、环境准备Maven 依赖本文使用 GeoTools 29.3 JTS 1.19.0依赖配置如下dependenciesdependencygroupIdorg.geotools/groupIdartifactIdgt-shapefile/artifactIdversion29.3/version/dependencydependencygroupIdorg.geotools/groupIdartifactIdgt-main/artifactIdversion29.3/version/dependencydependencygroupIdorg.geotools/groupIdartifactIdgt-epsg-hsql/artifactIdversion29.3/version/dependencydependencygroupIdorg.locationtech.jts/groupIdartifactIdjts-core/artifactIdversion1.19.0/version/dependency/dependencies三、GeoTools 核心概念在开始写代码之前先理解 GeoTools 的核心概念这对后续使用其他数据格式GeoJSON、PostGIS 等也非常有用。DataStore —— 数据存储抽象GeoTools 不直接操作文件而是通过 DataStore 进行统一管理。它是所有空间数据源的抽象层支持文件、数据库、Web 服务等多种形式。FeatureSource / FeatureCollection / SimpleFeature这三个接口是 GeoTools 的核心数据模型接口职责FeatureSource数据源的只读视图用于查询和获取元数据FeatureCollection元素集合支持遍历和过滤SimpleFeature单个元素包含几何字段和属性字段SimpleFeatureType —— 属性结构定义SimpleFeatureType 定义了一个图层的完整结构包含几何字段名称和类型如 the_geom: Point属性字段名称和类型如 name: String坐标系信息CRS四、读取 Shapefile读取 Shapefile 是 GeoTools 最基本的操作流程如下通过 DataStoreFinder 创建 DataStore 实例获取图层名称typeName通过 FeatureSource 获取元素集合使用 FeatureIterator 遍历元素读取代码示例StringinputShpyour_path.shp;MapString,SerializableparamsnewHashMap();params.put(url,newFile(inputShp).toURI().toURL());params.put(charset,StandardCharsets.UTF_8.name());DataStoreinputStoreDataStoreFinder.getDataStore(params);if(inputStorenull){thrownewRuntimeException(无法识别的 Shapefile 格式);}StringtypeNameinputStore.getTypeNames()[0];FeatureSourceSimpleFeatureType,SimpleFeaturesourceinputStore.getFeatureSource(typeName);FeatureCollectionSimpleFeatureType,SimpleFeaturecollectionsource.getFeatures();System.out.println(要素数量: collection.size());System.out.println(属性结构: source.getSchema());遍历元素try(FeatureIteratorSimpleFeatureitcollection.features()){intcount0;while(it.hasNext()count3){SimpleFeaturefeatureit.next();System.out.println(ID: feature.getID());System.out.println(Geometry: feature.getDefaultGeometry());for(Objectattr:feature.getAttributes()){System.out.println(Attribute: attr);}count;}}// 关闭 DataStoreinputStore.dispose();关键点说明DataStoreFinder 会自动识别数据源类型Shapefile 对应 ShapefileDataStorecharset 参数解决中文属性乱码问题FeatureIterator 必须在 try-with-resources 中使用避免内存泄漏dispose() 释放资源生产环境一定不能遗漏五、创建新 Shapefile写入 Shapefile 比读取复杂一些因为需要先定义属性结构Schema再逐个写入元素。GeoTools 29.x 写入步骤创建 ShapefileDataStore 并设置编码使用 SimpleFeatureTypeBuilder 定义 Schema调用 createSchema() 创建层使用 FeatureWriter 写入元素完整写入代码privatestaticvoidcreateShapefile()throwsException{FilefilenewFile(output.shp);MapString,SerializableparamsnewHashMap();params.put(ShapefileDataStoreFactory.URLP.key,file.toURI().toURL());params.put(ShapefileDataStoreFactory.CREATE_SPATIAL_INDEX.key,Boolean.TRUE);ShapefileDataStoreFactoryfactorynewShapefileDataStoreFactory();ShapefileDataStoreds(ShapefileDataStore)factory.createNewDataStore(params);ds.setCharset(StandardCharsets.UTF_8);// 1. 构建 SchemaSimpleFeatureTypeBuildertypeBuildernewSimpleFeatureTypeBuilder();typeBuilder.setName(test_layer);typeBuilder.setCRS(CRS.decode(EPSG:4326));typeBuilder.add(the_geom,Point.class);typeBuilder.add(name,String.class);typeBuilder.add(value,Double.class);SimpleFeatureTypeschematypeBuilder.buildFeatureType();ds.createSchema(schema);// 2. 写入要素GeometryFactorygfnewGeometryFactory();String[]names{点A,点B,点C};double[][]coords{{116.407,39.904},{116.415,39.912},{116.423,39.908}};try(FeatureWriterSimpleFeatureType,SimpleFeaturewriterds.getFeatureWriterAppend(ds.getTypeNames()[0],Transaction.AUTO_COMMIT)){for(inti0;inames.length;i){SimpleFeaturefeaturewriter.next();feature.setDefaultGeometry(gf.createPoint(newCoordinate(coords[i][0],coords[i][1])));feature.setAttribute(name,names[i]);feature.setAttribute(value,100.0i*10);writer.write();}}ds.dispose();}GeoTools 29.x 写入核心三步曲这是 GeoTools 29.x 中创建 Shapefile 的写法SimpleFeaturefeaturewriter.next();// 获取占位 FeatureFID 在此生成feature.setAttribute(...);// 填充字段writer.write();// 写入这三步是严格顺序的任何变式都会导致异常。六、完整可运行代码下面是读取 写入的完整代码可直接复制到 IDE 中运行importorg.geotools.data.*;importorg.geotools.data.shapefile.ShapefileDataStore;importorg.geotools.data.shapefile.ShapefileDataStoreFactory;importorg.geotools.feature.FeatureCollection;importorg.geotools.feature.FeatureIterator;importorg.geotools.feature.simple.SimpleFeatureTypeBuilder;importorg.locationtech.jts.geom.Point;importorg.locationtech.jts.geom.GeometryFactory;importorg.opengis.feature.simple.SimpleFeature;importorg.opengis.feature.simple.SimpleFeatureType;importjava.io.File;importjava.io.IOException;importjava.io.Serializable;importjava.nio.charset.StandardCharsets;importjava.util.HashMap;importjava.util.Map;publicclassShapefileReadWriteDemo{publicstaticvoidmain(String[]args)throwsException{StringinputShpD:/testdata/testshp/ne_110m_admin_0_tiny_countries/ne_110m_admin_0_tiny_countries.shp;// 替换为你的 shp 路径readShapefile(inputShp);// // 第二部分创建新的 Shapefile// createShapefile();System.out.println(\n新 Shapefile 已生成output.shp);}privatestaticvoidreadShapefile(StringinputShp)throwsIOException{// // 第一部分读取 Shapefile// MapString,SerializableparamsnewHashMap();params.put(url,newFile(inputShp).toURI().toURL());params.put(charset,StandardCharsets.UTF_8.name());DataStoreinputStoreDataStoreFinder.getDataStore(params);if(inputStorenull){thrownewRuntimeException(无法识别的 Shapefile 格式);}StringtypeNameinputStore.getTypeNames()[0];FeatureSourceSimpleFeatureType,SimpleFeaturesourceinputStore.getFeatureSource(typeName);FeatureCollectionSimpleFeatureType,SimpleFeaturecollectionsource.getFeatures();System.out.println( Shapefile 读取结果 );System.out.println(要素数量: collection.size());System.out.println(属性结构: source.getSchema());try(FeatureIteratorSimpleFeatureitcollection.features()){intcount0;while(it.hasNext()count3){SimpleFeaturefeatureit.next();System.out.println(\nFeature #(count1));System.out.println( ID: feature.getID());System.out.println( Geometry: feature.getDefaultGeometry());for(Objectattr:feature.getAttributes()){System.out.println( Attribute: attr);}count;}}inputStore.dispose();}privatestaticvoidcreateShapefile()throwsException{FilefilenewFile(output_fixed.shp);MapString,SerializableparamsnewHashMap();params.put(ShapefileDataStoreFactory.URLP.key,file.toURI().toURL());params.put(ShapefileDataStoreFactory.CREATE_SPATIAL_INDEX.key,Boolean.TRUE);ShapefileDataStoreFactoryfactorynewShapefileDataStoreFactory();ShapefileDataStoreds(ShapefileDataStore)factory.createNewDataStore(params);ds.setCharset(StandardCharsets.UTF_8);// 构建 SchemaSimpleFeatureTypeBuildertypeBuildernewSimpleFeatureTypeBuilder();typeBuilder.setName(test_layer);typeBuilder.setCRS(org.geotools.referencing.CRS.decode(EPSG:4326));typeBuilder.add(the_geom,Point.class);// Shapefile 几何字段推荐 the_geomtypeBuilder.add(name,String.class);typeBuilder.add(value,Double.class);SimpleFeatureTypeschematypeBuilder.buildFeatureType();ds.createSchema(schema);// 写入要素GeometryFactorygfnewGeometryFactory();String[]names{点A,点B,点C};double[][]coords{{116.407,39.904},{116.415,39.912},{116.423,39.908}};try(FeatureWriterSimpleFeatureType,SimpleFeaturewriterds.getFeatureWriterAppend(ds.getTypeNames()[0],Transaction.AUTO_COMMIT)){for(inti0;inames.length;i){// next() 返回当前可编辑 FeatureFID 在此生成SimpleFeaturefeaturewriter.next();feature.setDefaultGeometry(gf.createPoint(neworg.locationtech.jts.geom.Coordinate(coords[i][0],coords[i][1])));feature.setAttribute(name,names[i]);feature.setAttribute(value,100.0i*10);writer.write();}}ds.dispose();}}创建出来的shapefile在QGIS中打开效果如下七、常见坑位与注意事项1. 中文乱码问题Shapefile 的 DBF 文件默认使用 ISO-8859-1 编码中文属性会乱码。解决方法读取时params.put(charset, StandardCharsets.UTF_8.name())写入时ds.setCharset(StandardCharsets.UTF_8)建议同时生成 .cpg 文件声明编码2. GeoTools 28.x → 29.x 破坏性变更FeatureWriter.write() 方法在 29.x 中变为无参这是最常见的升级坑版本write 方法写法28.xwrite(feature)writer.write(builder.buildFeature(null))29.xwrite()feature writer.next(); writer.write();3. 几何字段名称必须为 the_geomShapefile 规范中几何字段名称建议使用 the_geom否则可能导致 QGIS / ArcGIS 无法正确识别几何字段。4. FID 索引异常提示 Current fid index is null 时说明 writer.next() 没有被调用就直接 write() 了。记住固定模式SimpleFeaturefeaturewriter.next();// 不能省略feature.setAttribute(...);writer.write();5. 资源泄露风险DataStore 和 FeatureIterator 都必须显式关闭DataStore.dispose() 释放文件锁和连接池FeatureIterator 用 try-with-resources 自动关闭长期不关闭会导致文件被锁定无法删除或覆盖。八、工程实践建议读取前先判断 DataStore 是否为 null避免空指针异常生产环境建议将 Shapefile 路径配置化不要硬编码批量写入时使用 Transaction 控制事务出错时 rollback建议将属性名称和类型抽取为常量管理大文件处理时注意内存考虑分块读取写入完成后立即 dispose释放文件锁九、小结本文介绍了 GeoTools 读取和写入 Shapefile 的完整流程覆盖了以下核心知识点DataStore 体系与 ShapefileDataStore 的使用FeatureSource / FeatureCollection / SimpleFeature 的关系Schema 定义与属性结构构建GeoTools 29.x 的 FeatureWriter 无参 write() 写法中文编码解决方案常见异常与调试技巧

相关新闻

HiveWE:5个关键功能让魔兽争霸III地图创作变得轻松高效

HiveWE:5个关键功能让魔兽争霸III地图创作变得轻松高效

HiveWE:5个关键功能让魔兽争霸III地图创作变得轻松高效 【免费下载链接】HiveWE A Warcraft III world editor. 项目地址: https://gitcode.com/gh_mirrors/hi/HiveWE 你是否曾想过,制作一张精彩的魔兽争霸III地图可以像绘画一样直观?…

2026/7/6 0:53:28 阅读更多 →
LSTM 时间序列预测:从单步到多步(5步)预测的PyTorch实现与误差分析

LSTM 时间序列预测:从单步到多步(5步)预测的PyTorch实现与误差分析

LSTM时间序列预测:从单步到多步预测的PyTorch实战与误差演化分析当我们需要预测未来多个时间点的数据时,传统的单步预测方法就显得力不从心。本文将深入探讨如何改造标准LSTM模型,实现从t1到t5的多步预测,并系统分析预测步长增加对…

2026/7/6 0:51:28 阅读更多 →
TCN 时间卷积网络 PyTorch 实战:4层残差块构建时序预测模型(附完整代码)

TCN 时间卷积网络 PyTorch 实战:4层残差块构建时序预测模型(附完整代码)

TCN 时间卷积网络 PyTorch 实战:4层残差块构建时序预测模型时序数据预测一直是机器学习领域的重要课题。从股票价格到电力负荷,从气象数据到工业设备状态监测,准确预测未来趋势对决策制定至关重要。传统RNN和LSTM虽然广泛应用,但存…

2026/7/6 0:49:28 阅读更多 →

最新新闻

中小教培机构到底该怎么选管理系统?一个12年运营顾问掏心窝建议

中小教培机构到底该怎么选管理系统?一个12年运营顾问掏心窝建议

教培机构为什么总是管不好账、留不住人? 做了12年校区运营咨询,我见过太多中小机构死在"管理"两个字上。不是课上得不好,是排课冲突、续费提醒漏发、课时算不清、家长投诉没人接——这些琐碎的事,一点点把校长的精力吃…

2026/7/6 1:49:40 阅读更多 →
线结构光标定精度对比:棋盘格法 vs 平面法向量法,3种中心线提取算法实测

线结构光标定精度对比:棋盘格法 vs 平面法向量法,3种中心线提取算法实测

线结构光标定精度对比:棋盘格法 vs 平面法向量法,3种中心线提取算法实测在工业检测、逆向工程和机器人引导等领域,高精度三维测量技术发挥着关键作用。线结构光技术因其非接触、高效率和高精度的特点,成为三维测量的重要手段。然而…

2026/7/6 1:47:40 阅读更多 →
温州大学机器学习课程开源项目全解析:从环境搭建到算法实战的保姆级学习指南

温州大学机器学习课程开源项目全解析:从环境搭建到算法实战的保姆级学习指南

温州大学机器学习课程开源项目全解析:从环境搭建到算法实战的保姆级学习指南 在人工智能技术日新月异的今天,机器学习已成为计算机科学领域最热门的方向之一。对于初学者而言,面对浩如烟海的算法理论和复杂的数学推导,往往感到无从…

2026/7/6 1:45:39 阅读更多 →
Java设计模式——结构型

Java设计模式——结构型

设计模式:结构型模式结构型模式关注的是:类和对象之间如何组合,如何让系统结构更灵活、更容易扩展。 创建型模式解决“对象怎么创建”,结构型模式解决“对象怎么组装”。一、结构型模式总览结构型模式主要解决以下问题&#xff1a…

2026/7/6 1:45:39 阅读更多 →
震散机自动化厂家技术能力与设备可靠性分析

震散机自动化厂家技术能力与设备可靠性分析

在化肥、化工、食品等行业的物料处理环节中,原料因长期堆放产生的板结问题,一直是影响生产效率和产品质量的常见痛点。传统的处理方式多依赖人工敲袋或外部机械破碎,不仅劳动强度大、效率低,而且容易损坏包装袋和内衬膜&#xff0…

2026/7/6 1:43:39 阅读更多 →
事件通道:EventChannel实现原生向ArkTS推送数据(102)

事件通道:EventChannel实现原生向ArkTS推送数据(102)

一、 ArkTS 侧:创建通道并监听事件在 ArkTS 侧,首先需要创建一个 EventChannel 实例,并设置消息监听器。当原生层推送数据时,监听器会被触发。核心代码示例(ArkTS):import bridge from arkui-x.…

2026/7/6 1:41:38 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/5 0:03:34 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/5 0:03:34 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/5 0:07:38 阅读更多 →

月新闻