首页 GIS基础理论 Geotools库怎么用?Java读写Shapefile?

Geotools库怎么用?Java读写Shapefile?

作者: GIS研习社 更新时间:2025-12-05 07:00:03 分类:GIS基础理论

Java搞GIS别再手写解析了!Geotools读写Shapefile避坑指南

你是不是也遇到过这种情况:老板让你用Java处理一批Shapefile,结果你吭哧吭哧写了三天文件解析器,最后发现坐标系全乱了?或者程序跑着跑着就报NullPointerException,调试到凌晨三点还找不到原因?别慌——这不是你代码水平不行,而是你还没认识Geotools这位Java GIS界的“瑞士军刀”。

Geotools库怎么用?Java读写Shapefile?

我在参与某省级国土空间规划平台开发时,团队最初试图自己解析.shp文件,结果两个月后连投影转换都没搞定。引入Geotools后,三天重构完毕,稳定运行至今——这就是专业工具的力量。

Geotools到底是什么?为什么非它不可?

简单说,Geotools是一个开源的Java GIS工具包,专治各种“地理数据不服”。它不仅能读写Shapefile、GeoTIFF、PostGIS等主流格式,还能做坐标转换、空间分析、地图渲染——相当于把ArcGIS的核心功能拆成Java模块给你用。

类比一下:如果你把Shapefile想象成一本用外星文写的菜谱,那Geotools就是你的AI翻译+智能厨具套装。你不需要懂外星文语法(.shp二进制结构),也不用手动生火切菜(字节流解析),告诉它“我要读这个文件”,它就把热腾腾的FeatureCollection端到你面前。

五分钟实战:从零读取一个Shapefile

废话不多说,上代码。以下是最简可运行示例(假设你已配置好Maven依赖):

import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.feature.FeatureIterator;
import org.opengis.feature.simple.SimpleFeature;

import java.io.File;

public class ShapefileReader {
    public static void main(String[] args) throws Exception {
        // 1. 指定Shapefile路径(注意:必须包含.shp/.shx/.dbf三件套)
        File file = new File("path/to/your/file.shp");
        FileDataStore store = FileDataStoreFinder.getDataStore(file);
        
        // 2. 获取要素源
        SimpleFeatureSource featureSource = store.getFeatureSource();
        
        // 3. 遍历所有要素
        try (FeatureIterator<SimpleFeature> features = featureSource.getFeatures().features()) {
            while (features.hasNext()) {
                SimpleFeature feature = features.next();
                System.out.println("要素ID: " + feature.getID());
                System.out.println("属性值: " + feature.getAttribute("NAME")); // 替换为你的字段名
                System.out.println("几何类型: " + feature.getDefaultGeometry().getClass().getSimpleName());
            }
        }
        
        store.dispose(); // 重要!释放资源
    }
}

关键细节提醒:

  • 路径中的文件必须是完整的Shapefile家族(.shp+.shx+.dbf,缺一不可)
  • store.dispose() 必须调用,否则文件句柄会泄漏——我见过有人因此把服务器磁盘占满
  • 属性字段名区分大小写,建议先用feature.getType().getAttributeDescriptors()打印所有字段

写入Shapefile?三步教你造数据

读取只是热身,写入才是真功夫。下面这段代码会创建一个包含点要素的新Shapefile:

import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.Transaction;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
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 ShapefileWriter {
    public static void main(String[] args) throws Exception {
        // 1. 定义要素类型(相当于建表结构)
        SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
        typeBuilder.setName("MyPoints");
        typeBuilder.add("the_geom", Point.class); // 几何字段
        typeBuilder.add("name", String.class);   // 属性字段
        typeBuilder.add("value", Double.class);
        SimpleFeatureType TYPE = typeBuilder.buildFeatureType();
        
        // 2. 创建数据存储
        File newFile = new File("output.shp");
        Map<String, Serializable> params = new HashMap<>();
        params.put("url", newFile.toURI().toURL());
        params.put("create spatial index", Boolean.TRUE);
        ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
        DataStore dataStore = factory.createNewDataStore(params);
        
        // 设置编码避免中文乱码
        ((org.geotools.data.shapefile.ShapefileDataStore) dataStore).setCharset(Charset.forName("UTF-8"));
        
        dataStore.createSchema(TYPE);
        
        // 3. 写入要素
        Transaction transaction = new DefaultTransaction("create");
        String typeName = dataStore.getTypeNames()[0];
        org.geotools.data.FeatureWriter<SimpleFeatureType, SimpleFeature> writer = 
            dataStore.getFeatureWriterAppend(typeName, transaction);
        
        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
        
        try {
            // 创建第一个点
            SimpleFeature feature = writer.next();
            Point point = geometryFactory.createPoint(new Coordinate(116.4, 39.9)); // 北京坐标
            feature.setAttribute("the_geom", point);
            feature.setAttribute("name", "北京市中心");
            feature.setAttribute("value", 100.0);
            writer.write();
            
            // 提交事务
            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
            throw e;
        } finally {
            writer.close();
            transaction.close();
            dataStore.dispose();
        }
        
        System.out.println("Shapefile创建成功!");
    }
}

血泪经验:

  • 中文乱码?在ShapefileDataStore上调用setCharset(Charset.forName("UTF-8"))
  • 坐标顺序很重要!JTS默认是(经度, 纬度)(X,Y),和某些系统相反
  • 务必使用Transaction管理写入——这是Geotools的“安全气囊”

避坑锦囊:三个高频报错解决方案

错误信息原因解决方案
No datastore available for url文件路径错误或缺少.shx/.dbf检查文件完整性,使用绝对路径
Unsupported charset: UTF-8未设置编码强制设置setCharset(Charset.forName("UTF-8"))
NullPointerException on getFeatureSource()文件被其他程序占用确保关闭QGIS/ArcMap等软件

总结:让Geotools成为你的生产力杠杆

掌握Geotools读写Shapefile,本质是学会“站在巨人肩膀上偷懒”。与其重复造轮子,不如把精力放在真正的业务逻辑上——比如空间分析算法优化、大数据量性能调优,或是给老板画个漂亮的数据看板。

记住这三个核心要点:

  1. 读取用FileDataStoreFinder + FeatureIterator
  2. 写入用ShapefileDataStoreFactory + FeatureWriter
  3. 永远记得dispose()和事务管理

现在轮到你了:你在用Geotools时踩过什么坑?或者有什么骚操作想分享?评论区留下你的故事——说不定下期教程就为你定制!

相关文章