地理位置序列化

This commit is contained in:
attiya 2025-12-30 13:44:23 +08:00
parent 5407da4c93
commit 7b200dba40
2 changed files with 104 additions and 91 deletions

View File

@ -13,7 +13,7 @@ import java.util.Locale;
/** /**
* JTS Point 反序列化器 - JSON 反序列化为 Point * JTS Point 反序列化器 - JSON 反序列化为 Point
* 支持多种 JSON 格式 * 支持 typelongitudelatitude 三个字段的格式
*/ */
@Slf4j @Slf4j
public class PointDeserializer extends JsonDeserializer<Point> { public class PointDeserializer extends JsonDeserializer<Point> {
@ -21,7 +21,7 @@ public class PointDeserializer extends JsonDeserializer<Point> {
private final GeometryFactory geometryFactory = new GeometryFactory(); private final GeometryFactory geometryFactory = new GeometryFactory();
@Override @Override
public Point deserialize(JsonParser p, DeserializationContext context) throws IOException { public Point deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
JsonNode node = p.getCodec().readTree(p); JsonNode node = p.getCodec().readTree(p);
if (node.isNull()) { if (node.isNull()) {
@ -29,47 +29,48 @@ public class PointDeserializer extends JsonDeserializer<Point> {
} }
try { try {
Point result = null; // 格式1: 三字段格式 {"type": "point", "longitude": 116.3974, "latitude": 39.9093}
if (node.isObject() && node.has("type") &&
(node.has("longitude") || node.has("latitude"))) {
return parseFromThreeFields(node);
}
// 格式1: 自定义格式 {"type": "point", "coordinates": [lng, lat]} // 格式2: 自定义格式 {"type": "point", "coordinates": [lng, lat]}
if (node.isObject() && node.has("type")) { if (node.isObject() && node.has("type")) {
String type = node.get("type").asText().toLowerCase(Locale.ROOT); String type = node.get("type").asText().toLowerCase(Locale.ROOT);
if ("point".equals(type)) { if ("point".equals(type)) {
result = parseFromCustomFormat(node); return parseFromCustomFormat(node);
} }
} }
// 格式2: GeoJSON 格式 {"type": "Point", "coordinates": [lng, lat]} // 格式3: GeoJSON 格式 {"type": "Point", "coordinates": [lng, lat]}
else if (node.isObject() && node.has("type")) { else if (node.isObject() && node.has("type")) {
String type = node.get("type").asText(); String type = node.get("type").asText();
if ("Point".equals(type)) { if ("Point".equals(type)) {
result = parseFromGeoJSON(node); return parseFromGeoJSON(node);
} }
} }
// 格式3: 坐标数组格式 [lng, lat] // 格式4: 坐标数组格式 [lng, lat]
else if (node.isArray()) { else if (node.isArray()) {
result = parseFromArray(node); return parseFromArray(node);
} }
// 格式4: 对象格式但没有 type 字段尝试解析 coordinates 或直接解析 x,y // 格式5: 简化的三字段格式没有type
else if (node.isObject()) { else if (node.isObject() && (node.has("longitude") || node.has("latitude"))) {
result = parseFromObject(node); return parseFromLongitudeLatitude(node);
} }
// 格式5: WKT 字符串格式 "POINT(lng lat)" // 格式6: WKT 字符串格式 "POINT(lng lat)"
else if (node.isTextual()) { else if (node.isTextual()) {
result = parseFromWKT(node.asText()); return parseFromWKT(node.asText());
} }
if (result == null) { throw new IOException("""
throw new IOException(""" 不支持的 Point JSON 格式支持的格式包括
不支持的 Point JSON 格式支持的格式包括 1. 三字段格式: {"type":"point","longitude":116.3974,"latitude":39.9093}
1. 自定义格式: {"type":"point","coordinates":[lng,lat]} 2. 自定义格式: {"type":"point","coordinates":[lng,lat]}
2. GeoJSON格式: {"type":"Point","coordinates":[lng,lat]} 3. GeoJSON格式: {"type":"Point","coordinates":[lng,lat]}
3. 坐标数组: [lng,lat] 4. 坐标数组: [lng,lat]
4. 简单对象: {"lng":116.3974,"lat":39.9093} {"x":116.3974,"y":39.9093} 5. 简化格式: {"longitude":116.3974,"latitude":39.9093}
5. WKT字符串: "POINT(lng lat)" 6. WKT字符串: "POINT(lng lat)"
6. PostgreSQL点格式: "(lng,lat)\""""); 7. PostgreSQL点格式: "(lng,lat)" """);
}
return result;
} catch (Exception e) { } catch (Exception e) {
log.error("!!! PointDeserializer 反序列化失败 !!!"); log.error("!!! PointDeserializer 反序列化失败 !!!");
@ -84,6 +85,31 @@ public class PointDeserializer extends JsonDeserializer<Point> {
return Point.class; 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]} * 解析自定义格式{"type":"point","coordinates":[lng,lat]}
*/ */
@ -116,37 +142,23 @@ public class PointDeserializer extends JsonDeserializer<Point> {
} }
/** /**
* 解析对象格式{"lng":116.3974,"lat":39.9093} {"x":116.3974,"y":39.9093} * 解析简化格式{"longitude":116.3974,"latitude":39.9093}
*/ */
private Point parseFromObject(JsonNode node) throws IOException { private Point parseFromLongitudeLatitude(JsonNode node) throws IOException {
// 尝试 lng/lat 格式 JsonNode lngNode = node.get("longitude");
if (node.has("lng") && node.has("lat")) { JsonNode latNode = node.get("latitude");
try {
double lng = node.get("lng").asDouble(); if (lngNode == null || latNode == null) {
double lat = node.get("lat").asDouble(); throw new IOException("longitude 或 latitude 字段缺失");
return geometryFactory.createPoint(new Coordinate(lng, lat));
} catch (NumberFormatException e) {
throw new IOException("lng/lat 格式错误: " + node, e);
}
} }
// 尝试 x/y 格式 try {
if (node.has("x") && node.has("y")) { double longitude = lngNode.asDouble();
try { double latitude = latNode.asDouble();
double x = node.get("x").asDouble(); return geometryFactory.createPoint(new Coordinate(longitude, latitude));
double y = node.get("y").asDouble(); } catch (NumberFormatException e) {
return geometryFactory.createPoint(new Coordinate(x, y)); throw new IOException("longitude/latitude 格式错误", e);
} catch (NumberFormatException e) {
throw new IOException("x/y 格式错误: " + node, e);
}
} }
// 尝试 coordinates 格式
if (node.has("coordinates")) {
return parseCoordinates(node.get("coordinates"));
}
throw new IOException("对象格式不支持: " + node);
} }
/** /**
@ -159,24 +171,24 @@ public class PointDeserializer extends JsonDeserializer<Point> {
try { try {
wktString = wktString.trim(); wktString = wktString.trim();
// 支持 PostgreSQL 点格式转换(x,y) -> POINT(x y) // 支持 PostgreSQL 点格式转换(x,y) -> POINT(x y)
if (wktString.startsWith("(") && wktString.endsWith(")")) { if (wktString.startsWith("(") && wktString.endsWith(")")) {
wktString = convertPostgresPointToWKT(wktString); wktString = convertPostgresPointToWKT(wktString);
} }
// 确保是有效的 WKT // 确保是有效的 WKT
if (!wktString.toUpperCase().startsWith("POINT")) { if (!wktString.toUpperCase().startsWith("POINT")) {
wktString = "POINT(" + wktString + ")"; wktString = "POINT(" + wktString + ")";
} }
WKTReader reader = new WKTReader(geometryFactory); WKTReader reader = new WKTReader(geometryFactory);
Geometry geometry = reader.read(wktString); Geometry geometry = reader.read(wktString);
if (!(geometry instanceof Point)) { if (!(geometry instanceof Point)) {
throw new IOException("WKT 字符串不是有效的 Point: " + wktString); throw new IOException("WKT 字符串不是有效的 Point: " + wktString);
} }
return (Point) geometry; return (Point) geometry;
} catch (Exception e) { } catch (Exception e) {
throw new IOException("WKT 格式解析失败: " + wktString, e); throw new IOException("WKT 格式解析失败: " + wktString, e);
@ -202,7 +214,7 @@ public class PointDeserializer extends JsonDeserializer<Point> {
try { try {
double lng = coordinatesNode.get(0).asDouble(); double lng = coordinatesNode.get(0).asDouble();
double lat = coordinatesNode.get(1).asDouble(); double lat = coordinatesNode.get(1).asDouble();
return geometryFactory.createPoint(new Coordinate(lng, lat)); return geometryFactory.createPoint(new Coordinate(lng, lat));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw new IOException("坐标格式错误: " + coordinatesNode, e); throw new IOException("坐标格式错误: " + coordinatesNode, e);
@ -215,17 +227,17 @@ public class PointDeserializer extends JsonDeserializer<Point> {
private String convertPostgresPointToWKT(String pgPointString) { private String convertPostgresPointToWKT(String pgPointString) {
// PostgreSQL 格式: (x,y) (x, y) // PostgreSQL 格式: (x,y) (x, y)
// WKT 格式: POINT(x y) // WKT 格式: POINT(x y)
String cleaned = pgPointString String cleaned = pgPointString
.replaceAll("\\s+", "") // 移除所有空格 .replaceAll("\\s+", "") // 移除所有空格
.replace("(", "") .replace("(", "")
.replace(")", ""); .replace(")", "");
String[] coords = cleaned.split(","); String[] coords = cleaned.split(",");
if (coords.length >= 2) { if (coords.length >= 2) {
return "POINT(" + coords[0] + " " + coords[1] + ")"; return "POINT(" + coords[0] + " " + coords[1] + ")";
} }
return pgPointString; // 无法转换返回原值 return pgPointString; // 无法转换返回原值
} }
} }

View File

@ -14,18 +14,18 @@ import java.util.Locale;
/** /**
* JTS Point 序列化器 - Point 序列化为 JSON * JTS Point 序列化器 - Point 序列化为 JSON
* 生成一致的 JSON 格式 * 输出 typelongitudelatitude 三个字段
*/ */
@Slf4j @Slf4j
public class PointSerializer extends JsonSerializer<Point> { public class PointSerializer extends JsonSerializer<Point> {
private static final DecimalFormat COORD_FORMAT = new DecimalFormat("#.##########", private static final DecimalFormat COORD_FORMAT = new DecimalFormat("#.##########",
DecimalFormatSymbols.getInstance(Locale.US)); DecimalFormatSymbols.getInstance(Locale.US));
private final WKTWriter wktWriter = new WKTWriter(); private final WKTWriter wktWriter = new WKTWriter();
@Override @Override
public void serialize(Point point, JsonGenerator gen, SerializerProvider serializers) public void serialize(Point point, JsonGenerator gen, SerializerProvider serializers)
throws IOException { throws IOException {
if (point == null) { if (point == null) {
gen.writeNull(); gen.writeNull();
@ -35,22 +35,18 @@ public class PointSerializer extends JsonSerializer<Point> {
try { try {
// 获取坐标 // 获取坐标
Coordinate coordinate = point.getCoordinate(); Coordinate coordinate = point.getCoordinate();
if (coordinate == null || Double.isNaN(coordinate.x) || Double.isNaN(coordinate.y)) { if (coordinate == null || Double.isNaN(coordinate.x) || Double.isNaN(coordinate.y)) {
log.warn("警告: 点坐标无效: {}", coordinate); log.warn("警告: 点坐标无效: {}", coordinate);
gen.writeNull(); gen.writeNull();
return; return;
} }
// 序列化为自定义格式 PolygonSerializer 格式一致 // 序列化为三字段格式typelongitudelatitude
gen.writeStartObject(); gen.writeStartObject();
gen.writeStringField("type", "point"); gen.writeStringField("type", "point");
gen.writeNumberField("longitude", formatCoordinate(coordinate.x));
gen.writeArrayFieldStart("coordinates"); gen.writeNumberField("latitude", formatCoordinate(coordinate.y));
gen.writeNumber(formatCoordinate(coordinate.x));
gen.writeNumber(formatCoordinate(coordinate.y));
gen.writeEndArray();
gen.writeEndObject(); gen.writeEndObject();
} catch (Exception e) { } catch (Exception e) {
@ -58,7 +54,7 @@ public class PointSerializer extends JsonSerializer<Point> {
log.error("错误信息: {}", e.getMessage()); log.error("错误信息: {}", e.getMessage());
// 尝试备用序列化方式 // 尝试备用序列化方式
try { try {
serializeAsSimpleObject(point, gen); serializeAsAlternativeFormat(point, gen);
} catch (Exception e2) { } catch (Exception e2) {
throw new IOException("Point 序列化失败: " + e.getMessage(), e); throw new IOException("Point 序列化失败: " + e.getMessage(), e);
} }
@ -79,9 +75,9 @@ public class PointSerializer extends JsonSerializer<Point> {
} }
/** /**
* 备用序列化方式序列化为简单对象格式 * 备用序列化方式序列化为 coordinates 数组格式
*/ */
private void serializeAsSimpleObject(Point point, JsonGenerator gen) throws IOException { private void serializeAsAlternativeFormat(Point point, JsonGenerator gen) throws IOException {
if (point == null || point.isEmpty()) { if (point == null || point.isEmpty()) {
gen.writeNull(); gen.writeNull();
return; return;
@ -89,16 +85,21 @@ public class PointSerializer extends JsonSerializer<Point> {
try { try {
Coordinate coordinate = point.getCoordinate(); Coordinate coordinate = point.getCoordinate();
// 方式1简单对象格式 {"lng":116.3974,"lat":39.9093} // 方式1coordinates 数组格式
gen.writeStartObject(); gen.writeStartObject();
gen.writeNumberField("lng", coordinate.x); gen.writeStringField("type", "point");
gen.writeNumberField("lat", coordinate.y);
gen.writeArrayFieldStart("coordinates");
gen.writeNumber(coordinate.x);
gen.writeNumber(coordinate.y);
gen.writeEndArray();
gen.writeEndObject(); gen.writeEndObject();
} catch (Exception e) { } catch (Exception e) {
log.error("简单对象序列化失败: {}", e.getMessage()); log.error("备用序列化失败: {}", e.getMessage());
// 最终备用序列化为 WKT 字符串 // 最终备用序列化为 WKT 字符串
try { try {
String wkt = wktWriter.write(point); String wkt = wktWriter.write(point);
@ -127,7 +128,7 @@ public class PointSerializer extends JsonSerializer<Point> {
} }
Coordinate coordinate = point.getCoordinate(); Coordinate coordinate = point.getCoordinate();
return "(" + getFormattedCoordinateString(coordinate.x) + return "(" + getFormattedCoordinateString(coordinate.x) +
"," + getFormattedCoordinateString(coordinate.y) + ")"; "," + getFormattedCoordinateString(coordinate.y) + ")";
} }
} }