Java 后台生成 3 维 GeoJSON 完整实战指南
摘要本文详细介绍如何在 Java 后台生成带高程Z 坐标的 3 维 GeoJSON 数据。基于 GeoTools FastJSON2 实现包含完整代码示例、坐标转换、高程叠加等核心技术。适用于 GIS 开发、三维可视化、数字孪生等场景。一、什么是 3 维 GeoJSON标准 GeoJSON 只支持 2 维坐标经度、纬度但在实际项目中我们经常需要表达高程信息// 2D GeoJSON { type: Point, coordinates: [116.4, 39.9] } ​ // 3D GeoJSON带 Z 坐标 { type: Point, coordinates: [116.4, 39.9, 50.5] }应用场景建筑物高度可视化地形高程展示通信基站海拔无人机航迹规划二、技术选型2.1 核心依赖dependencies !-- GeoTools 核心库 -- dependency groupIdorg.geotools/groupId artifactIdgt-main/artifactId version28.2/version /dependency !-- GeoJSON 支持 -- dependency groupIdorg.geotools/groupId artifactIdgt-geojson/artifactId version28.2/version /dependency !-- JTS 几何库 -- dependency groupIdorg.locationtech.jts/groupId artifactIdjts-core/artifactId version1.19.0/version /dependency !-- FastJSON2结果序列化 -- dependency groupIdcom.alibaba.fastjson2/groupId artifactIdfastjson2/artifactId version2.0.43/version /dependency /dependencies2.2 技术架构┌─────────────────────────────────────┐ │ Spring Boot Controller │ │ GetMapping(/get3DGeojson) │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ Service 层 │ │ generate3DGeoJson(layerName) │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ GeoTools SimpleFeature │ │ JTS Geometry (带 Z 坐标) │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ FeatureJSON → GeoJSON String │ │ → FastJSON2 → JSONObject │ └─────────────────────────────────────┘三、核心实现3.1 工具类geojsonTools.java这是整个 3D GeoJSON 生成的核心工具类主要功能将ListMapString, Object转换为SimpleFeatureCollection支持 WKT 字符串解析自动添加 Z 坐标高程package com.ruoyi.gis.utils; ​ import lombok.extern.slf4j.Slf4j; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.feature.DefaultFeatureCollection; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.geojson.feature.FeatureJSON; import org.geotools.geojson.geom.GeometryJSON; import org.geotools.geometry.jts.JTSFactoryFinder; import org.locationtech.jts.geom.*; import org.locationtech.jts.io.WKTReader; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; ​ import java.io.StringReader; import java.io.StringWriter; import java.util.*; ​ /** * GeoJSON 工具类 - 支持 3D 坐标 * author zhong */ Slf4j public class geojsonTools { ​ /** * 将 ListMap 转换为 SimpleFeatureCollection3D 版本 */ public static SimpleFeatureCollection toSimpleFeatureCollections( ListMapString, Object results, String geometryField) throws Exception { if (results null || results.isEmpty()) { throw new IllegalArgumentException(输入数据为空); } ​ SimpleFeatureTypeBuilder typeBuilder new SimpleFeatureTypeBuilder(); typeBuilder.setName(Feature); // 找到字段最多的记录用于构建 FeatureType OptionalMapString, Object maxFieldRecord results.stream() .max(Comparator.comparingInt(Map::size)); ​ if (maxFieldRecord.isPresent()) { for (Map.EntryString, Object entry : maxFieldRecord.get().entrySet()) { String key entry.getKey(); Class? clazz; if (geom.equals(key)) { continue; } if (entry.getValue() null) { clazz String.class; } else if (entry.getValue() instanceof Geometry) { clazz ((Geometry) entry.getValue()).getClass(); } else { clazz entry.getValue().getClass(); } ​ if (key.equals(geometryField)) { typeBuilder.add(key, Geometry.class); } else { typeBuilder.add(key, clazz); } } } ​ SimpleFeatureType featureType typeBuilder.buildFeatureType(); SimpleFeatureBuilder featureBuilder new SimpleFeatureBuilder(featureType); DefaultFeatureCollection featureCollection new DefaultFeatureCollection(); ​ WKTReader wktReader new WKTReader(); ​ for (MapString, Object row : results) { for (Map.EntryString, Object entry : row.entrySet()) { String key entry.getKey(); Object value entry.getValue(); if (geom.equals(key)) { continue; } // 处理几何字段 if (key.equals(geometryField) value instanceof String) { String wktStr (String) value; Geometry geometry wktReader.read(new StringReader(wktStr)); // ★★★ 关键添加高程值Z 坐标★★★ Geometry withZ addZCoordinate(geometry, row.get(high)); featureBuilder.set(key, withZ); } else { if (featureBuilder.getFeatureType().getDescriptor(key) ! null) { featureBuilder.set(key, value null ? : value); } } } ​ SimpleFeature feature featureBuilder.buildFeature(null); featureCollection.add(feature); } ​ return featureCollection; } ​ /** * ★★★ 核心方法为 Geometry 添加 Z 坐标 ★★★ */ private static Geometry addZCoordinate(Geometry geometry, Object zValue) { if (zValue ! null) { GeometryFactory geometryFactory JTSFactoryFinder.getGeometryFactory(); if (geometry instanceof Point) { double z ((Number) zValue).doubleValue(); Point point (Point) geometry; Coordinate coordinate point.getCoordinate(); return geometryFactory.createPoint( new Coordinate(coordinate.x, coordinate.y, z) ); } else if (geometry instanceof LineString) { double z ((Number) zValue).doubleValue(); LineString lineString (LineString) geometry; Coordinate[] coordinates lineString.getCoordinates(); for (int i 0; i coordinates.length; i) { coordinates[i] new Coordinate( coordinates[i].x, coordinates[i].y, z ); } return geometryFactory.createLineString(coordinates); } else if (geometry instanceof Polygon) { double z ((Number) zValue).doubleValue(); Polygon polygon (Polygon) geometry; // 处理外环 Coordinate[] shellCoordinates polygon.getExteriorRing().getCoordinates(); for (int i 0; i shellCoordinates.length; i) { shellCoordinates[i] new Coordinate( shellCoordinates[i].x, shellCoordinates[i].y, z ); } LinearRing shell geometryFactory.createLinearRing(shellCoordinates); // 处理内环洞 LinearRing[] holes new LinearRing[polygon.getNumInteriorRing()]; for (int h 0; h holes.length; h) { Coordinate[] holeCoordinates polygon.getInteriorRingN(h).getCoordinates(); for (int i 0; i holeCoordinates.length; i) { holeCoordinates[i] new Coordinate( holeCoordinates[i].x, holeCoordinates[i].y, z ); } holes[h] geometryFactory.createLinearRing(holeCoordinates); } ​ return geometryFactory.createPolygon(shell, holes); } } return geometry; } }3.2 Service 层实现Service Slf4j public class IShapefileServiceImpl implements IShapefileService { Override public JSONObject generate3DGeoJson(String layerName) { try { // 1. 从数据库获取图层数据 ListMapString, Object results baseMapper.getFeatures( layerName, null, null ); ​ // 2. 转换为 SimpleFeatureCollection带 Z 坐标 SimpleFeatureCollection featureCollection geojsonTools.toSimpleFeatureCollections(results, geometry); ​ // 3. ★★★ 关键设置高精度16 位小数★★★ GeometryJSON geometryJSON new GeometryJSON(16); FeatureJSON featureJson new FeatureJSON(geometryJSON); StringWriter geoJsonWriter new StringWriter(); ​ // 4. 转换为 GeoJSON 字符串 featureJson.writeFeatureCollection(featureCollection, geoJsonWriter); String geoJsonString geoJsonWriter.toString(); ​ // 5. 使用 FastJSON2 解析为 JSONObject return JSONObject.parseObject(geoJsonString); } catch (Exception e) { throw new RuntimeException( 生成 3D GeoJSON 时发生错误 e.getMessage(), e ); } } }3.3 Controller 层接口RestController RequestMapping(/spatial) Tag(name 3D GeoJSON 生成, description 三维地理数据接口) Slf4j public class SpatialController { Autowired private IShapefileService iShapefileService; ​ /** * 获取 3D GeoJSON 数据 * param layerName 图层名称 * return 3D GeoJSON */ GetMapping(/get3DGeojson) Operation(summary 3D GeoJSON, description 生成带高程的 GeoJSON 数据) Anonymous public AjaxResult get3DGeojson( RequestParam(name layerName, defaultValue y_point) String layerName ) { JSONObject geojson iShapefileService.generate3DGeoJson(layerName); return AjaxResult.success(获得 3D GeoJSON, geojson); } }四、数据库层支持4.1 MyBatis Mapper 查询Mapper public interface ShapefileMapper extends BaseMapperShapefileInfo { /** * 获取图层要素包含高程字段 */ Select(SELECT id, name, ST_AsText(geometry) as geometry, height as high // ★★★ 高程字段 ★★★ FROM ${layerName} WHERE deleted 0) ListMapString, Object getFeatures( Param(layerName) String layerName, Param(parameter) String parameter, Param(parameterContent) String parameterContent ); }4.2 数据表结构示例CREATE TABLE y_point ( id bigint NOT NULL AUTO_INCREMENT, name varchar(100) DEFAULT NULL COMMENT 名称, geometry point DEFAULT NULL COMMENT 几何坐标, height decimal(10,2) DEFAULT NULL COMMENT 高程/海拔, -- ★★★ 关键字段 ★★★ deleted tinyint DEFAULT 0, PRIMARY KEY (id), SPATIAL KEY idx_geometry (geometry) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;五、输出结果示例5.1 3D Point{ type: FeatureCollection, features: [ { type: Feature, geometry: { type: Point, coordinates: [116.407526, 39.90403, 50.5] }, properties: { name: 北京市朝阳区某建筑, id: 1 } } ] }5.2 3D LineString{ type: Feature, geometry: { type: LineString, coordinates: [ [116.4, 39.9, 100], [116.41, 39.91, 120], [116.42, 39.92, 150] ] }, properties: { name: 无人机航线, id: 2 } }5.3 3D Polygon{ type: Feature, geometry: { type: Polygon, coordinates: [[ [116.4, 39.9, 50], [116.41, 39.9, 50], [116.41, 39.91, 50], [116.4, 39.91, 50], [116.4, 39.9, 50] ]] }, properties: { name: 建筑物轮廓, height: 50 } }六、前端可视化Cesium.js生成 3D GeoJSON 后可以在 Cesium 中直接加载// Cesium 加载 3D GeoJSON viewer.dataSources.add( Cesium.GeoJsonDataSource.load(/spatial/get3DGeojson?layerNamey_point, { stroke: Cesium.Color.HOTPINK, fill: Cesium.Color.PINK.withAlpha(0.5), strokeWidth: 3, markerSymbol: ?, clampToGround: false // ★★★ 不贴地保留 Z 坐标 ★★★ }) ).then(dataSource { viewer.zoomTo(dataSource); });七、常见问题7.1 Z 坐标丢失原因GeoTools 默认可能忽略 Z 坐标解决// 确保使用 GeometryJSON 并设置精度 GeometryJSON geometryJSON new GeometryJSON(16); FeatureJSON featureJson new FeatureJSON(geometryJSON);7.2 精度不够解决// 设置 16 位小数精度 GeometryJSON geometryJSON new GeometryJSON(16);7.3 性能优化对于大数据量// 1. 分页查询 ListMapString, Object results baseMapper.getFeaturesWithPagination( layerName, pageNum, pageSize ); ​ // 2. 使用异步处理 CompletableFutureJSONObject future CompletableFuture.supplyAsync(() - { return iShapefileService.generate3DGeoJson(layerName); });八、完整项目结构gis-ruoyi/ ├── gis/ │ ├── src/main/java/com/ruoyi/gis/ │ │ ├── controller/ │ │ │ └── SpatialController.java # REST 接口 │ │ ├── service/ │ │ │ ├── IShapefileService.java # 服务接口 │ │ │ └── impl/ │ │ │ └── IShapefileServiceImpl.java # 服务实现 │ │ ├── mapper/ │ │ │ └── ShapefileMapper.java # 数据访问 │ │ └── utils/ │ │ └── geojsonTools.java # 核心工具类 │ └── pom.xml # Maven 依赖九、总结核心技术点技术点实现方式关键代码WKT 解析JTS WKTReaderwktReader.read(wktStr)Z 坐标添加JTS Coordinatenew Coordinate(x, y, z)Feature 构建GeoTools SimpleFeatureBuilderfeatureBuilder.set()GeoJSON 序列化GeoTools FeatureJSONfeatureJson.writeFeatureCollection()精度控制GeometryJSONnew GeometryJSON(16)最佳实践数据库存储 WKT 格式几何单独字段存储高程值使用高精度 GeometryJSON16 位小数异常处理要完善大数据量考虑分页参考资料GeoTools 官方文档JTS Topology SuiteGeoJSON 规范 RFC 7946Cesium 3D Tiles版权声明本文代码已开源转载请注明出处。欢迎交流如有问题欢迎在评论区留言讨论觉得有用请点赞 收藏 ⭐ 关注

相关新闻

最新新闻

日新闻

周新闻

月新闻