Geotools库怎么用?Java读写Shapefile?
很多 Java GIS 项目都会遇到一个基础问题:Geotools库怎么用?Java读写Shapefile? 如果你正在做桌面 GIS、数据质检、空间数据入库或批量格式转换,GeoTools 读取和写入 Shapefile 是最常见的入门场景之一。本文以一个可复现的 Java 示例,讲清楚 GeoTools 依赖配置、读取属性、读取几何、创建 Shapefile、写入要素以及常见编码和坐标系问题。
引言:GeoTools库怎么用,先从 Java读写Shapefile 开始
GeoTools 是 Java 生态中常用的开源 GIS 工具库,可以处理矢量数据、坐标参考系、空间过滤、样式渲染和部分栅格数据。在实际工作中,很多人第一次接触 GeoTools,并不是从复杂的地图渲染开始,而是从一个简单需求开始:
- 用 Java 读取 Shapefile 的字段和几何;
- 批量检查 Shapefile 属性是否为空;
- 把数据库或 CSV 中的坐标写成 Shapefile;
- 读取 Shapefile 后转换坐标系或入库;
- 解决中文字段乱码、坐标系丢失、写出文件打不开等问题。
本文围绕 GeoTools库怎么用 这个问题,重点演示 Java读写Shapefile 的完整流程。你可以直接把示例改成自己的数据路径和字段结构。

背景:为什么 Java 读写 Shapefile 经常出问题
Shapefile 看起来只是一个文件,实际上它通常由多个文件共同组成。最常见的有:
- .shp:存储几何图形,例如点、线、面;
- .shx:几何索引文件;
- .dbf:属性表文件;
- .prj:坐标参考系文件;
- .cpg:字符编码说明文件,常用于中文编码识别。
因此,Java 读写 Shapefile 的问题通常不是“能不能读”,而是下面这些细节没有处理好:
- 只复制了 .shp,没有复制 .dbf、.shx、.prj;
- 中文字段或中文属性值乱码;
- 写出的 Shapefile 没有坐标系;
- 字段名超过 Shapefile 限制导致被截断;
- 几何类型不一致,例如面图层中写入了点;
- 没有正确关闭 FeatureIterator、DataStore 或事务。
GeoTools 可以帮我们处理大量底层细节,但仍然需要你理解 Shapefile 的文件结构、字段限制和坐标系机制。
原理:GeoTools 读写 Shapefile 的核心对象
使用 GeoTools 读写 Shapefile,通常会接触到以下几个核心对象。
| 对象 | 作用 | 常见用途 |
|---|---|---|
| DataStore | 数据源抽象 | 打开 Shapefile、PostGIS 等数据源 |
| SimpleFeatureSource | 只读要素源 | 读取 Shapefile 要素 |
| SimpleFeatureStore | 可写要素源 | 写入、追加、修改要素 |
| SimpleFeatureType | 图层结构定义 | 定义字段、几何类型、坐标系 |
| SimpleFeature | 单个空间要素 | 一条几何和属性记录 |
| Geometry | 几何对象 | 点、线、面、多面等空间图形 |
可以把它理解为:DataStore 打开数据,FeatureSource 读取数据,FeatureType 描述结构,Feature 表示每一条记录,Geometry 表示空间图形。
步骤:使用 Maven 配置 GeoTools 依赖
如果你使用 Maven 管理 Java 项目,可以先配置 GeoTools 相关依赖。GeoTools 的版本建议在项目中统一用变量管理,并以 Maven Central 或 OSGeo 仓库中可用版本为准。
<properties>
<geotools.version>请替换为项目实际使用的GeoTools版本</geotools.version>
</properties>
<repositories>
<repository>
<id>osgeo</id>
<name>OSGeo Release Repository</name>
<url>https://repo.osgeo.org/repository/release/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-main</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-referencing</artifactId>
<version>${geotools.version}</version>
</dependency>
</dependencies>
如果只是 Java读写Shapefile,核心依赖是 gt-shapefile。如果还要处理坐标参考系、坐标转换和空间过滤,通常还会用到 gt-referencing、gt-epsg-hsql 等模块。
步骤:Java 读取 Shapefile 属性和几何
下面的代码演示如何用 GeoTools 读取 Shapefile。示例会输出每个要素的属性和几何类型。
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import java.io.File;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
public class ReadShapefileDemo {
public static void main(String[] args) throws Exception {
File shpFile = new File("D:/gis/data/roads.shp");
Map<String, Object> params = new HashMap<>();
params.put("url", shpFile.toURI().toURL());
params.put("charset", Charset.forName("UTF-8"));
DataStore dataStore = DataStoreFinder.getDataStore(params);
if (dataStore == null) {
throw new RuntimeException("无法打开Shapefile,请检查文件路径和依赖配置。");
}
String typeName = dataStore.getTypeNames()[0];
SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName);
SimpleFeatureCollection collection = featureSource.getFeatures();
System.out.println("图层名称:" + typeName);
System.out.println("要素数量:" + collection.size());
System.out.println("坐标参考系:" + featureSource.getSchema().getCoordinateReferenceSystem());
try (SimpleFeatureIterator iterator = collection.features()) {
while (iterator.hasNext()) {
SimpleFeature feature = iterator.next();
System.out.println("Feature ID: " + feature.getID());
System.out.println("Geometry: " + feature.getDefaultGeometry());
for (Property property : feature.getProperties()) {
System.out.println(property.getName() + " = " + property.getValue());
}
System.out.println("----------------------");
}
} finally {
dataStore.dispose();
}
}
}
这段代码里有几个关键点:
- url 参数指向 .shp 文件;
- charset 参数用于指定属性表编码,中文数据常见为 UTF-8、GBK 或 GB2312;
- SimpleFeatureSource 适合读取,不适合写入;
- SimpleFeatureIterator 必须关闭,推荐使用 try-with-resources;
- dataStore.dispose() 用于释放文件句柄,避免 Windows 下文件被占用。
步骤:Java 写入 Shapefile 文件
写入 Shapefile 的核心流程是:先定义图层结构,再创建文件,再构造要素,最后通过事务写入。下面示例创建一个点图层,字段包括名称和编号。
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
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;
import java.io.File;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
public class WriteShapefileDemo {
public static void main(String[] args) throws Exception {
File newFile = new File("D:/gis/output/sample_points.shp");
SimpleFeatureType featureType = DataUtilities.createType(
"sample_points",
"the_geom:Point:srid=4326,name:String,id:Integer"
);
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
Map<String, Serializable> params = new HashMap<>();
params.put("url", newFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStore dataStore =
(ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
dataStore.createSchema(featureType);
dataStore.setCharset(Charset.forName("UTF-8"));
GeometryFactory geometryFactory = new GeometryFactory();
DefaultFeatureCollection featureCollection = new DefaultFeatureCollection();
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
Point point1 = geometryFactory.createPoint(new Coordinate(116.391, 39.907));
featureBuilder.add(point1);
featureBuilder.add("北京示例点");
featureBuilder.add(1);
SimpleFeature feature1 = featureBuilder.buildFeature(null);
featureCollection.add(feature1);
Point point2 = geometryFactory.createPoint(new Coordinate(121.473, 31.230));
featureBuilder.add(point2);
featureBuilder.add("上海示例点");
featureBuilder.add(2);
SimpleFeature feature2 = featureBuilder.buildFeature(null);
featureCollection.add(feature2);
String typeName = dataStore.getTypeNames()[0];
Transaction transaction = new DefaultTransaction("create");
SimpleFeatureStore featureStore =
(SimpleFeatureStore) dataStore.getFeatureSource(typeName);
featureStore.setTransaction(transaction);
try {
featureStore.addFeatures(featureCollection);
transaction.commit();
System.out.println("Shapefile写入完成:" + newFile.getAbsolutePath());
} catch (Exception e) {
transaction.rollback();
throw e;
} finally {
transaction.close();
dataStore.dispose();
}
}
}
运行完成后,输出目录中应该至少生成 .shp、.shx、.dbf、.prj 等文件。如果只看到一个 .shp 文件,通常说明写入流程没有正常完成,或者程序在事务提交前异常退出。
步骤:检查写出的 Shapefile 是否正确
写出文件后,不建议只看“有没有生成文件”。建议按下面方式检查:
- 用 QGIS 或 ArcGIS Pro 打开输出的 Shapefile;
- 检查图层是否能正常加载;
- 打开属性表,确认字段名、字段类型和中文属性是否正常;
- 查看图层属性中的坐标参考系是否为 EPSG:4326;
- 缩放到图层,确认点位是否落在预期位置;
- 检查输出目录中是否存在 .prj 和 .cpg 文件。
如果点位出现在非预期位置,不一定是 GeoTools 写错了,也可能是经纬度顺序、坐标系定义或数据源坐标本身有问题。
常见坑:GeoTools 读取 Shapefile 中文乱码
Java读写Shapefile 最常见的问题之一是中文乱码。原因通常在于 .dbf 文件本身没有统一的编码标准,历史数据可能使用 GBK、GB2312、UTF-8 或其他编码。
读取时可以通过参数指定编码:
params.put("charset", Charset.forName("GBK"));
写入时可以设置 ShapefileDataStore 的编码:
dataStore.setCharset(Charset.forName("UTF-8"));
如果你不确定原始数据编码,可以尝试以下排查顺序:
- 先查看同目录下是否有 .cpg 文件;
- 用 QGIS 打开,手动尝试不同编码;
- 确认数据来源系统导出时的编码设置;
- 读取时分别尝试 UTF-8、GBK、GB2312;
- 统一项目输出编码,避免不同环节混用编码。
常见坑:Shapefile 字段名被截断
Shapefile 的属性表基于 dBASE 格式,字段名长度有限。很多情况下,字段名超过限制会被截断。例如 administrative_code 可能在输出后变成较短的字段名。
建议在写出 Shapefile 前主动设计字段名:
- 字段名尽量使用英文、数字和下划线;
- 字段名保持简短,例如 name、code、type、area;
- 不要依赖很长的中文字段名;
- 如果需要完整字段说明,可以另存一份字段字典表。
如果你的项目需要长字段名、复杂字段类型和更稳定的编码,建议考虑 GeoPackage、PostGIS 或 File Geodatabase,而不是继续强行使用 Shapefile。
常见坑:坐标系和经纬度顺序不一致
示例中使用的坐标为:
new Coordinate(116.391, 39.907)
这里的顺序是 x, y,对于 EPSG:4326 的常见 GIS 使用场景,通常对应 经度, 纬度。很多错误来自把纬度写在前面,经度写在后面。
如果你写出的点跑到海里、国外或地图外,优先检查:
- 坐标值是否为经度在前、纬度在后;
- 原始数据到底是 WGS84 经纬度还是投影坐标;
- 是否把米制坐标误当成经纬度写入 EPSG:4326;
- .prj 文件是否正确生成;
- 在 QGIS 中图层坐标系和项目坐标系是否被混淆。
常见坑:FeatureIterator 和 DataStore 没有关闭
在 Windows 环境下,如果你发现 Shapefile 写完后无法删除、无法覆盖,常见原因是文件句柄没有释放。GeoTools 读取 Shapefile 后,一定要关闭迭代器,并调用 DataStore 的 dispose 方法。
try (SimpleFeatureIterator iterator = collection.features()) {
while (iterator.hasNext()) {
SimpleFeature feature = iterator.next();
// 处理要素
}
} finally {
dataStore.dispose();
}
如果是写入操作,还要关闭事务:
transaction.close();
dataStore.dispose();
方法比较:GeoTools 读写 Shapefile 与其他方案怎么选
| 方法 | 适合场景 | 优点 | 限制 |
|---|---|---|---|
| GeoTools | Java 项目内直接读写 GIS 数据 | Java 集成方便,支持数据源多,适合工程化 | API 相对复杂,需要理解 Feature 和 CRS |
| GDAL/OGR | 命令行转换、批处理、跨格式转换 | 格式支持非常广,工具成熟 | Java 集成通常需要额外环境配置 |
| QGIS | 人工检查、少量数据编辑、可视化验证 | 直观,适合检查坐标系和属性 | 不适合纯后端自动化流程 |
| PostGIS | 空间数据入库、查询、服务端分析 | 适合大数据量、多用户和空间索引 | 不是文件读写工具,需要数据库环境 |
| GeoPackage | 替代 Shapefile 的现代文件格式 | 单文件、字段限制少、编码更稳定 | 部分老系统仍要求 Shapefile |
如果你的目标是 Java 后端系统中直接处理 Shapefile,GeoTools 是合适选择。如果只是一次性格式转换,GDAL 可能更简单。如果是长期存储和空间查询,建议进入 PostGIS。
检查清单:Java读写Shapefile 前后要确认什么
- 是否引入了 gt-shapefile 依赖;
- GeoTools 各模块版本是否一致;
- 输入路径是否指向 .shp 文件,而不是目录;
- .shp、.shx、.dbf 是否在同一目录;
- 中文乱码时是否指定了 charset;
- 写出字段名是否过长;
- 写出几何类型是否与 schema 定义一致;
- 是否设置了正确的坐标参考系;
- 坐标顺序是否为 x, y;
- FeatureIterator、Transaction、DataStore 是否关闭;
- 输出文件是否能在 QGIS 或 ArcGIS Pro 中正常打开;
- 输出目录是否生成 .prj 和 .cpg 文件。
FAQ:GeoTools库怎么用的常见问题
GeoTools 可以直接读取 shp 文件吗?
可以。GeoTools 通过 ShapefileDataStore 或 DataStoreFinder 读取 .shp 文件。实际读取时要确保 .shp、.shx、.dbf 等配套文件完整,否则可能出现无法打开、属性缺失或要素异常。
Java读写Shapefile 必须安装 QGIS 或 ArcGIS 吗?
不必须。GeoTools 是 Java 库,可以在不安装 QGIS 或 ArcGIS 的情况下读写 Shapefile。但建议安装 QGIS 用于结果检查,因为它可以直观看到几何位置、属性表、编码和坐标系是否正常。
GeoTools 读取 Shapefile 中文乱码怎么办?
优先检查 .cpg 文件。如果没有 .cpg,可以在读取参数中指定 charset,例如 UTF-8 或 GBK。不同数据来源的编码可能不同,不能假设所有 Shapefile 都是 UTF-8。
GeoTools 写出的 Shapefile 为什么没有坐标系?
通常是创建 SimpleFeatureType 时没有指定 CRS,或者没有正确生成 .prj 文件。可以在 schema 中设置 srid,例如 Point:srid=4326,也可以通过 CRS 对象构造更明确的 SimpleFeatureType。
GeoTools 能写面 Shapefile 吗?
可以。把 schema 中的几何类型从 Point 改为 Polygon 或 MultiPolygon,并构造对应的 JTS Geometry 对象即可。需要注意,一个 Shapefile 图层只能保持一种主几何类型,不建议在同一个图层中混写点、线、面。
Shapefile 适合存储大量数据吗?
Shapefile 可以存储一定规模的矢量数据,但它有字段名、编码、文件大小和复杂类型方面的限制。对于长期业务系统,建议优先考虑 PostGIS 或 GeoPackage。
结论:用 GeoTools 读写 Shapefile,关键在结构、编码和坐标系
GeoTools库怎么用 这个问题,最好的入门方式就是完成一次完整的 Java读写Shapefile 流程。读取时重点关注 DataStore、FeatureSource、FeatureIterator;写入时重点关注 FeatureType、FeatureCollection、FeatureStore 和 Transaction。
在真实项目中,GeoTools 读写 Shapefile 的难点通常不在 API 调用本身,而在 Shapefile 文件配套、中文编码、字段限制、坐标参考系和资源释放。只要把这些问题逐项检查清楚,Java 处理 Shapefile 就会稳定很多。
如果你只是做简单文件转换,可以考虑 GDAL;如果你要在 Java 服务中集成 GIS 数据处理,GeoTools 更适合;如果数据需要长期管理和高效空间查询,则建议进一步学习 PostGIS。