246 lines
8.7 KiB
Java
Raw Normal View History

2025-12-30 09:26:52 +08:00
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;
2026-01-16 11:25:03 +08:00
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
2025-12-30 09:26:52 +08:00
import org.locationtech.jts.io.WKTReader;
import java.io.IOException;
import java.util.Locale;
/**
* JTS Point 反序列化器 - JSON 反序列化为 Point
2025-12-30 13:44:23 +08:00
* 支持 typelongitudelatitude 三个字段的格式
2025-12-30 09:26:52 +08:00
*/
@Slf4j
public class PointDeserializer extends JsonDeserializer<Point> {
private final GeometryFactory geometryFactory = new GeometryFactory();
@Override
2025-12-30 13:44:23 +08:00
public Point deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
2025-12-30 09:26:52 +08:00
JsonNode node = p.getCodec().readTree(p);
if (node.isNull()) {
return null;
}
try {
2025-12-30 13:44:23 +08:00
// 格式1: 三字段格式 {"type": "point", "longitude": 116.3974, "latitude": 39.9093}
if (node.isObject() && node.has("type") &&
(node.has("longitude") || node.has("latitude"))) {
return parseFromThreeFields(node);
}
2025-12-30 09:26:52 +08:00
2025-12-30 13:44:23 +08:00
// 格式2: 自定义格式 {"type": "point", "coordinates": [lng, lat]}
2025-12-30 09:26:52 +08:00
if (node.isObject() && node.has("type")) {
String type = node.get("type").asText().toLowerCase(Locale.ROOT);
if ("point".equals(type)) {
2025-12-30 13:44:23 +08:00
return parseFromCustomFormat(node);
2025-12-30 09:26:52 +08:00
}
}
2025-12-30 13:44:23 +08:00
// 格式3: GeoJSON 格式 {"type": "Point", "coordinates": [lng, lat]}
2025-12-30 09:26:52 +08:00
else if (node.isObject() && node.has("type")) {
String type = node.get("type").asText();
if ("Point".equals(type)) {
2025-12-30 13:44:23 +08:00
return parseFromGeoJSON(node);
2025-12-30 09:26:52 +08:00
}
}
2025-12-30 13:44:23 +08:00
// 格式4: 坐标数组格式 [lng, lat]
2025-12-30 09:26:52 +08:00
else if (node.isArray()) {
2025-12-30 13:44:23 +08:00
return parseFromArray(node);
2025-12-30 09:26:52 +08:00
}
2025-12-30 13:44:23 +08:00
// 格式5: 简化的三字段格式没有type
else if (node.isObject() && (node.has("longitude") || node.has("latitude"))) {
return parseFromLongitudeLatitude(node);
2025-12-30 09:26:52 +08:00
}
2025-12-30 13:44:23 +08:00
// 格式6: WKT 字符串格式 "POINT(lng lat)"
2025-12-30 09:26:52 +08:00
else if (node.isTextual()) {
2025-12-30 13:44:23 +08:00
return parseFromWKT(node.asText());
2025-12-30 09:26:52 +08:00
}
2025-12-30 13:44:23 +08:00
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)"
2026-01-06 09:12:07 +08:00
7. PostgreSQL点格式: "(lng,lat)\"""");
2025-12-30 09:26:52 +08:00
} catch (Exception e) {
log.error("!!! PointDeserializer 反序列化失败 !!!");
log.error("错误信息: {}", e.getMessage());
log.error("输入 JSON: {}", node);
throw new IOException("Point 反序列化失败: " + e.getMessage(), e);
}
}
@Override
public Class<Point> handledType() {
return Point.class;
}
2025-12-30 13:44:23 +08:00
/**
* 解析三字段格式{"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);
}
}
2025-12-30 09:26:52 +08:00
/**
* 解析自定义格式{"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);
}
/**
2025-12-30 13:44:23 +08:00
* 解析简化格式{"longitude":116.3974,"latitude":39.9093}
2025-12-30 09:26:52 +08:00
*/
2025-12-30 13:44:23 +08:00
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 字段缺失");
2025-12-30 09:26:52 +08:00
}
2025-12-30 13:44:23 +08:00
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);
2025-12-30 09:26:52 +08:00
}
}
/**
* 解析 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();
2025-12-30 13:44:23 +08:00
2025-12-30 09:26:52 +08:00
// 支持 PostgreSQL 点格式转换:(x,y) -> POINT(x y)
if (wktString.startsWith("(") && wktString.endsWith(")")) {
wktString = convertPostgresPointToWKT(wktString);
}
2025-12-30 13:44:23 +08:00
2025-12-30 09:26:52 +08:00
// 确保是有效的 WKT
if (!wktString.toUpperCase().startsWith("POINT")) {
wktString = "POINT(" + wktString + ")";
}
2025-12-30 13:44:23 +08:00
2025-12-30 09:26:52 +08:00
WKTReader reader = new WKTReader(geometryFactory);
Geometry geometry = reader.read(wktString);
2025-12-30 13:44:23 +08:00
2025-12-30 09:26:52 +08:00
if (!(geometry instanceof Point)) {
throw new IOException("WKT 字符串不是有效的 Point: " + wktString);
}
2025-12-30 13:44:23 +08:00
2025-12-30 09:26:52 +08:00
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();
2025-12-30 13:44:23 +08:00
2025-12-30 09:26:52 +08:00
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)
2025-12-30 13:44:23 +08:00
2025-12-30 09:26:52 +08:00
String cleaned = pgPointString
2025-12-30 13:44:23 +08:00
.replaceAll("\\s+", "") // 移除所有空格
.replace("(", "")
.replace(")", "");
2025-12-30 09:26:52 +08:00
String[] coords = cleaned.split(",");
if (coords.length >= 2) {
return "POINT(" + coords[0] + " " + coords[1] + ")";
}
2025-12-30 13:44:23 +08:00
2025-12-30 09:26:52 +08:00
return pgPointString; // 无法转换,返回原值
}
}