坐标偏移算法
This commit is contained in:
parent
513e0e45fc
commit
5c7fb64ad8
@ -5,7 +5,10 @@ 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.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;
|
||||
@ -14,7 +17,6 @@ import java.util.Locale;
|
||||
/**
|
||||
* JTS Point 反序列化器 - 将 JSON 反序列化为 Point
|
||||
* 支持 type、longitude、latitude 三个字段的格式
|
||||
* @author attiya
|
||||
*/
|
||||
@Slf4j
|
||||
public class PointDeserializer extends JsonDeserializer<Point> {
|
||||
|
||||
@ -104,4 +104,5 @@ public class PointSerializer extends JsonSerializer<Point> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,18 +1,17 @@
|
||||
package com.cdzy.operations.handler;
|
||||
|
||||
import com.cdzy.common.utils.CoordinateUtil;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.TypeHandler;
|
||||
import org.locationtech.jts.geom.*;
|
||||
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 org.postgresql.util.PGobject;
|
||||
|
||||
import java.sql.*;
|
||||
|
||||
/**
|
||||
* JTS Point 类型处理器 - 用于 PostgreSQL PostGIS 的 Point 类型
|
||||
* 直接存储和读取 WGS-84 坐标系坐标
|
||||
* @author attiya
|
||||
*/
|
||||
public class PointTypeHandler implements TypeHandler<Point> {
|
||||
|
||||
private static final int WGS84_SRID = 4326;
|
||||
@ -25,19 +24,18 @@ public class PointTypeHandler implements TypeHandler<Point> {
|
||||
ps.setNull(i, Types.OTHER);
|
||||
} else {
|
||||
try {
|
||||
// 创建 PGobject 并设置 PostGIS 类型
|
||||
// 写入时:GCJ-02 → WGS-84
|
||||
Point wgs84Point = gcj02ToWgs84(parameter);
|
||||
|
||||
PGobject pgObject = new PGobject();
|
||||
pgObject.setType("geometry");
|
||||
|
||||
// 使用 WKT 格式,设置正确的 SRID
|
||||
parameter.setSRID(WGS84_SRID);
|
||||
String wkt = parameter.toText();
|
||||
String wktWithSRID = "SRID=" + WGS84_SRID + ";" + wkt;
|
||||
pgObject.setValue(wktWithSRID);
|
||||
String wkt = "SRID=" + WGS84_SRID + ";" + wgs84Point.toText();
|
||||
pgObject.setValue(wkt);
|
||||
|
||||
ps.setObject(i, pgObject);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Point 设置参数失败: " + e.getMessage());
|
||||
System.err.println("Point 坐标转换失败: " + e.getMessage());
|
||||
ps.setNull(i, Types.OTHER);
|
||||
}
|
||||
}
|
||||
@ -46,62 +44,37 @@ public class PointTypeHandler implements TypeHandler<Point> {
|
||||
@Override
|
||||
public Point getResult(ResultSet rs, String columnName) throws SQLException {
|
||||
Object object = rs.getObject(columnName);
|
||||
return convertToPoint(object);
|
||||
Point wgs84Point = convertToPoint(object);
|
||||
return wgs84ToGcj02(wgs84Point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
Object object = rs.getObject(columnIndex);
|
||||
return convertToPoint(object);
|
||||
Point wgs84Point = convertToPoint(object);
|
||||
return wgs84ToGcj02(wgs84Point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
Object object = cs.getObject(columnIndex);
|
||||
return convertToPoint(object);
|
||||
Point wgs84Point = convertToPoint(object);
|
||||
return wgs84ToGcj02(wgs84Point);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据库对象转换为 Point
|
||||
*/
|
||||
private Point convertToPoint(Object object) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
if (object == null) return null;
|
||||
|
||||
try {
|
||||
if (object instanceof PGobject pgObject) {
|
||||
return parseFromPGobject(pgObject);
|
||||
} else if (object instanceof String str) {
|
||||
return parseFromString(str);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("转换 Point 失败: " + e.getMessage() +
|
||||
", 对象类型: " + object.getClass().getName());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 PGobject 解析 Point
|
||||
*/
|
||||
private Point parseFromPGobject(PGobject pgObject) throws Exception {
|
||||
if (pgObject == null || pgObject.getValue() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String value = pgObject.getValue().trim();
|
||||
String value = pgObject.getValue();
|
||||
if (value == null) return null;
|
||||
|
||||
// 解析 WKT 格式:SRID=4326;POINT(lng lat)
|
||||
if (value.contains("POINT")) {
|
||||
String wkt = value;
|
||||
// 去除 SRID 前缀
|
||||
if (value.startsWith("SRID=")) {
|
||||
int semicolonIndex = value.indexOf(';');
|
||||
if (semicolonIndex > 0) {
|
||||
wkt = value.substring(semicolonIndex + 1);
|
||||
}
|
||||
wkt = value.substring(value.indexOf(';') + 1);
|
||||
}
|
||||
|
||||
WKTReader reader = new WKTReader(geometryFactory);
|
||||
@ -111,55 +84,60 @@ public class PointTypeHandler implements TypeHandler<Point> {
|
||||
return point;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字符串解析 Point
|
||||
*/
|
||||
private Point parseFromString(String str) throws Exception {
|
||||
if (str == null || str.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
str = str.trim();
|
||||
|
||||
// 尝试解析 WKT 格式
|
||||
} else if (object instanceof String str) {
|
||||
WKTReader reader = new WKTReader(geometryFactory);
|
||||
Geometry geometry = reader.read(str);
|
||||
if (geometry instanceof Point point) {
|
||||
point.setSRID(WGS84_SRID);
|
||||
return point;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("转换 Point 失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置多边形到 PreparedStatement(辅助方法)
|
||||
*/
|
||||
private void setPointToStatement(PreparedStatement ps, int i, Point point)
|
||||
throws SQLException {
|
||||
if (point == null) {
|
||||
ps.setNull(i, Types.OTHER);
|
||||
return;
|
||||
private Point gcj02ToWgs84(Point gcj02Point) {
|
||||
if (gcj02Point == null || gcj02Point.isEmpty()) {
|
||||
return gcj02Point;
|
||||
}
|
||||
|
||||
try {
|
||||
PGobject pgObject = new PGobject();
|
||||
pgObject.setType("geometry");
|
||||
Coordinate coord = gcj02Point.getCoordinate();
|
||||
double[] wgs84 = CoordinateUtil.GCJ02ToWGS84(coord.x, coord.y);
|
||||
|
||||
// 使用 WKT 格式,设置正确的 SRID
|
||||
point.setSRID(WGS84_SRID);
|
||||
String wkt = point.toText();
|
||||
String wktWithSRID = "SRID=" + WGS84_SRID + ";" + wkt;
|
||||
pgObject.setValue(wktWithSRID);
|
||||
Point wgs84Point = geometryFactory.createPoint(
|
||||
new Coordinate(wgs84[0], wgs84[1])
|
||||
);
|
||||
wgs84Point.setSRID(WGS84_SRID);
|
||||
|
||||
ps.setObject(i, pgObject);
|
||||
return wgs84Point;
|
||||
} catch (Exception e) {
|
||||
System.err.println("设置 Point 失败: " + e.getMessage());
|
||||
ps.setNull(i, Types.OTHER);
|
||||
System.err.println("GCJ-02 → WGS-84 点坐标转换失败: " + e.getMessage());
|
||||
gcj02Point.setSRID(WGS84_SRID);
|
||||
return gcj02Point;
|
||||
}
|
||||
}
|
||||
|
||||
private Point wgs84ToGcj02(Point wgs84Point) {
|
||||
if (wgs84Point == null || wgs84Point.isEmpty()) {
|
||||
return wgs84Point;
|
||||
}
|
||||
|
||||
try {
|
||||
Coordinate coord = wgs84Point.getCoordinate();
|
||||
double[] gcj02 = CoordinateUtil.WGS84ToGCJ02(coord.x, coord.y);
|
||||
|
||||
Point gcj02Point = geometryFactory.createPoint(
|
||||
new Coordinate(gcj02[0], gcj02[1])
|
||||
);
|
||||
gcj02Point.setSRID(WGS84_SRID);
|
||||
|
||||
return gcj02Point;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("WGS-84 → GCJ-02 点坐标转换失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -129,6 +129,7 @@ public class PolygonSerializer extends JsonSerializer<Polygon> {
|
||||
try {
|
||||
String wkt = wktWriter.write(polygon);
|
||||
|
||||
// 转换为与 PGpolygon 相似的格式
|
||||
if (wkt.startsWith("POLYGON")) {
|
||||
// 提取坐标部分
|
||||
String coordsPart = wkt.substring(wkt.indexOf("((") + 2, wkt.lastIndexOf("))"));
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package com.cdzy.operations.handler;
|
||||
|
||||
import com.cdzy.common.ex.EbikeException;
|
||||
import com.cdzy.common.utils.CoordinateUtil;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.TypeHandler;
|
||||
import org.locationtech.jts.geom.*;
|
||||
@ -13,8 +15,10 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JTS Polygon 类型处理器 - 用于 PostgreSQL PostGIS 的 Polygon 类型
|
||||
* 直接存储和读取 WGS-84 坐标系的多边形
|
||||
* JTS Polygon 类型处理器 - 集成 CoordinateUtil 实现坐标转换
|
||||
* 写入时:GCJ-02 → WGS-84
|
||||
* 读取时:WGS-84 → GCJ-02
|
||||
* 支持 PostgreSQL + PostGIS 的 geometry(Polygon, 4326) 类型
|
||||
*/
|
||||
public class PolygonTypeHandler implements TypeHandler<Polygon> {
|
||||
|
||||
@ -30,40 +34,52 @@ public class PolygonTypeHandler implements TypeHandler<Polygon> {
|
||||
ps.setNull(i, Types.OTHER);
|
||||
} else {
|
||||
try {
|
||||
// 写入数据库时:GCJ-02 → WGS-84
|
||||
Polygon wgs84Polygon = gcj02ToWgs84(parameter);
|
||||
if (wgs84Polygon != null) {
|
||||
// 创建 PGobject 并设置 PostGIS 类型
|
||||
PGobject pgObject = new PGobject();
|
||||
pgObject.setType("geometry");
|
||||
|
||||
// 使用 WKT 格式,设置正确的 SRID
|
||||
parameter.setSRID(WGS84_SRID);
|
||||
String wkt = parameter.toText();
|
||||
String wkt = wgs84Polygon.toText();
|
||||
String wktWithSRID = "SRID=" + WGS84_SRID + ";" + wkt;
|
||||
pgObject.setValue(wktWithSRID);
|
||||
|
||||
ps.setObject(i, pgObject);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Polygon 设置参数失败: " + e.getMessage());
|
||||
} else {
|
||||
ps.setNull(i, Types.OTHER);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 如果转换失败,直接使用原始值(不带坐标转换)
|
||||
System.err.println("坐标转换失败,使用原始值: " + e.getMessage());
|
||||
setPolygonWithoutConversion(ps, i, parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Polygon getResult(ResultSet rs, String columnName) throws SQLException {
|
||||
Object object = rs.getObject(columnName);
|
||||
return convertToPolygon(object);
|
||||
Polygon wgs84Polygon = convertToPolygon(object);
|
||||
// 读取时:WGS-84 → GCJ-02
|
||||
return wgs84ToGcj02(wgs84Polygon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Polygon getResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
Object object = rs.getObject(columnIndex);
|
||||
return convertToPolygon(object);
|
||||
Polygon wgs84Polygon = convertToPolygon(object);
|
||||
// 读取时:WGS-84 → GCJ-02
|
||||
return wgs84ToGcj02(wgs84Polygon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Polygon getResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
Object object = cs.getObject(columnIndex);
|
||||
return convertToPolygon(object);
|
||||
Polygon wgs84Polygon = convertToPolygon(object);
|
||||
// 读取时:WGS-84 → GCJ-02
|
||||
return wgs84ToGcj02(wgs84Polygon);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,7 +195,151 @@ public class PolygonTypeHandler implements TypeHandler<Polygon> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置多边形到 PreparedStatement(辅助方法)
|
||||
* GCJ-02 转 WGS-84(写入数据库时调用)
|
||||
*/
|
||||
private Polygon gcj02ToWgs84(Polygon gcj02Polygon) {
|
||||
if (gcj02Polygon == null || gcj02Polygon.isEmpty()) {
|
||||
return gcj02Polygon;
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取多边形外环坐标
|
||||
Coordinate[] coordinates = gcj02Polygon.getExteriorRing().getCoordinates();
|
||||
if (coordinates == null || coordinates.length == 0) {
|
||||
return gcj02Polygon;
|
||||
}
|
||||
|
||||
List<Coordinate> wgs84Coordinates = new ArrayList<>();
|
||||
|
||||
// 对每个坐标点进行转换
|
||||
for (Coordinate gcj02Coord : coordinates) {
|
||||
double[] wgs84 = CoordinateUtil.GCJ02ToWGS84(gcj02Coord.x, gcj02Coord.y);
|
||||
wgs84Coordinates.add(new Coordinate(wgs84[0], wgs84[1]));
|
||||
}
|
||||
|
||||
// 确保多边形闭合
|
||||
Coordinate first = wgs84Coordinates.get(0);
|
||||
Coordinate last = wgs84Coordinates.get(wgs84Coordinates.size() - 1);
|
||||
if (!first.equals2D(last)) {
|
||||
wgs84Coordinates.add(new Coordinate(first.x, first.y));
|
||||
}
|
||||
|
||||
// 创建 WGS-84 多边形
|
||||
Coordinate[] wgs84Array = wgs84Coordinates.toArray(new Coordinate[0]);
|
||||
LinearRing shell = geometryFactory.createLinearRing(wgs84Array);
|
||||
|
||||
// 如果有孔洞,也需要转换
|
||||
int numHoles = gcj02Polygon.getNumInteriorRing();
|
||||
LinearRing[] holes = new LinearRing[numHoles];
|
||||
|
||||
for (int i = 0; i < numHoles; i++) {
|
||||
LineString holeRing = gcj02Polygon.getInteriorRingN(i);
|
||||
Coordinate[] holeCoords = holeRing.getCoordinates();
|
||||
List<Coordinate> wgs84HoleCoords = new ArrayList<>();
|
||||
|
||||
for (Coordinate holeCoord : holeCoords) {
|
||||
double[] wgs84Hole = CoordinateUtil.GCJ02ToWGS84(holeCoord.x, holeCoord.y);
|
||||
wgs84HoleCoords.add(new Coordinate(wgs84Hole[0], wgs84Hole[1]));
|
||||
}
|
||||
|
||||
// 确保孔洞闭合
|
||||
Coordinate holeFirst = wgs84HoleCoords.get(0);
|
||||
Coordinate holeLast = wgs84HoleCoords.get(wgs84HoleCoords.size() - 1);
|
||||
if (!holeFirst.equals2D(holeLast)) {
|
||||
wgs84HoleCoords.add(new Coordinate(holeFirst.x, holeFirst.y));
|
||||
}
|
||||
|
||||
holes[i] = geometryFactory.createLinearRing(
|
||||
wgs84HoleCoords.toArray(new Coordinate[0])
|
||||
);
|
||||
}
|
||||
|
||||
Polygon wgs84Polygon = geometryFactory.createPolygon(shell, holes);
|
||||
wgs84Polygon.setSRID(WGS84_SRID);
|
||||
|
||||
System.out.println("GCJ-02 → WGS-84 转换成功,点数: " + wgs84Coordinates.size());
|
||||
return wgs84Polygon;
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("GCJ-02 → WGS-84 坐标系转换错误: " + e.getMessage());
|
||||
// 转换失败时,返回原始多边形但设置正确的 SRID
|
||||
gcj02Polygon.setSRID(WGS84_SRID);
|
||||
return gcj02Polygon;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WGS-84 转 GCJ-02(从数据库读取时调用)
|
||||
*/
|
||||
private Polygon wgs84ToGcj02(Polygon wgs84Polygon) {
|
||||
if (wgs84Polygon == null || wgs84Polygon.isEmpty()) {
|
||||
return wgs84Polygon;
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取多边形外环坐标
|
||||
Coordinate[] coordinates = wgs84Polygon.getExteriorRing().getCoordinates();
|
||||
if (coordinates == null || coordinates.length == 0) {
|
||||
return wgs84Polygon;
|
||||
}
|
||||
|
||||
List<Coordinate> gcj02Coordinates = new ArrayList<>();
|
||||
|
||||
// 对每个坐标点进行转换
|
||||
for (Coordinate wgs84Coord : coordinates) {
|
||||
double[] gcj02 = CoordinateUtil.WGS84ToGCJ02(wgs84Coord.x, wgs84Coord.y);
|
||||
gcj02Coordinates.add(new Coordinate(gcj02[0], gcj02[1]));
|
||||
}
|
||||
|
||||
// 确保多边形闭合
|
||||
Coordinate first = gcj02Coordinates.get(0);
|
||||
Coordinate last = gcj02Coordinates.get(gcj02Coordinates.size() - 1);
|
||||
if (!first.equals2D(last)) {
|
||||
gcj02Coordinates.add(new Coordinate(first.x, first.y));
|
||||
}
|
||||
|
||||
// 创建 GCJ-02 多边形
|
||||
Coordinate[] gcj02Array = gcj02Coordinates.toArray(new Coordinate[0]);
|
||||
LinearRing shell = geometryFactory.createLinearRing(gcj02Array);
|
||||
|
||||
// 如果有孔洞,也需要转换
|
||||
int numHoles = wgs84Polygon.getNumInteriorRing();
|
||||
LinearRing[] holes = new LinearRing[numHoles];
|
||||
|
||||
for (int i = 0; i < numHoles; i++) {
|
||||
LineString holeRing = wgs84Polygon.getInteriorRingN(i);
|
||||
Coordinate[] holeCoords = holeRing.getCoordinates();
|
||||
List<Coordinate> gcj02HoleCoords = new ArrayList<>();
|
||||
|
||||
for (Coordinate holeCoord : holeCoords) {
|
||||
double[] gcj02Hole = CoordinateUtil.WGS84ToGCJ02(holeCoord.x, holeCoord.y);
|
||||
gcj02HoleCoords.add(new Coordinate(gcj02Hole[0], gcj02Hole[1]));
|
||||
}
|
||||
|
||||
// 确保孔洞闭合
|
||||
Coordinate holeFirst = gcj02HoleCoords.get(0);
|
||||
Coordinate holeLast = gcj02HoleCoords.get(gcj02HoleCoords.size() - 1);
|
||||
if (!holeFirst.equals2D(holeLast)) {
|
||||
gcj02HoleCoords.add(new Coordinate(holeFirst.x, holeFirst.y));
|
||||
}
|
||||
|
||||
holes[i] = geometryFactory.createLinearRing(
|
||||
gcj02HoleCoords.toArray(new Coordinate[0])
|
||||
);
|
||||
}
|
||||
|
||||
Polygon gcj02Polygon = geometryFactory.createPolygon(shell, holes);
|
||||
gcj02Polygon.setSRID(WGS84_SRID); // 保持 SRID 不变
|
||||
|
||||
return gcj02Polygon;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new EbikeException("WGS-84 → GCJ-02 坐标系转换错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置多边形到 PreparedStatement(不进行坐标转换)
|
||||
*/
|
||||
private void setPolygonWithoutConversion(PreparedStatement ps, int i, Polygon polygon)
|
||||
throws SQLException {
|
||||
|
||||
@ -41,7 +41,7 @@ public class ReoprtHandler {
|
||||
public void reportHandler(JsonNode response) {
|
||||
int c = response.get("c").asInt();
|
||||
JsonNode param = response.get("param");
|
||||
String deviceId = param.get("SN").asText();
|
||||
String deviceId = response.get("SN").asText();
|
||||
switch (c) {
|
||||
case 56:
|
||||
gpsMsgHandler(param, deviceId);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user