在 GIS 开发中,经常会遇到 GeoJSONWKT(Well-Known Text) 之间的互转需求。
比如地图绘制、空间数据存储、数据库操作(PostGIS)、或者系统对接时,都会需要这类转换工具。

本文分享一个自己实现的 GeoJSON ↔ WKT 工具类,支持常见几何类型,方便在项目中直接使用。


📌 支持的几何类型

  • Point

  • LineString

  • Polygon

  • MultiPoint

  • MultiLineString

  • MultiPolygon

  • 兼容 Feature / FeatureCollection


📦 工具类代码


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

/**
 * GeoJSON 工具类
 * 提供 GeoJSON 与 WKT 的相互转换功能
 * 支持的几何类型:Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon
 */
@Slf4j
public class GeoJsonWktUtils {

    private static final ObjectMapper objectMapper = new ObjectMapper()
            .enable(SerializationFeature.INDENT_OUTPUT);

    /**
     * 将 GeoJSON 字符串转换为 WKT 字符串
     * 支持 FeatureCollection、Feature 及 Geometry
     */
    public static String geoJsonToWkt(String geoJsonGeoStr) {
        JSONObject root = JSON.parseObject(geoJsonGeoStr);
        String rootType = root.getString("type");

        if ("FeatureCollection".equalsIgnoreCase(rootType)) {
            JSONArray features = root.getJSONArray("features");
            if (features == null || features.isEmpty()) {
                log.warn("FeatureCollection 中 features 为空");
                return StringUtils.EMPTY;
            }
            JSONObject firstFeature = features.getJSONObject(0);
            JSONObject geometry = firstFeature.getJSONObject("geometry");
            if (geometry == null) {
                log.warn("Feature 中无 geometry");
                return StringUtils.EMPTY;
            }
            return geoJsonToWkt(geometry.toJSONString());
        } else if ("Feature".equalsIgnoreCase(rootType)) {
            JSONObject geometry = root.getJSONObject("geometry");
            if (geometry == null) {
                log.warn("Feature 不存在 geometry");
                return StringUtils.EMPTY;
            }
            return geoJsonToWkt(geometry.toJSONString());
        } else {
            String type = root.getString("type");
            JSONArray coordinates = root.getJSONArray("coordinates");
            return switch (type) {
                case "Point" -> pointToWkt(coordinates);
                case "LineString" -> lineStringToWkt(coordinates);
                case "Polygon" -> polygonToWkt(coordinates);
                case "MultiPoint" -> multiPointToWkt(coordinates);
                case "MultiLineString" -> multiLineStringToWkt(coordinates);
                case "MultiPolygon" -> multiPolygonToWkt(coordinates);
                default -> {
                    log.warn("不支持的数据类型: {}", type);
                    yield StringUtils.EMPTY;
                }
            };
        }
    }

    /** 判断字符串是否是有效的 GeoJSON 格式 */
    public static boolean isValidGeoJson(String json) {
        if (StringUtils.isBlank(json)){
            return false;
        }
        try {
            objectMapper.readTree(json);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    /** 将 GeoJSON 字符串格式化输出 */
    public static String formatGeoJson(String json) {
        try {
            Object obj = objectMapper.readValue(json, Object.class);
            return objectMapper.writeValueAsString(obj);
        } catch (IOException e) {
            throw new RuntimeException("GeoJSON格式化失败: " + e.getMessage(), e);
        }
    }

    /** 将任意对象序列化为 GeoJSON 字符串 */
    public static String toGeoJson(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (IOException e) {
            throw new RuntimeException("对象转GeoJSON失败: " + e.getMessage(), e);
        }
    }

    /** Point 类型坐标转 WKT */
    private static String pointToWkt(JSONArray coords) {
        return "POINT(" + coords.getDouble(0) + " " + coords.getDouble(1) + ")";
    }

    /** LineString 类型坐标转 WKT */
    private static String lineStringToWkt(JSONArray coords) {
        StringBuilder sb = new StringBuilder("LINESTRING(");
        for (int i = 0; i < coords.size(); i++) {
            JSONArray point = coords.getJSONArray(i);
            sb.append(point.getDouble(0)).append(" ").append(point.getDouble(1));
            if (i < coords.size() - 1) sb.append(", ");
        }
        sb.append(")");
        return sb.toString();
    }

    /** Polygon 类型坐标转 WKT */
    private static String polygonToWkt(JSONArray coords) {
        StringBuilder sb = new StringBuilder("POLYGON(");
        for (int i = 0; i < coords.size(); i++) {
            JSONArray ring = coords.getJSONArray(i);
            sb.append("(");
            for (int j = 0; j < ring.size(); j++) {
                JSONArray point = ring.getJSONArray(j);
                sb.append(point.getDouble(0)).append(" ").append(point.getDouble(1));
                if (j < ring.size() - 1) sb.append(", ");
            }
            sb.append(")");
            if (i < coords.size() - 1) sb.append(", ");
        }
        sb.append(")");
        return sb.toString();
    }

    /** MultiPoint 类型坐标转 WKT */
    private static String multiPointToWkt(JSONArray coords) {
        StringBuilder sb = new StringBuilder("MULTIPOINT(");
        for (int i = 0; i < coords.size(); i++) {
            JSONArray point = coords.getJSONArray(i);
            sb.append("(").append(point.getDouble(0)).append(" ").append(point.getDouble(1)).append(")");
            if (i < coords.size() - 1) sb.append(", ");
        }
        sb.append(")");
        return sb.toString();
    }

    /** MultiLineString 类型坐标转 WKT */
    private static String multiLineStringToWkt(JSONArray coords) {
        StringBuilder sb = new StringBuilder("MULTILINESTRING(");
        for (int i = 0; i < coords.size(); i++) {
            JSONArray line = coords.getJSONArray(i);
            sb.append("(");
            for (int j = 0; j < line.size(); j++) {
                JSONArray point = line.getJSONArray(j);
                sb.append(point.getDouble(0)).append(" ").append(point.getDouble(1));
                if (j < line.size() - 1) sb.append(", ");
            }
            sb.append(")");
            if (i < coords.size() - 1) sb.append(", ");
        }
        sb.append(")");
        return sb.toString();
    }

    /** MultiPolygon 类型坐标转 WKT */
    private static String multiPolygonToWkt(JSONArray coords) {
        StringBuilder sb = new StringBuilder("MULTIPOLYGON(");
        for (int i = 0; i < coords.size(); i++) {
            JSONArray polygon = coords.getJSONArray(i);
            sb.append("(");
            for (int j = 0; j < polygon.size(); j++) {
                JSONArray ring = polygon.getJSONArray(j);
                sb.append("(");
                for (int k = 0; k < ring.size(); k++) {
                    JSONArray point = ring.getJSONArray(k);
                    sb.append(point.getDouble(0)).append(" ").append(point.getDouble(1));
                    if (k < ring.size() - 1) sb.append(", ");
                }
                sb.append(")");
                if (j < polygon.size() - 1) sb.append(", ");
            }
            sb.append(")");
            if (i < coords.size() - 1) sb.append(", ");
        }
        sb.append(")");
        return sb.toString();
    }

    /** WKT 转 GeoJSON */
    public static String wktToGeoJson(String wkt) {
        wkt = wkt.trim();
        if (wkt.startsWith("POINT")) return pointWktToGeoJson(wkt);
        if (wkt.startsWith("LINESTRING")) return lineStringWktToGeoJson(wkt);
        if (wkt.startsWith("POLYGON")) return polygonWktToGeoJson(wkt);
        if (wkt.startsWith("MULTIPOINT")) return multiPointWktToGeoJson(wkt);
        if (wkt.startsWith("MULTILINESTRING")) return multiLineStringWktToGeoJson(wkt);
        if (wkt.startsWith("MULTIPOLYGON")) return multiPolygonWktToGeoJson(wkt);
        throw new UnsupportedOperationException("不支持的WKT类型: " + wkt);
    }

    /** WKT Point 转 GeoJSON */
    private static String pointWktToGeoJson(String wkt) {
        String[] parts = extractCoordinates(wkt);
        JSONObject geo = new JSONObject();
        geo.put("type", "Point");
        geo.put("coordinates", new double[]{Double.parseDouble(parts[0]), Double.parseDouble(parts[1])});
        return geo.toJSONString();
    }

    /** WKT LineString 转 GeoJSON */
    private static String lineStringWktToGeoJson(String wkt) {
        String[] parts = extractMultiCoordinates(wkt);
        JSONArray coords = toJsonCoordinates(parts);
        JSONObject geo = new JSONObject();
        geo.put("type", "LineString");
        geo.put("coordinates", coords);
        return geo.toJSONString();
    }

    /** WKT Polygon 转 GeoJSON */
    private static String polygonWktToGeoJson(String wkt) {
        return buildPolygonGeoJson(wkt, "Polygon");
    }

    /** WKT MultiPoint 转 GeoJSON */
    private static String multiPointWktToGeoJson(String wkt) {
        return buildSimpleMultiGeoJson(wkt, "MultiPoint");
    }

    /** WKT MultiLineString 转 GeoJSON */
    private static String multiLineStringWktToGeoJson(String wkt) {
        return buildPolygonGeoJson(wkt, "MultiLineString");
    }

    /** WKT MultiPolygon 转 GeoJSON */
    private static String multiPolygonWktToGeoJson(String wkt) {
        return buildPolygonGeoJson(wkt, "MultiPolygon");
    }

    /** 提取单点坐标 */
    private static String[] extractCoordinates(String wkt) {
        String content = wkt.substring(wkt.indexOf('(') + 1, wkt.lastIndexOf(')')).trim();
        return content.split(" ");
    }

    /** 提取多点/线坐标 */
    private static String[] extractMultiCoordinates(String wkt) {
        String content = wkt.substring(wkt.indexOf('(') + 1, wkt.lastIndexOf(')')).trim();
        return content.split(",");
    }

    /** 转换为坐标数组 */
    private static JSONArray toJsonCoordinates(String[] parts) {
        JSONArray arr = new JSONArray();
        for (String part : parts) {
            String[] xy = part.trim().split(" ");
            arr.add(new double[]{Double.parseDouble(xy[0]), Double.parseDouble(xy[1])});
        }
        return arr;
    }

    /** 构建简单的 Multi 类型 GeoJSON(如 MultiPoint) */
    private static String buildSimpleMultiGeoJson(String wkt, String type) {
        String content = wkt.substring(wkt.indexOf('('), wkt.lastIndexOf(')') + 1);
        content = content.replace("(", "").replace(")", "");
        String[] points = content.split(",");
        JSONArray arr = new JSONArray();
        for (String p : points) {
            String[] xy = p.trim().split(" ");
            arr.add(new double[]{Double.parseDouble(xy[0]), Double.parseDouble(xy[1])});
        }
        JSONObject geo = new JSONObject();
        geo.put("type", type);
        geo.put("coordinates", arr);
        return geo.toJSONString();
    }

    /** 构建 Polygon/MultiPolygon 类型 GeoJSON */
    private static String buildPolygonGeoJson(String wkt, String type) {
        String content = wkt.substring(wkt.indexOf('('), wkt.lastIndexOf(')') + 1);
        content = content.replaceAll("^\\(+|\\)+$", "");
        String[] polygonParts = content.split("\\)\\s*,\\s*\\(");
        JSONArray polygons = new JSONArray();
        for (String part : polygonParts) {
            String[] rings = part.split("\\)\\s*,\\s*\\(");
            JSONArray ringsArr = new JSONArray();
            for (String ring : rings) {
                ring = ring.replace("(", "").replace(")", "");
                String[] points = ring.split(",");
                JSONArray coords = new JSONArray();
                for (String p : points) {
                    String[] xy = p.trim().split(" ");
                    coords.add(new double[]{Double.parseDouble(xy[0]), Double.parseDouble(xy[1])});
                }
                ringsArr.add(coords);
            }
            polygons.add(ringsArr);
        }
        JSONObject geo = new JSONObject();
        geo.put("type", type);
        geo.put("coordinates", type.equals("Polygon") ? polygons.get(0) : polygons);
        return geo.toJSONString();
    }
}

功能介绍

  1. GeoJSON → WKT

    • 支持直接传入 FeatureCollection、Feature 或 Geometry 格式

    • 自动识别类型,生成对应的 WKT 字符串

    String geoJson = "{ \"type\": \"Point\", \"coordinates\": [120.2, 30.3] }"; 
    String wkt = GeoJsonWktUtils.geoJsonToWkt(geoJson); // 输出: POINT(120.2 30.3)

  2. WKT → GeoJSON

    • 识别几何类型

    • 转换为标准的 GeoJSON 字符串

      String wkt = "LINESTRING(120.1 30.2, 120.5 30.8)";
       String geoJson = GeoJsonWktUtils.wktToGeoJson(wkt); 
      // 输出: {"type":"LineString","coordinates":[[120.1,30.2],[120.5,30.8]]}

  3. 格式化 GeoJSON

    • 使用 Jackson 美化输出

    String prettyGeoJson = GeoJsonWktUtils.formatGeoJson(geoJson); 
    System.out.println(prettyGeoJson);

  4. 校验 GeoJSON 是否有效

    
    
    boolean valid = GeoJsonWktUtils.isValidGeoJson(geoJson);


⚡ 使用场景

  • 数据库交互

    • PostGIS 常用 WKT 存储空间数据,接口则可能返回 GeoJSON

  • 地图渲染

    • 前端地图组件(如 OpenLayers、Leaflet、Mapbox)更偏向 GeoJSON

  • 系统对接

    • 第三方接口可能格式不一致,工具类能快速统一


🎯 总结

  • 本文实现了一个通用的 GeoJSON ↔ WKT 工具类

  • 水利、测绘、GIS、地理信息系统 等项目中很实用

  • 支持常见几何类型,兼容 Feature / FeatureCollection

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐