> 技术文档 > 基于Java、GeoTools与PostGIS的GeoJSON动态属性注入实战指南

基于Java、GeoTools与PostGIS的GeoJSON动态属性注入实战指南

目录

前言        

一、需求场景以及GeoJSON介绍

1、需求场景简介

2、GeoJSON基础知识

二、GeoJSON属性动态生成

1、纯Java生成

2、GeoTools生成

3、PostGIS静态生成

4、PostGIS动态生成

三、总结


前言        

        在当今数字化时代,地理信息系统(GIS)技术正以前所未有的速度融入我们的生活。从智能交通到城市规划,从环境监测到资源管理,地理信息数据的应用场景日益丰富。而 GeoJSON 作为一种轻量级、易于解析的地理信息数据格式,已经成为 Web 地理信息应用中不可或缺的组成部分。然而,随着业务需求的不断增长,传统的静态 GeoJSON 数据已经无法满足实时性、动态性的要求。如何实现 GeoJSON 数据的动态属性注入,成为了 GIS 开发者面临的重要挑战。

        传统的地理信息应用中,GeoJSON 数据通常是静态的,一旦生成就不会再改变。然而,随着业务需求的动态化和实时化,这种静态模式已经无法满足需求。例如,在智能交通系统中,道路的拥堵情况需要实时更新;在环境监测系统中,传感器数据需要实时反映到地图上。这就要求 GeoJSON 数据能够动态地注入新的属性,以反映最新的业务状态。实现 GeoJSON 动态属性注入面临着诸多挑战。首先,如何从数据库中高效地查询和提取数据是一个关键问题。PostGIS 提供了强大的空间查询功能,但如何优化查询以满足实时性要求是一个需要深入探讨的问题。其次,如何在 Java 中处理和转换这些数据也是一个挑战。GeoTools 提供了丰富的工具,但如何高效地使用这些工具来实现动态属性注入需要一定的技巧。最后,如何将动态生成的 GeoJSON 数据有效地传输到前端并进行渲染也是一个需要解决的问题。

        本文旨在为基于 Java、GeoTools 和 PostGIS 的 GeoJSON 动态属性注入提供一套完整的实战方案。希望通过分享我们在项目中积累的经验和教训,帮助开发者快速掌握这一关键技术,从而在地理信息应用开发中更加得心应手。

        本指南适用于以下几类读者:

  • Java 开发者:希望在地理信息应用中实现动态数据更新的 Java 开发者。

  • GIS 开发者:熟悉 GIS 技术,但希望了解如何在 Web 应用中实现动态数据更新的 GIS 开发者。

  • 数据工程师:负责地理信息数据的存储和管理,希望了解如何高效地将数据推送到前端的数据工程师。

  • 项目管理者:希望了解地理信息应用开发中动态数据更新的技术方案和实施要点的项目管理者。

一、需求场景以及GeoJSON介绍

        为了让大家对相关的需求场景和GeoJSON知识有所了解,这里我们将对这两方面的知识和内容进行简单讲解,更多详细的内容,尤其是关于GeoJSON的官方标准的,欢迎大家查阅官方文档。

1、需求场景简介

        在很多的GIS类的项目中,GeoJSON这种数据格式一定是大家在WebGIS等项目建设时的首选。在项目建设过程中,我们在进行空间数据的展示时,有可能为了演示简单就只将空间数据进行输出,而属性数据则进行了忽略,比如我们在查询一张地名空间信息表时,我们也许只返回了Point点数据,而忽略了名称、大小等属性信息;更甚者,在我们的业务开展过程中,我们需要动态链接一些外部数据,来自动添加属性,以我们常见的POI数据为例,需要动态的拼接外部系统的业务信息,从而返回一个信息非常丰富的超级实体。在此场景下就需要进行GeoJSON的动态动态设置及输出。

2、GeoJSON基础知识

        首先我们来介绍一个有关于GeoJSON的基本知识,上图其实就是一个简单的GeoJSON的信息结构和具体的信息数据组成图。通过上面的层次图我们可以掌握组织层次,而数据组成则展示了一个格式良好的JSON对象。GeoJSON是一个 JSON 对象,必须带 type 字段,用来描述几何、特征或特征集合,其余字段根据 type 的值按固定模式出现。

        1️⃣ 原子单位:坐标(Coordinate)

  • 一维:[x]

  • 二维:[lng, lat](最常见)

  • 三维:[lng, lat, z](可选高程)


        2️⃣ 几何对象(Geometry)

        必须字段

字段 含义 示例值 type 几何类型 \"Point\" coordinates 坐标或嵌套坐标数组 [120.0, 30.0] 或更高维数组

        常见 7 种类型:

  • Point / MultiPoint

  • LineString / MultiLineString

  • Polygon / MultiPolygon

  • GeometryCollection(可再嵌套任意几何)

        示例如下:

{ \"type\": \"Point\", \"coordinates\": [120.0, 30.0]}

        3️⃣ 特征对象(Feature)

字段 含义 type 固定值 \"Feature\" geometry 任一 Geometry 对象或 null properties 任意 JSON 对象(属性表)

        可选字段

  • id:字符串或数字,全局唯一标识

  • bbox[minX, minY, maxX, maxY] 空间范围

        示例:

{ \"type\": \"Feature\", \"id\": \"f1\", \"geometry\": { \"type\": \"Point\", \"coordinates\": [120, 30] }, \"properties\": { \"name\": \"杭州\", \"pop\": 1200 }}

        4️⃣ 特征集合(FeatureCollection)

        必须字段

字段 含义 type 固定值 \"FeatureCollection\" features Feature 对象数组

        可选字段

  • bbox:整体空间范围

  • 其他自定义顶层元数据(不在规范,但常见)

        示例:

{ \"type\": \"FeatureCollection\", \"bbox\": [110, 20, 130, 40], \"features\": [ { \"type\": \"Feature\", \"geometry\": {...}, \"properties\": {...} } ]}

        5️⃣ 总结口诀

  • 根对象必带 \"type\"

  • Geometry 看 \"coordinates\"

  • Feature 看 \"geometry\" + \"properties\"

  • FeatureCollection 看 \"features\" 数组

        以上就是关于需求场景展示和GeoJSON基础知识的主要内容,希望通过本节的讲解,大家对需求出现的场景以及GeoJSON的结构知识有一个简单的了解。

二、GeoJSON属性动态生成

        本节将通过代码实战的方式重点介绍GeoJSON中属性的动态生成实践,通过使用纯Java的方式、Geotools生成方式以及两种基于PostGIS的动静态生成方法,希望对大家有一定的帮助。本节示例使用的数据是地名数据,以湖南省怀化市新晃侗族自治县为例进行讲述。

1、纯Java生成

        在Java中,可以通过标准的JSON库(如Jackson或Gson)来构建GeoJSON对象。properties属性是一个键值对的集合,通常使用Map来表示。在纯Java的开发环境中,不需要关注空间信息,因此我们可以专注于数据的展示。使用纯java的生成步骤大约分成以下四步,第一步是设置元素类型,第二步是添加集合对象,第三步是添加属性,最后一步是数据输出。关键的Java代码如下所示:

package com.yelang.project.geotools.geojson;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.node.ObjectNode;import java.util.HashMap;import java.util.Map;public class GeoJsonExample {public static void main(String[] args) { ObjectMapper objectMapper = new ObjectMapper(); // 创建GeoJSON对象 ObjectNode geoJson = objectMapper.createObjectNode(); // 添加类型 geoJson.put(\"type\", \"Feature\"); // 添加几何对象 ObjectNode geometry = objectMapper.createObjectNode(); geometry.put(\"type\", \"Feature\"); geometry.putArray(\"coordinates\").add(109.171027134).add(27.354881948); geoJson.set(\"geometry\", geometry); // 添加properties属性 Map properties = new HashMap(); properties.put(\"name\", \"新晃侗族自治县\"); properties.put(\"pinyin\", \"Xinhuang Dongzu Zizhixian\"); properties.put(\"bz\", \"区县名\"); properties.put(\"type\", \"Point\"); geoJson.set(\"properties\", objectMapper.valueToTree(properties)); // 输出GeoJSON try {System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(geoJson)); } catch (JsonProcessingException e) { e.printStackTrace(); } }}

        完成后运行以下程序,可以在控制台中看到如下输出:

{ \"type\" : \"Feature\", \"geometry\" : { \"type\" : \"Feature\", \"coordinates\" : [ 109.171027134, 27.354881948 ] }, \"properties\" : { \"pinyin\" : \"Xinhuang Dongzu Zizhixian\", \"name\" : \"新晃侗族自治县\", \"bz\" : \"区县名\", \"type\" : \"Point\" }}

        看到以上数据说明环境配置正确,程序设置没有问题可以正常运行。

2、GeoTools生成

        除了在Java当中直接使用原生代码生成GeoJSON之后,我们同时可以利用一些便捷的库,如GeoTools进行项目支撑。GeoTools 是一个开源的 Java GIS 工具包,提供了丰富的地理信息处理功能,包括数据读写、几何操作、投影转换等。本节将重点讲解使用GeoTools方式来进行GeoJSON数据的生成。GeoTools是一个开源的Java GIS工具包,提供了丰富的地理空间数据处理功能。GeoTools可以方便地创建和操作GeoJSON对象,包括properties属性。使用Geotools生成的重点代码如下:

package com.yelang.project.geotools.geojson;import org.geotools.data.DataUtilities;import org.geotools.feature.simple.SimpleFeatureBuilder;import org.geotools.feature.simple.SimpleFeatureTypeBuilder;import org.geotools.geojson.feature.FeatureJSON;import org.locationtech.jts.geom.*;import org.opengis.feature.simple.SimpleFeature;import java.io.StringWriter;public class GeoToolsExample {@SuppressWarnings(\"deprecation\")public static void main(String[] args) throws Exception { GeometryFactory gf = new GeometryFactory(); // 1. 定义属性结构 SimpleFeatureTypeBuilder tBuilder = new SimpleFeatureTypeBuilder(); tBuilder.setName(\"Point\"); tBuilder.add(\"geom\", Point.class, 4326); tBuilder.add(\"name\", String.class); tBuilder.add(\"bz\", String.class); tBuilder.add(\"pinyin\", String.class); // 2. 构造特征 Point point = gf.createPoint(new Coordinate(109.171027134, 27.354881948 )); SimpleFeatureBuilder fBuilder = new SimpleFeatureBuilder(tBuilder.buildFeatureType()); fBuilder.add(point); fBuilder.add(\"新晃侗族自治县\"); fBuilder.add(\"区县名\"); fBuilder.add(\"Xinhuang Dongzu Zizhixian\"); SimpleFeature feature = fBuilder.buildFeature(\"1784020048910807047\"); // 3. 直接打印 FeatureJSON fjson = new FeatureJSON(); StringWriter out = new StringWriter(); //单个Feature输出 fjson.writeFeature(feature, out); //输出Feature集合 //fjson.writeFeatureCollection(DataUtilities.collection(feature), out); System.out.println(out.toString()); }}

        在IDE中运行以上程序后可以看到以下输出:

{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[109.171,27.3549]},\"properties\":{\"name\":\"新晃侗族自治县\",\"bz\":\"区县名\",\"pinyin\":\"Xinhuang Dongzu Zizhixian\"},\"id\":\"1784020048910807047\"}

        可以看到GeoJSON数据已经正常的生成。

3、PostGIS静态生成

        熟悉PostGIS的朋友应该知道,在PostGIS当中有直接将空间数据转为GeoJSON的方法,PostGIS是一个开源对象关系型空间数据库扩展,它允许用户在PostgreSQL数据库中存储和操作空间数据。PostGIS提供了ST_AsGeoJSON函数,可以将空间数据转换为GeoJSON格式。但是细心的朋友同时也会发现,使用St_asGeoJSON函数得到的数据没有包含属性,对于数据的展示不太友好,因此合理就需要基于PostGIS来进行生成。使用PostGIS静态生成有两种方式,第一种就是跟Java类型直接拼接JSON的数据返回给前端即可。关键SQL代码如下:

SELECT \'{\"type\": \"Feature\", \"geometry\": \' || ST_AsGeoJSON(geom) || \', \"properties\": {\"name\": \"\' || name || \'\", \"pinyin\": \"\' || pinyin || \'\", \"bz\": \"\' || bz || \'\"}}\' AS geojsonFROM biz_geographic_name where pk_id = \'1784020048910807047\';

        执行完成之后可以在数据库中看到以下输出:

{\"type\": \"Feature\", \"geometry\": {\"type\":\"Point\",\"coordinates\":[109.171027134,27.354881948]}, \"properties\": {\"name\": \"新晃侗族自治县\", \"pinyin\": \"Xinhuang Dongzu Zizhixian\", \"bz\": \"区县名\"}}

        关于在PostGIS中的静态生成除了这种拼接的方式,还有一种基于json_build_object的固定模板生成方法,我们可以根据需要按照我们的实际情况,将需要的字段进行查询返回出。SQL调整如下:

SELECT json_build_object( \'type\', \'Feature\', \'geometry\', ST_AsGeoJSON(geom)::json, \'properties\', json_build_object( \'name\', name, \'pinyin\', pinyin, \'bz\', bz,\'slx\',slx )) AS geojsonFROM biz_geographic_name where pk_id = \'1784020048910807047\';

        与上一种方式不一样的是,这里使用了json_build_object函数,然后使用一个查询将需要的字段进行了设置到Properties中。这么做的好处是字段比较确定,缺点就是需要提前明确字段信息。

4、PostGIS动态生成

        最后介绍一种比较偷懒的做法,这种做法直接使用查询语句将所有的属性都查询到结果集中,然后将这些数据及字段全部都设置到GeoJSON中,SQL语句如下:

SELECT jsonb_build_object( \'type\', \'Feature\', \'id\', pk_id, \'geometry\', ST_AsGeoJSON(geom)::jsonb, \'properties\', to_jsonb(row) - \'id\' - \'geom\') FROM (SELECT * FROM biz_geographic_name where pk_id = \'1784020048910807047\') ) row;

        在这里通过ST_AsGeoJSON生成几何部分,然后使用to_jsonb将属性字段转换为JSONB格式,并通过jsonb_build_object构建完整的GeoJSON对象。

        说明

  • to_jsonb(row) - \'geom\':这里假设id字段是需要的,所以没有排除它。如果id字段不需要,可以加上- \'id\'

  • ST_AsGeoJSON(geom)::jsonb:将几何对象转换为GeoJSON格式,并转换为jsonb类型,以便与properties属性合并。

        在数据库软件中执行上述sql语句后得到的结果与之前的一致,这里不再进行赘述。

三、总结

        以上就是本文的主要内容, 本文旨在为基于 Java、GeoTools 和 PostGIS 的 GeoJSON 动态属性注入提供一套完整的实战方案。希望通过分享我们在项目中积累的经验和教训,帮助开发者快速掌握这一关键技术,从而在地理信息应用开发中更加得心应手。不同的构建方法和使用场景不同,请大家合理选择合适的方式构建GeoJSON的properties属性。

  • 普通Java:使用标准的JSON库(如Jackson或Gson)构建GeoJSON对象,适合简单的场景。

  • GeoTools:提供丰富的地理空间数据处理功能,适合需要复杂地理空间操作的场景。

  • PostGIS:适合存储和操作大量空间数据的场景,可以直接在数据库中生成GeoJSON,减少应用层的处理负担。

        GeoJSON 动态属性注入是一个充满挑战但又极具价值的领域。通过本指南,我们希望读者能够掌握这一关键技术,并将其应用到实际项目中。我们相信,随着技术的不断发展和创新,地理信息应用将变得更加智能和高效,为我们的生活带来更多的便利。让我们一起探索地理信息技术的无限可能!行文仓促,定有不足之处,欢迎各位朋友在评论区批评指正,不胜感激。