231 lines
8.0 KiB
Java
231 lines
8.0 KiB
Java
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; // 无法转换,返回原值
|
||
}
|
||
} |