
创建索引

设置字段默认值
ALTER TABLE jszj_love_pavilion
ALTER COLUMN wgs SET DEFAULT (ST_GeomFromText('POINT(0 0)', 4326));
引入依赖
<!-- JTS几何库引入数据库Point类型的支持 -->
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>1.19.0</version>
</dependency>
在实体类新建字段
//wgs84坐标系位置
@TableField(value = "wgs", typeHandler = JtsPointTypeHandler.class)
private Point wgs;
JtsPointTypeHandler.java
package com.mmwzcloud.jszj.framework.typehandler;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKBReader;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.sql.*;
@MappedTypes(Point.class)
@MappedJdbcTypes({
JdbcType.VARCHAR, // WKT文本格式
JdbcType.BINARY, // WKB二进制格式
JdbcType.BLOB, // Blob格式
JdbcType.LONGVARBINARY
})
public class JtsPointTypeHandler extends BaseTypeHandler<Point> {
private static final Logger log = LoggerFactory.getLogger(JtsPointTypeHandler.class);
// WKB格式常量
private static final int WKB_POINT_TYPE = 1;
private static final int WKB_SRID_FLAG = 0x20000000;
private static final byte WKB_NDR = 1; // 小端字节序
private static final byte WKB_XDR = 0; // 大端字节序
// 几何工厂(线程安全,可共享)
private static final GeometryFactory GEOMETRY_FACTORY =
new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING), 4326);
// 读写器(非线程安全,需要ThreadLocal或每次创建)
private static final ThreadLocal<WKTReader> WKT_READER = ThreadLocal.withInitial(
() -> new WKTReader(GEOMETRY_FACTORY)
);
private static final ThreadLocal<WKBReader> WKB_READER = ThreadLocal.withInitial(
() -> new WKBReader(GEOMETRY_FACTORY)
);
// ==================== 写入数据库 ====================
@Override
public void setNonNullParameter(PreparedStatement ps, int parameterIndex,
Point point, JdbcType jdbcType) throws SQLException {
try {
double x = point.getX(); // 经度
double y = point.getY(); // 纬度
int srid = point.getSRID() > 0 ? point.getSRID() : 4326;
byte[] standardWkb = createStandardWkb(x, y, srid);
ps.setBytes(parameterIndex, standardWkb);
} catch (Exception e) {
log.error("写入Point数据失败", e);
throw new SQLException("写入几何数据失败: " + e.getMessage(), e);
}
}
public static byte[] createStandardWkb(double lon, double lat, int srid) {
// 1. 标准 WKB 核心 21 B
ByteBuffer core = ByteBuffer.allocate(21).order(ByteOrder.LITTLE_ENDIAN);
core.put((byte) 1); // little-endian
core.putInt(1); // WKB_POINT
core.putDouble(lon); // X
core.putDouble(lat); // Y
byte[] wkbCore = core.array();
// 2. 前面拼 4 B SRID
return ByteBuffer.allocate(4 + wkbCore.length)
.order(ByteOrder.LITTLE_ENDIAN)
.putInt(srid)
.put(wkbCore)
.array();
}
// ==================== 从数据库读取 ====================
@Override
public Point getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return parseDatabaseValue(rs.getObject(columnName));
}
@Override
public Point getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return parseDatabaseValue(rs.getObject(columnIndex));
}
@Override
public Point getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return parseDatabaseValue(cs.getObject(columnIndex));
}
/**
* 智能解析数据库返回值
* 支持: byte[](WKB), String(WKT), Blob, 其他类型自动转换
*/
private Point parseDatabaseValue(Object dbValue) throws SQLException {
if (dbValue == null) {
return null;
}
try {
// 1. byte[] - WKB格式(最常见)
if (dbValue instanceof byte[]) {
return parseWkbByByteArray((byte[]) dbValue);
}
//mysql返回String类型数据
if (dbValue instanceof String) {
return null;//未实现
}
return null;
} catch (Exception e) {
throw new SQLException("解析几何数据失败", e);
}
}
private Point parseWkbByByteArray(byte[] dbValue) {
byte[] dbValue1 = (byte[]) dbValue;
// 打印调试信息
StringBuilder hex = new StringBuilder();
for (int i = 0; i < dbValue1.length; i++) {
hex.append(String.format("%02X ", dbValue1[i] & 0xFF));
}
log.debug("原始数据(hex): {}", hex.toString());
// 字节 9-16: 经度 (double)
long lngBits = 0;
// MySQL是小端字节序,但需要反向读取
for (int i = 16; i >= 9; i--) {
lngBits = (lngBits << 8) | (dbValue1[i] & 0xFF);
}
double longitude = Double.longBitsToDouble(lngBits);
// 字节 17-24: 纬度 (double)
long latBits = 0;
for (int i = 24; i >= 17; i--) {
latBits = (latBits << 8) | (dbValue1[i] & 0xFF);
}
double latitude = Double.longBitsToDouble(latBits);
log.debug("方法1解析结果: 经度={}, 纬度={}", longitude, latitude);
GeometryFactory factory = new GeometryFactory(new PrecisionModel(), 4326);
Point point = factory.createPoint(new Coordinate(longitude, latitude));
return point;
}
}
CoordinateConverter.java
package com.mmwzcloud.jszj.framework.commonjszj.utils;
public class CoordinateConverter {
private static final double PI = 3.1415926535897932384626;
private static final double A = 6378245.0; // 长半轴
private static final double EE = 0.00669342162296594323; // 扁率
/**
* GCJ-02 转 WGS-84
* @param lng 高德经度
* @param lat 高德纬度
* @return [经度, 纬度]
*/
public static double[] gcj02ToWgs84(double lng, double lat) {
if (outOfChina(lng, lat)) {
return new double[]{lng, lat};
}
double dLat = transformLat(lng - 105.0, lat - 35.0);
double dLng = transformLng(lng - 105.0, lat - 35.0);
double radLat = lat / 180.0 * PI;
double magic = Math.sin(radLat);
magic = 1 - EE * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI);
dLng = (dLng * 180.0) / (A / sqrtMagic * Math.cos(radLat) * PI);
return new double[]{lng - dLng, lat - dLat};
}
/**
* WGS-84 转 GCJ-02(返回给前端用)
*/
public static double[] wgs84ToGcj02(double lng, double lat) {
if (outOfChina(lng, lat)) {
return new double[]{lng, lat};
}
double dLat = transformLat(lng - 105.0, lat - 35.0);
double dLng = transformLng(lng - 105.0, lat - 35.0);
double radLat = lat / 180.0 * PI;
double magic = Math.sin(radLat);
magic = 1 - EE * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI);
dLng = (dLng * 180.0) / (A / sqrtMagic * Math.cos(radLat) * PI);
return new double[]{lng + dLng, lat + dLat};
}
private static double transformLat(double x, double y) {
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;
return ret;
}
private static double transformLng(double x, double y) {
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;
return ret;
}
private static boolean outOfChina(double lng, double lat) {
return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271;
}
}
高德gcj02 转 Wgs84 并保存
double[] doubles = CoordinateConverter.gcj02ToWgs84(Double.valueOf(updateReqVO.getLongitude()), Double.valueOf(updateReqVO.getLatitude()));
Point wgs84Point = CreateWgs84Point.createWgs84Point(doubles[0], doubles[1]);
updateReqVO.setWgs(wgs84Point);
查询时 Wgs84 转 gcj02
double[] doubles = CoordinateConverter.wgs84ToGcj02(x, y);
return StringUtils.join(Arrays.asList(String.valueOf(doubles[0]),String.valueOf(doubles[1])),",");