2025-12-30 09:26:52 +08:00

231 lines
8.0 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.*;
import org.locationtech.jts.io.WKTReader;
import java.io.IOException;
import java.util.Locale;
/**
* JTS Point 反序列化器 - 将 JSON 反序列化为 Point
* 支持多种 JSON 格式
*/
@Slf4j
public class PointDeserializer extends JsonDeserializer<Point> {
private final GeometryFactory geometryFactory = new GeometryFactory();
@Override
public Point deserialize(JsonParser p, DeserializationContext context) throws IOException {
JsonNode node = p.getCodec().readTree(p);
if (node.isNull()) {
return null;
}
try {
Point result = null;
// 格式1: 自定义格式 {"type": "point", "coordinates": [lng, lat]}
if (node.isObject() && node.has("type")) {
String type = node.get("type").asText().toLowerCase(Locale.ROOT);
if ("point".equals(type)) {
result = parseFromCustomFormat(node);
}
}
// 格式2: GeoJSON 格式 {"type": "Point", "coordinates": [lng, lat]}
else if (node.isObject() && node.has("type")) {
String type = node.get("type").asText();
if ("Point".equals(type)) {
result = parseFromGeoJSON(node);
}
}
// 格式3: 坐标数组格式 [lng, lat]
else if (node.isArray()) {
result = parseFromArray(node);
}
// 格式4: 对象格式但没有 type 字段,尝试解析 coordinates 或直接解析 x,y
else if (node.isObject()) {
result = parseFromObject(node);
}
// 格式5: WKT 字符串格式 "POINT(lng lat)"
else if (node.isTextual()) {
result = parseFromWKT(node.asText());
}
if (result == null) {
throw new IOException("""
不支持的 Point JSON 格式,支持的格式包括:
1. 自定义格式: {"type":"point","coordinates":[lng,lat]}
2. GeoJSON格式: {"type":"Point","coordinates":[lng,lat]}
3. 坐标数组: [lng,lat]
4. 简单对象: {"lng":116.3974,"lat":39.9093} 或 {"x":116.3974,"y":39.9093}
5. WKT字符串: "POINT(lng lat)"
6. PostgreSQL点格式: "(lng,lat)\"""");
}
return result;
} 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;
}
/**
* 解析自定义格式:{"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);
}
/**
* 解析对象格式:{"lng":116.3974,"lat":39.9093} 或 {"x":116.3974,"y":39.9093}
*/
private Point parseFromObject(JsonNode node) throws IOException {
// 尝试 lng/lat 格式
if (node.has("lng") && node.has("lat")) {
try {
double lng = node.get("lng").asDouble();
double lat = node.get("lat").asDouble();
return geometryFactory.createPoint(new Coordinate(lng, lat));
} catch (NumberFormatException e) {
throw new IOException("lng/lat 格式错误: " + node, e);
}
}
// 尝试 x/y 格式
if (node.has("x") && node.has("y")) {
try {
double x = node.get("x").asDouble();
double y = node.get("y").asDouble();
return geometryFactory.createPoint(new Coordinate(x, y));
} catch (NumberFormatException e) {
throw new IOException("x/y 格式错误: " + node, e);
}
}
// 尝试 coordinates 格式
if (node.has("coordinates")) {
return parseCoordinates(node.get("coordinates"));
}
throw new IOException("对象格式不支持: " + node);
}
/**
* 解析 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; // 无法转换,返回原值
}
}