package com.cdzy.operations.handler; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Point; import org.locationtech.jts.io.WKTReader; import java.io.IOException; import java.util.Locale; /** * JTS Point 反序列化器 - 将 JSON 反序列化为 Point * 支持 type、longitude、latitude 三个字段的格式 */ @Slf4j public class PointDeserializer extends JsonDeserializer { private final GeometryFactory geometryFactory = new GeometryFactory(); @Override public Point deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.getCodec().readTree(p); if (node.isNull()) { return null; } try { // 格式1: 三字段格式 {"type": "point", "longitude": 116.3974, "latitude": 39.9093} if (node.isObject() && node.has("type") && (node.has("longitude") || node.has("latitude"))) { return parseFromThreeFields(node); } // 格式2: 自定义格式 {"type": "point", "coordinates": [lng, lat]} if (node.isObject() && node.has("type")) { String type = node.get("type").asText().toLowerCase(Locale.ROOT); if ("point".equals(type)) { return parseFromCustomFormat(node); } } // 格式3: GeoJSON 格式 {"type": "Point", "coordinates": [lng, lat]} else if (node.isObject() && node.has("type")) { String type = node.get("type").asText(); if ("Point".equals(type)) { return parseFromGeoJSON(node); } } // 格式4: 坐标数组格式 [lng, lat] else if (node.isArray()) { return parseFromArray(node); } // 格式5: 简化的三字段格式(没有type) else if (node.isObject() && (node.has("longitude") || node.has("latitude"))) { return parseFromLongitudeLatitude(node); } // 格式6: WKT 字符串格式 "POINT(lng lat)" else if (node.isTextual()) { return parseFromWKT(node.asText()); } throw new IOException(""" 不支持的 Point JSON 格式,支持的格式包括: 1. 三字段格式: {"type":"point","longitude":116.3974,"latitude":39.9093} 2. 自定义格式: {"type":"point","coordinates":[lng,lat]} 3. GeoJSON格式: {"type":"Point","coordinates":[lng,lat]} 4. 坐标数组: [lng,lat] 5. 简化格式: {"longitude":116.3974,"latitude":39.9093} 6. WKT字符串: "POINT(lng lat)" 7. PostgreSQL点格式: "(lng,lat)\""""); } catch (Exception e) { log.error("!!! PointDeserializer 反序列化失败 !!!"); log.error("错误信息: {}", e.getMessage()); log.error("输入 JSON: {}", node); throw new IOException("Point 反序列化失败: " + e.getMessage(), e); } } @Override public Class handledType() { return Point.class; } /** * 解析三字段格式:{"type":"point","longitude":116.3974,"latitude":39.9093} */ private Point parseFromThreeFields(JsonNode node) throws IOException { String type = node.get("type").asText(); if (!"point".equalsIgnoreCase(type)) { throw new IOException("type 字段必须是 'point',当前是: " + type); } JsonNode lngNode = node.get("longitude"); JsonNode latNode = node.get("latitude"); if (lngNode == null || latNode == null) { throw new IOException("longitude 或 latitude 字段缺失"); } try { double longitude = lngNode.asDouble(); double latitude = latNode.asDouble(); return geometryFactory.createPoint(new Coordinate(longitude, latitude)); } catch (NumberFormatException e) { throw new IOException("longitude/latitude 格式错误", e); } } /** * 解析自定义格式:{"type":"point","coordinates":[lng,lat]} */ private Point parseFromCustomFormat(JsonNode node) throws IOException { JsonNode coordinatesNode = node.get("coordinates"); if (coordinatesNode == null) { throw new IOException("coordinates 字段缺失"); } return parseCoordinates(coordinatesNode); } /** * 解析 GeoJSON 格式:{"type":"Point","coordinates":[lng,lat]} */ private Point parseFromGeoJSON(JsonNode node) throws IOException { JsonNode coordinatesNode = node.get("coordinates"); if (coordinatesNode == null) { throw new IOException("coordinates 字段缺失"); } return parseCoordinates(coordinatesNode); } /** * 解析坐标数组格式:[lng, lat] */ private Point parseFromArray(JsonNode node) throws IOException { return parseCoordinates(node); } /** * 解析简化格式:{"longitude":116.3974,"latitude":39.9093} */ private Point parseFromLongitudeLatitude(JsonNode node) throws IOException { JsonNode lngNode = node.get("longitude"); JsonNode latNode = node.get("latitude"); if (lngNode == null || latNode == null) { throw new IOException("longitude 或 latitude 字段缺失"); } try { double longitude = lngNode.asDouble(); double latitude = latNode.asDouble(); return geometryFactory.createPoint(new Coordinate(longitude, latitude)); } catch (NumberFormatException e) { throw new IOException("longitude/latitude 格式错误", e); } } /** * 解析 WKT 字符串格式:POINT(lng lat) */ private Point parseFromWKT(String wktString) throws IOException { if (wktString == null || wktString.trim().isEmpty()) { throw new IOException("WKT 字符串为空"); } try { wktString = wktString.trim(); // 支持 PostgreSQL 点格式转换:(x,y) -> POINT(x y) if (wktString.startsWith("(") && wktString.endsWith(")")) { wktString = convertPostgresPointToWKT(wktString); } // 确保是有效的 WKT if (!wktString.toUpperCase().startsWith("POINT")) { wktString = "POINT(" + wktString + ")"; } WKTReader reader = new WKTReader(geometryFactory); Geometry geometry = reader.read(wktString); if (!(geometry instanceof Point)) { throw new IOException("WKT 字符串不是有效的 Point: " + wktString); } return (Point) geometry; } catch (Exception e) { throw new IOException("WKT 格式解析失败: " + wktString, e); } } /** * 解析坐标节点 */ private Point parseCoordinates(JsonNode coordinatesNode) throws IOException { if (coordinatesNode == null) { throw new IOException("坐标节点为 null"); } if (!coordinatesNode.isArray()) { throw new IOException("coordinates 字段必须是数组类型"); } if (coordinatesNode.size() < 2) { throw new IOException("坐标数组至少需要2个值(经度和纬度)"); } try { double lng = coordinatesNode.get(0).asDouble(); double lat = coordinatesNode.get(1).asDouble(); return geometryFactory.createPoint(new Coordinate(lng, lat)); } catch (NumberFormatException e) { throw new IOException("坐标格式错误: " + coordinatesNode, e); } } /** * 转换 PostgreSQL 点格式到 WKT 格式 */ private String convertPostgresPointToWKT(String pgPointString) { // PostgreSQL 格式: (x,y) 或 (x, y) // WKT 格式: POINT(x y) String cleaned = pgPointString .replaceAll("\\s+", "") // 移除所有空格 .replace("(", "") .replace(")", ""); String[] coords = cleaned.split(","); if (coords.length >= 2) { return "POINT(" + coords[0] + " " + coords[1] + ")"; } return pgPointString; // 无法转换,返回原值 } }