GPS坐标系转换:点、区域序列化与反序列化、类型处理

This commit is contained in:
attiya 2025-10-21 14:57:52 +08:00
parent 992fcc6bcb
commit 4bef9ce778
6 changed files with 478 additions and 9 deletions

View File

@ -3,6 +3,8 @@ package com.cdzy.operations.config;
import cn.hutool.core.date.DatePattern;
import com.cdzy.operations.handler.PGpointDeserializer;
import com.cdzy.operations.handler.PGpointSerializer;
import com.cdzy.operations.handler.PGpolygonDeserializer;
import com.cdzy.operations.handler.PGpolygonSerializer;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
@ -12,6 +14,7 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.postgresql.geometric.PGpoint;
import org.postgresql.geometric.PGpolygon;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
@ -38,11 +41,13 @@ public class JacksonConfig {
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DatePattern.NORM_DATE_FORMATTER));
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DatePattern.NORM_DATETIME_FORMATTER));
javaTimeModule.addSerializer(PGpoint.class, new PGpointSerializer());
javaTimeModule.addSerializer(PGpolygon.class, new PGpolygonSerializer());
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DatePattern.NORM_TIME_FORMATTER));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DatePattern.NORM_DATE_FORMATTER));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DatePattern.NORM_DATETIME_FORMATTER));
javaTimeModule.addDeserializer(PGpoint.class, new PGpointDeserializer());
javaTimeModule.addDeserializer(PGpolygon.class, new PGpolygonDeserializer());
return javaTimeModule;
}

View File

@ -1,14 +1,14 @@
package com.cdzy.operations.controller;
import com.cdzy.common.model.response.JsonResult;
import com.cdzy.operations.model.entity.EbikeBikeInfo;
import com.cdzy.operations.model.vo.EbikeBikeBindVo;
import com.cdzy.operations.service.EbikeBikeInfoService;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 车辆基本信息 控制层
@ -35,4 +35,24 @@ public class EbikeBikeInfoController {
return JsonResult.success();
}
/**
* 车辆列表
* @return 列表
*/
@GetMapping("list")
public JsonResult<?> list() {
List<EbikeBikeInfo> list = ebikeBikeInfoService.list();
return JsonResult.success(list);
}
/**
* 生成车辆
* @return 列表
*/
@PostMapping("save")
public JsonResult<?> save(@Validated @RequestBody EbikeBikeInfo ebikeBikeInfo) {
ebikeBikeInfoService.save(ebikeBikeInfo);
return JsonResult.success();
}
}

View File

@ -23,11 +23,12 @@ public class PGpointDeserializer extends JsonDeserializer<PGpoint> {
try {
// 方式1: GeoJSON 格式
if (node.isObject() && node.has("type") && "Point".equals(node.get("type").asText())) {
JsonNode coordinates = node.get("coordinates");
if (coordinates != null && coordinates.isArray() && coordinates.size() >= 2) {
double x = coordinates.get(0).asDouble();
double y = coordinates.get(1).asDouble();
if (node.isObject()) {
JsonNode longitude = node.get("longitude");
JsonNode latitude = node.get("latitude");
if (longitude != null && latitude != null) {
double x = longitude.asDouble();
double y =latitude.asDouble();
return new PGpoint(x, y);
}
}

View File

@ -0,0 +1,171 @@
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 org.postgresql.geometric.PGpoint;
import org.postgresql.geometric.PGpolygon;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* PGpolygon 反序列化器 - JSON 反序列化为 PGpolygon
*/
public class PGpolygonDeserializer extends JsonDeserializer<PGpolygon> {
@Override
public PGpolygon deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
JsonNode node = p.getCodec().readTree(p);
if (node.isNull()) {
return null;
}
try {
// 支持多种 JSON 格式
// 格式1: 自定义格式 {"type": "polygon", "coordinates": [[x1,y1], [x2,y2], ...]}
if (node.isObject() && node.has("type") && "polygon".equals(node.get("type").asText())) {
return parseFromCustomFormat(node);
}
// 格式2: GeoJSON 格式 {"type": "Polygon", "coordinates": [[[x1,y1], [x2,y2], ...]]}
if (node.isObject() && node.has("type") && "Polygon".equals(node.get("type").asText())) {
return parseFromGeoJSON(node);
}
// 格式3: 直接坐标数组 [[x1,y1], [x2,y2], ...]
if (node.isArray()) {
return parseFromArray(node);
}
// 格式4: 字符串格式 "((x1 y1, x2 y2, ...))"
if (node.isTextual()) {
return parseFromString(node.asText());
}
throw new IOException("不支持的 PGpolygon JSON 格式");
} catch (Exception e) {
throw new IOException("PGpolygon 反序列化失败: " + e.getMessage(), e);
}
}
@Override
public Class<PGpolygon> handledType() {
return PGpolygon.class;
}
/**
* 解析自定义格式
*/
private PGpolygon parseFromCustomFormat(JsonNode node) throws IOException {
JsonNode coordinatesNode = node.get("coordinates");
if (coordinatesNode == null || !coordinatesNode.isArray()) {
throw new IOException("coordinates 字段缺失或格式错误");
}
List<PGpoint> points = new ArrayList<>();
for (JsonNode coordNode : coordinatesNode) {
if (coordNode.isArray() && coordNode.size() >= 2) {
double x = coordNode.get(0).asDouble();
double y = coordNode.get(1).asDouble();
points.add(new PGpoint(x, y));
}
}
return createPGpolygonFromPoints(points);
}
/**
* 解析 GeoJSON 格式
*/
private PGpolygon parseFromGeoJSON(JsonNode node) throws IOException {
JsonNode coordinatesNode = node.get("coordinates");
if (coordinatesNode == null || !coordinatesNode.isArray() || coordinatesNode.isEmpty()) {
throw new IOException("coordinates 字段缺失或格式错误");
}
// GeoJSON 格式: coordinates 是三维数组 [[[x1,y1], [x2,y2], ...]]
JsonNode ringNode = coordinatesNode.get(0);
if (!ringNode.isArray()) {
throw new IOException("GeoJSON coordinates 格式错误");
}
List<PGpoint> points = new ArrayList<>();
for (JsonNode coordNode : ringNode) {
if (coordNode.isArray() && coordNode.size() >= 2) {
double x = coordNode.get(0).asDouble();
double y = coordNode.get(1).asDouble();
points.add(new PGpoint(x, y));
}
}
return createPGpolygonFromPoints(points);
}
/**
* 解析坐标数组格式
*/
private PGpolygon parseFromArray(JsonNode node) throws IOException {
List<PGpoint> points = new ArrayList<>();
for (JsonNode coordNode : node) {
if (coordNode.isArray() && coordNode.size() >= 2) {
double x = coordNode.get(0).asDouble();
double y = coordNode.get(1).asDouble();
points.add(new PGpoint(x, y));
}
}
return createPGpolygonFromPoints(points);
}
/**
* 解析字符串格式
*/
private PGpolygon parseFromString(String polygonString) throws IOException {
try {
return new PGpolygon(polygonString);
} catch (Exception e) {
throw new IOException("PGpolygon 字符串格式解析失败: " + polygonString, e);
}
}
/**
* 从点列表创建 PGpolygon
*/
private PGpolygon createPGpolygonFromPoints(List<PGpoint> points) throws IOException {
if (points == null || points.size() < 3) {
throw new IOException("多边形至少需要3个点");
}
try {
// 构建多边形字符串格式((x1 y1, x2 y2, x3 y3, x1 y1))
StringBuilder sb = new StringBuilder("((");
for (int i = 0; i < points.size(); i++) {
PGpoint point = points.get(i);
sb.append(point.x).append(" ").append(point.y);
if (i < points.size() - 1) {
sb.append(", ");
}
}
// 确保多边形闭合首尾点相同
PGpoint firstPoint = points.get(0);
PGpoint lastPoint = points.get(points.size() - 1);
if (firstPoint.x != lastPoint.x || firstPoint.y != lastPoint.y) {
sb.append(", ").append(firstPoint.x).append(" ").append(firstPoint.y);
}
sb.append("))");
return new PGpolygon(sb.toString());
} catch (Exception e) {
throw new IOException("创建 PGpolygon 失败", e);
}
}
}

View File

@ -0,0 +1,70 @@
package com.cdzy.operations.handler;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.postgresql.geometric.PGpolygon;
import org.postgresql.geometric.PGpoint;
import java.io.IOException;
/**
* PGpolygon 序列化器 - PGpolygon 序列化为 JSON
*/
public class PGpolygonSerializer extends JsonSerializer<PGpolygon> {
@Override
public void serialize(PGpolygon polygon, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (polygon == null) {
gen.writeNull();
return;
}
// 方式1: 序列化为自定义格式
gen.writeStartObject();
gen.writeStringField("type", "polygon");
// 解析多边形字符串获取点坐标
PGpoint[] points = parsePointsFromPolygon(polygon);
gen.writeArrayFieldStart("coordinates");
for (PGpoint point : points) {
gen.writeStartArray();
gen.writeNumber(point.x);
gen.writeNumber(point.y);
gen.writeEndArray();
}
gen.writeEndArray();
gen.writeEndObject();
}
@Override
public Class<PGpolygon> handledType() {
return PGpolygon.class;
}
/**
* PGpolygon 解析点坐标
*/
private PGpoint[] parsePointsFromPolygon(PGpolygon polygon) {
try {
String polygonString = polygon.toString();
// 解析格式((x1 y1, x2 y2, x3 y3, x1 y1))
String pointsStr = polygonString.replaceAll("[()]", "").trim();
String[] pointPairs = pointsStr.split(",");
PGpoint[] points = new PGpoint[pointPairs.length];
for (int i = 0; i < pointPairs.length; i++) {
String pointPair = pointPairs[i].trim();
String[] coords = pointPair.split("\\s+");
if (coords.length >= 2) {
double x = Double.parseDouble(coords[0]);
double y = Double.parseDouble(coords[1]);
points[i] = new PGpoint(x, y);
}
}
return points;
} catch (Exception e) {
throw new RuntimeException("Failed to parse points from polygon", e);
}
}
}

View File

@ -0,0 +1,202 @@
package com.cdzy.operations.handler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.postgresql.geometric.PGpolygon;
import org.postgresql.geometric.PGpoint;
import org.postgresql.util.PGobject;
import com.cdzy.common.utils.CoordinateUtil;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* PGpolygon 类型处理器 - 集成 CoordinateUtil 实现坐标转换
* 写入时GCJ-02 WGS-84
* 读取时WGS-84 GCJ-02
*/
public class PGpolygonTypeHandler implements TypeHandler<PGpolygon> {
// 正则表达式用于解析多边形字符串格式((x1 y1, x2 y2, x3 y3, x1 y1))
private static final Pattern POLYGON_PATTERN = Pattern.compile("\\(\\s*\\(([^)]+)\\)\\s*\\)");
@Override
public void setParameter(PreparedStatement ps, int i, PGpolygon parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
ps.setNull(i, Types.OTHER);
} else {
// 写入数据库时GCJ-02 WGS-84
PGpolygon wgs84Polygon = gcj02ToWgs84(parameter);
ps.setObject(i, wgs84Polygon);
}
}
@Override
public PGpolygon getResult(ResultSet rs, String columnName) throws SQLException {
Object object = rs.getObject(columnName);
PGpolygon wgs84Polygon = convertToPGpolygon(object);
// 读取时WGS-84 GCJ-02
return wgs84ToGcj02(wgs84Polygon);
}
@Override
public PGpolygon getResult(ResultSet rs, int columnIndex) throws SQLException {
Object object = rs.getObject(columnIndex);
PGpolygon wgs84Polygon = convertToPGpolygon(object);
// 读取时WGS-84 GCJ-02
return wgs84ToGcj02(wgs84Polygon);
}
@Override
public PGpolygon getResult(CallableStatement cs, int columnIndex) throws SQLException {
Object object = cs.getObject(columnIndex);
PGpolygon wgs84Polygon = convertToPGpolygon(object);
// 读取时WGS-84 GCJ-02
return wgs84ToGcj02(wgs84Polygon);
}
/**
* 将数据库对象转换为 PGpolygon
*/
private PGpolygon convertToPGpolygon(Object object) {
if (object == null) {
return null;
}
try {
if (object instanceof PGpolygon) {
// 直接是 PGpolygon 类型
return (PGpolygon) object;
} else if (object instanceof String) {
// 字符串格式 "((x1 y1, x2 y2, x3 y3, x1 y1))"
return new PGpolygon((String) object);
} else if (object instanceof PGobject pgObject) {
// PGobject 类型获取其字符串值
return new PGpolygon(pgObject.getValue());
}
} catch (Exception e) {
throw new RuntimeException("Failed to convert to PGpolygon: " + object, e);
}
return null;
}
/**
* GCJ-02 WGS-84写入数据库时调用
*/
private PGpolygon gcj02ToWgs84(PGpolygon gcj02Polygon) {
if (gcj02Polygon == null) {
return null;
}
try {
// 通过字符串解析获取点坐标
String polygonString = gcj02Polygon.toString();
PGpoint[] points = parsePointsFromPolygonString(polygonString);
List<PGpoint> wgs84Points = new ArrayList<>();
// 对每个点进行坐标转换
for (PGpoint gcj02Point : points) {
double[] wgs84 = CoordinateUtil.GCJ02ToWGS84(gcj02Point.x, gcj02Point.y);
wgs84Points.add(new PGpoint(wgs84[0], wgs84[1]));
}
// 创建新的 WGS-84 多边形
return createPGpolygonFromPoints(wgs84Points);
} catch (Exception e) {
throw new RuntimeException("GCJ-02 转 WGS-84 区域转换错误", e);
}
}
/**
* WGS-84 GCJ-02从数据库读取时调用
*/
private PGpolygon wgs84ToGcj02(PGpolygon wgs84Polygon) {
if (wgs84Polygon == null) {
return null;
}
try {
// 通过字符串解析获取点坐标
String polygonString = wgs84Polygon.toString();
PGpoint[] points = parsePointsFromPolygonString(polygonString);
List<PGpoint> gcj02Points = new ArrayList<>();
// 对每个点进行坐标转换
for (PGpoint wgs84Point : points) {
double[] gcj02 = CoordinateUtil.WGS84ToGCJ02(wgs84Point.x, wgs84Point.y);
gcj02Points.add(new PGpoint(gcj02[0], gcj02[1]));
}
// 创建新的 GCJ-02 多边形
return createPGpolygonFromPoints(gcj02Points);
} catch (Exception e) {
throw new RuntimeException("GCJ-02 转 WGS-84 区域转换错误", e);
}
}
/**
* 从多边形字符串中解析点坐标
*/
private PGpoint[] parsePointsFromPolygonString(String polygonString) {
List<PGpoint> points = new ArrayList<>();
try {
Matcher matcher = POLYGON_PATTERN.matcher(polygonString);
if (matcher.find()) {
String pointsStr = matcher.group(1);
// 分割点坐标x1 y1, x2 y2, x3 y3
String[] pointPairs = pointsStr.split(",");
for (String pointPair : pointPairs) {
pointPair = pointPair.trim();
String[] coords = pointPair.split("\\s+");
if (coords.length >= 2) {
double x = Double.parseDouble(coords[0]);
double y = Double.parseDouble(coords[1]);
points.add(new PGpoint(x, y));
}
}
}
} catch (Exception e) {
throw new RuntimeException("Failed to parse points from polygon string: " + polygonString, e);
}
return points.toArray(new PGpoint[0]);
}
/**
* 从点列表创建 PGpolygon
*/
private PGpolygon createPGpolygonFromPoints(List<PGpoint> points) {
if (points.isEmpty()) {
return null;
}
try {
// 构建多边形字符串格式((x1 y1, x2 y2, x3 y3, x1 y1))
StringBuilder sb = new StringBuilder("((");
for (int i = 0; i < points.size(); i++) {
PGpoint point = points.get(i);
sb.append(point.x).append(" ").append(point.y);
if (i < points.size() - 1) {
sb.append(", ");
}
}
// 确保多边形闭合首尾点相同
PGpoint firstPoint = points.get(0);
sb.append(", ").append(firstPoint.x).append(" ").append(firstPoint.y);
sb.append("))");
return new PGpolygon(sb.toString());
} catch (Exception e) {
throw new RuntimeException("区域点列表创建错误", e);
}
}
}