2026-01-16 11:25:03 +08:00

246 lines
8.7 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.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<Point> {
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<Point> 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; // 无法转换,返回原值
}
}