Java+GDAL GeoJSON数据读取入库精选指南

2026-06-22阅读 0热度 0
json

在面向空间的矢量数据处理中,Shapefile确实是常年霸榜的“老大哥”。但如果你只盯着它,可能会错过不少好东西——比如在WebGIS世界里非常活跃的GeoJSON。今天就来聊聊这个基于JSON的地理信息数据交换格式,看看它到底怎么用,以及如何通过Ja va和GDAL把它高效地塞进空间数据库里。

先简单介绍一下GeoJSON:它本质上就是JSON,只不过加了一套专门描述地理对象的规矩。可以表示点、线、面,还有它们的集合,比如多点、多线、多面,甚至几何集合。每个地理对象层层嵌套,结构清晰,浏览器原生支持,所以在前端GIS应用里特别吃香。

GeoJSON格式总是由一个顶层对象组成,这个对象必须有 type 成员,取值可以是 PointLineStringPolygonFeatureFeatureCollection 等。还可以带可选的 crs(坐标参考系统)和 bbox(边界框)。看个例子就明白了:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [102.0, 0.5]
      },
      "properties": {
        "prop0": "value0"
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [102.0, 0.0],
          [103.0, 1.0],
          [104.0, 0.0],
          [105.0, 1.0]
        ]
      },
      "properties": {
        "prop0": "value0",
        "prop1": 0.0
      }
    },
    {
      "type": "Feature",
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [100.0, 0.0],
            [101.0, 0.0],
            [101.0, 1.0],
            [100.0, 1.0],
            [100.0, 0.0]
          ]
        ]
      },
      "properties": {
        "prop0": "value0",
        "prop1": {
          "this": "that"
        }
      }
    }
  ]
}

接下来,我们就从实际数据出发,走一遍完整的流程:用QGIS了解数据长什么样,在PostGIS里设计空间表,再用Ja va+GDAL把GeoJSON批量写进数据库。

一、基础数据介绍

示例数据是2015年左右的全国分级地名数据,包含省会城市、地级市、县级市、区县四类,全部以GeoJSON格式存储。原始数据来自GISer last,大家有兴趣的话可以自行搜索获取。

1. 数据格式

地名数据主要是点数据,除了乡镇一级数据量稍大,其他级别的文件都不大,远没到MB级别。先用QGIS把它们打开看看效果。

加载进QGIS后,可以查看空间属性,比如投影信息、属性字段等。为了展示得更清晰,我叠加了省级行政区划矢量数据,并打开了标注,效果如下:

省会地名数据示意图

地级市地名示意图

县级市地名示意图

2. 数据参数

在QGIS里查看数据的属性信息,得到以下参数:

序号参数说明
1存储类型GeoJSON
2编码UTF-8
3几何图形Point
4坐标参考系EPSG:4326 - WGS 84 - 地理的
5范围87.6149638000000550,20.0500348290000261 : 126.5286520170000131,45.8019539320000604
6单位

属性字段如下:

序号属性名数据类型说明
1NAMEString地名
2PIINYINString地名拼音
3CLASSString类型
4BZString备注
5SLXString标记(暂无特殊含义)

各层级数据条数:

序号数据名称数据条数
1省会城市地名29
2地级市地名283
3县级市地名339
4区县地名2567

了解了数据结构和属性后,接下来就可以设计空间数据库表了。

二、空间数据库设计

我们选用PostGIS作为空间数据库。设计思路很简单:建立一张空间表,字段直接映射GeoJSON中的属性,同时增加主键和空间几何字段 geom,并在 geom 上建立GIST空间索引——这一步至关重要,没有索引的空间查询慢得让人抓狂。

1. 主体表模型

实体映射关系如下:

2. DDL语句与空间索引

先建表,再建索引。注意空间索引必须用GIST,普通BTREE对几何列无效。

CREATE TABLE "public"."biz_geographic_name" (
  "pk_id" int8 NOT NULL,
  "name" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
  "pinyin" varchar(255) COLLATE "pg_catalog"."default",
  "classz" varchar(4) COLLATE "pg_catalog"."default",
  "bz" varchar(100) COLLATE "pg_catalog"."default",
  "slx" varchar(20) COLLATE "pg_catalog"."default",
  "geom" "public"."geometry" NOT NULL,
  CONSTRAINT "pk_biz_geographic_name" PRIMARY KEY ("pk_id")
);
ALTER TABLE "public"."biz_geographic_name" OWNER TO "ghy01";
CREATE INDEX "idex_biz_geographic_name_classz" ON "public"."biz_geographic_name" USING btree ("classz" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST);
CREATE INDEX "idx_biz_geographic_name_geom" ON "public"."biz_geographic_name" USING gist ("geom" "public"."gist_geometry_ops_2d");
COMMENT ON COLUMN "public"."biz_geographic_name"."pk_id" IS '主键id';
COMMENT ON COLUMN "public"."biz_geographic_name"."name" IS '地名';
COMMENT ON COLUMN "public"."biz_geographic_name"."pinyin" IS '汉语拼音';
COMMENT ON COLUMN "public"."biz_geographic_name"."classz" IS 'classz';
COMMENT ON COLUMN "public"."biz_geographic_name"."bz" IS '备注';
COMMENT ON COLUMN "public"."biz_geographic_name"."slx" IS 'slx';
COMMENT ON COLUMN "public"."biz_geographic_name"."geom" IS '空间对象';
COMMENT ON TABLE "public"."biz_geographic_name" IS '地名基础信息表,用于存储中国范围内的地名信息';

至此空间表设计完成,索引也都建好了。

三、GDAL解析及入库

核心环节来了:怎么用Ja va+GDAL把GeoJSON数据读出来,再通过MyBatis-Plus写入PostGIS?

1. GDAL中GeoJSON驱动

GDAL对不同数据格式使用不同的驱动。解析GeoJSON就用 GeoJSON 驱动。它支持读写GeoJSON格式,并且提供了一些配置选项,比如是否展平嵌套属性(FLATTEN_NESTED_ATTRIBUTES)、是否把数组当字符串处理(ARRAY_AS_STRING)等。实际开发中按需设置就行。

2. 解析GeoJSON

关键代码如下:

@Test
public void readDjsGeoJSON() {
    String strVectorFile = "path/2015省市区县乡镇地名数据/地名点_地级市.geojson";
    ogr.RegisterAll();
    gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
    gdal.SetConfigOption("SHAPE_ENCODING", "UTF-8");
    String strDriverName = "GeoJSON";
    org.gdal.ogr.Driver oDriver = ogr.GetDriverByName(strDriverName);
    if (oDriver == null) {
        System.out.println(strDriverName + " 驱动不可用!");
        return;
    }
    DataSource dataSource = oDriver.Open(strVectorFile);
    Layer layer = dataSource.GetLayer(0);
    SpatialReference spatialReference = layer.GetSpatialRef();
    String srid = spatialReference.GetAttrValue("AUTHORITY", 1);
    long featureCount = layer.GetFeatureCount();
    List list = new ArrayList();
    for (int i = 0; i < featureCount; i++) {
        Feature feature = layer.GetFeature(i);
        String name = feature.GetFieldAsString("NAME");
        String pinyin = feature.GetFieldAsString("PINYIN");
        String classz = feature.GetFieldAsString("CLASS");
        String bz = feature.GetFieldAsString("BZ");
        String slx = feature.GetFieldAsString("SLX");
        Geometry geom = feature.GetGeometryRef();
        String wkt = geom.ExportToWkt();
        wkt = "SRID=" + srid + ";" + wkt;
        list.add(new GeographicName(name, pinyin, classz, bz, slx, wkt));
        System.out.println("name=" + name + " classz=" + classz + " wkt=" + wkt);
    }
    geographicNameService.sa veBatch(list, 300);
    dataSource.delete();
    gdal.GDALDestroyDriverManager();
}

步骤清晰:打开文件 → 获取图层 → 逐条读取属性字段和几何对象 → 构造WKT(带上SRID) → 批量插入。

3. Ja va模型

实体类(省略Mapper和Service):

package com.yelang.project.extend.earthquake.domain;

import ja va.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.yelang.framework.handler.PgGeometryTypeHandler;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "biz_geographic_name", autoResultMap = true)
public class GeographicName implements Serializable {
    private static final long serialVersionUID = -3694849578429480952L;
    @TableId(value = "pk_id")
    private Long pkId;
    private String name;
    private String pinyin;
    private String classz;
    private String bz;
    private String slx;
    
    public GeographicName(String name, String pinyin, String classz, String bz, String slx, String geom) {
        super();
        this.name = name;
        this.pinyin = pinyin;
        this.classz = classz;
        this.bz = bz;
        this.slx = slx;
        this.geom = geom;
    }
    
    @TableField(typeHandler = PgGeometryTypeHandler.class)
    private String geom;
    
    @TableField(exist = false)
    private String geomJson;
}

这里用到了自定义类型处理器 PgGeometryTypeHandler,负责把WKT字符串转换成PostGIS的geometry对象。

4. 数据入库

用JUnit跑一下测试,数据批量插入到数据库中。

控制台输出显示正在执行插入操作:

进入数据库验证一下:

select * from biz_geographic_name;

用Na vicat连接数据库,可以看到地级市的地名数据已经成功写入空间表中。

总结一下,整个流程并不复杂,关键是理解GeoJSON的结构、GDAL驱动的工作原理,以及PostGIS空间索引的重要性。把这几步串联起来,以后遇到类似的数据入库需求,就可以照方抓药了。

免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策