feat(geometry): 添加几何工具类和坐标转换支持
- 新增 GeometryUtil 工具类,提供几何对象创建和操作方法 - 添加 GpsConvertUtil 类,实现坐标系转换功能 - 在 EnterprisesUnit 模型中增加坐标字段- 更新相关参数和 VO 类,支持坐标信息 - 新增 PointTypeHandler 以支持 MyBatis-Plus 对 Point 类型的处理 - 在 FastJson2Config 中注册地理坐标相关的序列化和反序列化器 - 添加 GeometryInnerInterceptor 以支持 MyBatis-Plus 几何操作
This commit is contained in:
parent
18f6bd715a
commit
201f2112e8
|
@ -23,6 +23,7 @@
|
|||
<easyexcel.version>3.3.4</easyexcel.version>
|
||||
<mysql.driver.version>8.0.32</mysql.driver.version>
|
||||
<mybatis.plus.version>3.5.7</mybatis.plus.version>
|
||||
<geotools.version>25.2</geotools.version>
|
||||
<druid.version>1.2.20</druid.version>
|
||||
<minio.version>8.4.3</minio.version>
|
||||
<okhttp.version>4.8.1</okhttp.version>
|
||||
|
@ -190,6 +191,17 @@
|
|||
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||
<version>${mybatis.plus.version}</version>
|
||||
</dependency>
|
||||
<!-- 处理地理空间数据的读取、写入、转换、分析以及可视化 geotools-->
|
||||
<dependency>
|
||||
<groupId>org.geotools</groupId>
|
||||
<artifactId>gt-main</artifactId>
|
||||
<version>${geotools.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.geotools</groupId>
|
||||
<artifactId>gt-geojson</artifactId>
|
||||
<version>${geotools.version}</version>
|
||||
</dependency>
|
||||
<!-- minio对象存储 https://www.minio.org.cn/ -->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
|
|
|
@ -0,0 +1,428 @@
|
|||
package com.changhu.common.utils;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.changhu.common.exception.MessageException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.geotools.geojson.geom.GeometryJSON;
|
||||
import org.geotools.geometry.jts.JTS;
|
||||
import org.geotools.referencing.CRS;
|
||||
import org.geotools.referencing.GeodeticCalculator;
|
||||
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.opengis.referencing.FactoryException;
|
||||
import org.opengis.referencing.crs.CoordinateReferenceSystem;
|
||||
import org.opengis.referencing.operation.MathTransform;
|
||||
import org.opengis.referencing.operation.TransformException;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* author: luozhun
|
||||
* desc: GeometryUtil
|
||||
* createTime: 2023/8/25 18:03
|
||||
*/
|
||||
@Slf4j
|
||||
public class GeometryUtil {
|
||||
|
||||
/**
|
||||
* - WGS 84(srid=4326):用于全球地理坐标系统的标准参考坐标系。
|
||||
* - 美国地理参考系统(srid=4269):用于美国本土的地图和地理数据。
|
||||
* - 欧洲地理参考系统(srid=4258):用于欧洲地图和地理数据。
|
||||
* - 中国国家大地坐标系(srid=4490):用于中国的地图和地理数据。
|
||||
*/
|
||||
public static final int SRID = 4326;
|
||||
public static final String ST_GeomFromText = StrUtil.format("ST_GeomFromText({},{},{})", "?", SRID, "'axis-order=long-lat'");
|
||||
private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory(new PrecisionModel(), SRID);
|
||||
private static final GeometryFactory LOCATION_TECH_GEOMETRY_FACTORY = new GeometryFactory(new PrecisionModel(), SRID);
|
||||
|
||||
/**
|
||||
* 空对象,因为简历空间索引不能为空 Mysql 不支持创建 Empty 的数据 这里自定义一个类型 标识为null
|
||||
*/
|
||||
public static Point emptyPoint() {
|
||||
return createPoint("POINT(0 0)");
|
||||
}
|
||||
|
||||
public static Polygon emptyPolygon() {
|
||||
return createPolygon("POLYGON((0 0, 0 0, 0 0, 0 0))");
|
||||
}
|
||||
|
||||
public static LineString emptyLineString() {
|
||||
return createLineString("LINESTRING(0 0, 0 0)");
|
||||
}
|
||||
|
||||
public static MultiPoint emptyMultiPoint() {
|
||||
return createMultiPoint("MULTIPOINT((0 0))");
|
||||
}
|
||||
|
||||
public static MultiPolygon emptyMultiPolygon() {
|
||||
return createMultiPolygon("MULTIPOLYGON(((0 0, 0 0, 0 0, 0 0)))");
|
||||
}
|
||||
|
||||
public static MultiLineString emptyMultiLineString() {
|
||||
return createMultiLineString("MULTILINESTRING(((0 0, 0 0)))");
|
||||
}
|
||||
|
||||
|
||||
public static boolean equals(Geometry geometry1, Geometry geometry2) {
|
||||
return StrUtil.equals(geometry1.toText(), geometry2.toText());
|
||||
}
|
||||
|
||||
//** 点 **\\
|
||||
|
||||
/**
|
||||
* 创建点对象
|
||||
*/
|
||||
public static Point createPoint(List<BigDecimal> param) {
|
||||
if (param == null) {
|
||||
return null;
|
||||
}
|
||||
BigDecimal longitude = param.get(0);
|
||||
BigDecimal latitude = param.get(1);
|
||||
if (longitude == null || latitude == null) {
|
||||
return null;
|
||||
}
|
||||
Coordinate coordinate = new Coordinate(longitude.doubleValue(), latitude.doubleValue());
|
||||
return GEOMETRY_FACTORY.createPoint(coordinate);
|
||||
}
|
||||
|
||||
public static Point createPoint(double longitude, double latitude) {
|
||||
Coordinate coordinate = new Coordinate(longitude, latitude);
|
||||
return GEOMETRY_FACTORY.createPoint(coordinate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从WKT创建点对象
|
||||
*
|
||||
* @param wkt 注WKT类似: POINT(109.013388 32.715519)
|
||||
*/
|
||||
public static Point createPoint(String wkt) {
|
||||
return createGeometryFromWkt(wkt, Point.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从WKT创建点对象
|
||||
*
|
||||
* @param bytes 数据库二进制数据
|
||||
*/
|
||||
public static Point createPoint(byte[] bytes) {
|
||||
return createGeometryFromBytes(bytes, Point.class);
|
||||
}
|
||||
|
||||
|
||||
//** 多边形 **\\
|
||||
|
||||
/**
|
||||
* 创建多边形对象
|
||||
* <p>
|
||||
* 注:后面可能考虑兼容折线类型,如果是折线类型那么需要规范首尾结点要相同才能构成 polygon 否则构成 polyline
|
||||
* 目前暂时没有做这方面的规划,用代码兼容了首尾不相等的情况构成polygon
|
||||
*/
|
||||
public static Polygon createPolygon(List<List<BigDecimal>> params) {
|
||||
if (params == null || params.size() < 4) {
|
||||
throw new MessageException("构成多边形至少需要三个有效坐标,并且保证尾部坐标和首部坐标相同");
|
||||
}
|
||||
// 检测首尾是否相等,不相等则补充尾坐标
|
||||
List<BigDecimal> startPoint = params.get(0);
|
||||
List<BigDecimal> entPoint = params.get(params.size() - 1);
|
||||
if (!Objects.equals(startPoint.get(0), entPoint.get(0))
|
||||
|| !Objects.equals(startPoint.get(1), entPoint.get(1))) {
|
||||
params.add(params.get(0));
|
||||
}
|
||||
Coordinate[] coordinates = convertBigDecimalList2CoordinateArray(params);
|
||||
return GEOMETRY_FACTORY.createPolygon(coordinates);
|
||||
}
|
||||
|
||||
public static Polygon createPolygonWithDouble(List<List<Double>> params) {
|
||||
if (params == null || params.size() < 4) {
|
||||
throw new MessageException("构成多边形至少需要三个有效坐标,并且保证尾部坐标和首部坐标相同");
|
||||
}
|
||||
// 检测首尾是否相等,不相等则补充尾坐标
|
||||
List<Double> startPoint = params.get(0);
|
||||
List<Double> entPoint = params.get(params.size() - 1);
|
||||
if (!Objects.equals(startPoint.get(0), entPoint.get(0))
|
||||
|| !Objects.equals(startPoint.get(1), entPoint.get(1))) {
|
||||
params.add(params.get(0));
|
||||
}
|
||||
Coordinate[] coordinates = convertDoubleList2CoordinateArray(params);
|
||||
return GEOMETRY_FACTORY.createPolygon(coordinates);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从WKT创建多边形对象
|
||||
*
|
||||
* @param wkt 注WKT类似:POLYGON((20 10, 30 0, 40 10, 30 20, 20 10))
|
||||
*/
|
||||
public static Polygon createPolygon(String wkt) {
|
||||
return createGeometryFromWkt(wkt, Polygon.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建多边形对象
|
||||
*
|
||||
* @param bytes 数据库二进制数据
|
||||
*/
|
||||
public static Polygon createPolygon(byte[] bytes) {
|
||||
return createGeometryFromBytes(bytes, Polygon.class);
|
||||
}
|
||||
|
||||
//** 折线 **\\
|
||||
|
||||
/**
|
||||
* 创建折线对象
|
||||
*/
|
||||
public static LineString createLineString(List<List<BigDecimal>> params) {
|
||||
if (params == null || params.size() < 2) {
|
||||
throw new MessageException("构成折线至少需要两个有效坐标");
|
||||
}
|
||||
Coordinate[] coordinates = convertBigDecimalList2CoordinateArray(params);
|
||||
return GEOMETRY_FACTORY.createLineString(coordinates);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 wkt 创建折线对象
|
||||
*/
|
||||
public static LineString createLineString(String wkt) {
|
||||
return createGeometryFromWkt(wkt, LineString.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 bytes 创建折线对象
|
||||
*/
|
||||
public static LineString createLineString(byte[] bytes) {
|
||||
return createGeometryFromBytes(bytes, LineString.class);
|
||||
}
|
||||
|
||||
|
||||
//** 多点 **\\
|
||||
|
||||
/**
|
||||
* 创建多点对象
|
||||
*/
|
||||
public static MultiPoint createMultiPoint(List<List<BigDecimal>> multiParam) {
|
||||
int size = multiParam.size();
|
||||
Point[] points = new Point[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
points[i] = createPoint(multiParam.get(i));
|
||||
}
|
||||
return GEOMETRY_FACTORY.createMultiPoint(points);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从 wkt 创建多点对象
|
||||
*/
|
||||
public static MultiPoint createMultiPoint(String wkt) {
|
||||
return createGeometryFromWkt(wkt, MultiPoint.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从 bytes 创建多点对象
|
||||
*/
|
||||
public static MultiPoint createMultiPoint(byte[] bytes) {
|
||||
return createGeometryFromBytes(bytes, MultiPoint.class);
|
||||
}
|
||||
|
||||
|
||||
//** 多折线 **\\
|
||||
|
||||
/**
|
||||
* 创建多折线对象
|
||||
*/
|
||||
public static MultiLineString createMultiLineString(List<List<List<BigDecimal>>> multiParams) {
|
||||
int size = multiParams.size();
|
||||
LineString[] lineStrings = new LineString[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
lineStrings[i] = createLineString(multiParams.get(i));
|
||||
}
|
||||
return GEOMETRY_FACTORY.createMultiLineString(lineStrings);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 wkt 创建多折线对象
|
||||
*/
|
||||
public static MultiLineString createMultiLineString(String wkt) {
|
||||
return createGeometryFromWkt(wkt, MultiLineString.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 wkt 创建多折线对象
|
||||
*/
|
||||
public static MultiLineString createMultiLineString(byte[] bytes) {
|
||||
return createGeometryFromBytes(bytes, MultiLineString.class);
|
||||
}
|
||||
|
||||
//** 多多边形 **\\
|
||||
|
||||
/**
|
||||
* 创建多多边形
|
||||
*/
|
||||
public static MultiPolygon createMultiPolygon(List<List<List<BigDecimal>>> multiParams) {
|
||||
int size = multiParams.size();
|
||||
Polygon[] polygons = new Polygon[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
polygons[i] = createPolygon(multiParams.get(i));
|
||||
}
|
||||
return GEOMETRY_FACTORY.createMultiPolygon(polygons);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 wkt 创建多多边形
|
||||
*/
|
||||
public static MultiPolygon createMultiPolygon(String wkt) {
|
||||
return createGeometryFromWkt(wkt, MultiPolygon.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 bytes 创建多多边形
|
||||
*/
|
||||
public static MultiPolygon createMultiPolygon(byte[] bytes) {
|
||||
return createGeometryFromBytes(bytes, MultiPolygon.class);
|
||||
}
|
||||
|
||||
//** 通用工具 **\\
|
||||
|
||||
|
||||
/**
|
||||
* bytes 创建空间对象
|
||||
*
|
||||
* @param bytes 数据库的二进制数据
|
||||
*/
|
||||
public static <T extends Geometry> T createGeometryFromBytes(byte[] bytes, Class<T> clazz) {
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
WKBReader wkbReader = new WKBReader(GEOMETRY_FACTORY);
|
||||
byte[] geomBytes = ByteBuffer.allocate(bytes.length - 4).order(ByteOrder.LITTLE_ENDIAN)
|
||||
.put(bytes, 4, bytes.length - 4).array();
|
||||
Geometry geometry = wkbReader.read(geomBytes);
|
||||
return Convert.convert(clazz, geometry);
|
||||
} catch (Exception e) {
|
||||
throw new MessageException("从 Bytes 创建空间对象 {} 时出现错误:{}", clazz.getSimpleName(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 wkt 创建空间对象
|
||||
*
|
||||
* @param wkt 类似:POLYGON((20 10, 30 0, 40 10, 30 20, 20 10)) , POINT(109.013388 32.715519) 等
|
||||
* @param clazz 继承
|
||||
*/
|
||||
public static <T extends Geometry> T createGeometryFromWkt(String wkt, Class<T> clazz) {
|
||||
if (wkt == null || StrUtil.isBlank(wkt)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
WKTReader reader = new WKTReader(GEOMETRY_FACTORY);
|
||||
Geometry geometry = reader.read(wkt);
|
||||
return Convert.convert(clazz, geometry);
|
||||
} catch (ParseException e) {
|
||||
throw new MessageException("从WKT创建空间对象 {} 时出现错误。wkt:{}", clazz.getSimpleName(), wkt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 GeoJson 创建空间对象
|
||||
*/
|
||||
public static <T extends Geometry> T createGeometryFromGeoJson(String geoJson, Class<T> clazz) {
|
||||
try {
|
||||
GeometryJSON geometryJson = new GeometryJSON(20);
|
||||
Geometry read = geometryJson.read(new StringReader(geoJson));
|
||||
return createGeometryFromWkt(read.toString(), clazz);
|
||||
} catch (IOException e) {
|
||||
log.error("从 GeoJson 创建空间对象出错:{}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 List<LngLatParam> 转化为 Coordinate[]
|
||||
*/
|
||||
private static Coordinate[] convertBigDecimalList2CoordinateArray(List<List<BigDecimal>> params) {
|
||||
int size = params.size();
|
||||
Coordinate[] coordinates = new Coordinate[size];
|
||||
for (int i = 0; i < params.size(); i++) {
|
||||
List<BigDecimal> param = params.get(i);
|
||||
Coordinate coordinate = new Coordinate(param.get(0).doubleValue(), param.get(1).doubleValue());
|
||||
coordinates[i] = coordinate;
|
||||
}
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
private static Coordinate[] convertDoubleList2CoordinateArray(List<List<Double>> params) {
|
||||
int size = params.size();
|
||||
Coordinate[] coordinates = new Coordinate[size];
|
||||
for (int i = 0; i < params.size(); i++) {
|
||||
List<Double> param = params.get(i);
|
||||
Coordinate coordinate = new Coordinate(param.get(0), param.get(1));
|
||||
coordinates[i] = coordinate;
|
||||
}
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算面积 平方米
|
||||
* 28155 .195928.042862
|
||||
*/
|
||||
public static BigDecimal getApproximateArea(Geometry geometry) {
|
||||
try {
|
||||
String wkt = geometry.toText();
|
||||
WKTReader wktReader = new WKTReader(LOCATION_TECH_GEOMETRY_FACTORY);
|
||||
Geometry geom = wktReader.read(wkt);
|
||||
// WGS84(一般项目中常用的是CSR:84和EPSG:4326)
|
||||
CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:4326");
|
||||
// Pseudo-Mercator(墨卡托投影)
|
||||
CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4490");
|
||||
MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, true);
|
||||
Geometry geometryMercator = JTS.transform(geom, transform);
|
||||
// 面积、周长
|
||||
return BigDecimal.valueOf(geometryMercator.getArea());
|
||||
} catch (FactoryException | TransformException | ParseException e) {
|
||||
log.error("计算面积出错:{}", e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前数据库的数据来源都是高德数据源 坐标系为GCJ02 如果来源是其他的地图的需要根据规则转换成GCJ02
|
||||
* 同理如果需要转换到其他的数据源的坐标系也需要进行转换
|
||||
*/
|
||||
public static <T extends Geometry> T convertWgs84ToGcj02(T geometry) {
|
||||
log.info("原始数据:{}", geometry.toText());
|
||||
Coordinate[] coordinates = geometry.getCoordinates();
|
||||
for (Coordinate coordinate : coordinates) {
|
||||
double lon = coordinate.x;
|
||||
double lat = coordinate.y;
|
||||
double[] doubles = GpsConvertUtil.wgs84ToGcj02(lon, lat);
|
||||
coordinate.x = doubles[0];
|
||||
coordinate.y = doubles[1];
|
||||
}
|
||||
log.info("转后数据:{}", geometry.toText());
|
||||
return geometry;
|
||||
}
|
||||
|
||||
public static double distance(Point p0, Point p1) {
|
||||
GeodeticCalculator calculator = new GeodeticCalculator();
|
||||
calculator.setStartingGeographicPoint(p0.getX(), p0.getY());
|
||||
calculator.setDestinationGeographicPoint(p1.getX(), p1.getY());
|
||||
return calculator.getOrthodromicDistance();
|
||||
}
|
||||
|
||||
public static Point2D calculateEndingGlobalCoordinates(double longitude, double latitude, double azi, double dis) {
|
||||
GeodeticCalculator calculator = new GeodeticCalculator();
|
||||
calculator.setStartingGeographicPoint(longitude, latitude);
|
||||
calculator.setDirection(azi, dis);
|
||||
return calculator.getDestinationGeographicPoint();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
package com.changhu.common.utils;
|
||||
|
||||
/**
|
||||
* author: luozhun
|
||||
* desc: 坐标系转换
|
||||
* createTime: 2023/8/25 18:18
|
||||
*/
|
||||
public class GpsConvertUtil {
|
||||
public static final double pi = 3.1415926535897932384626;
|
||||
public static final double xpi = 3.14159265358979324 * 3000.0 / 180.0;
|
||||
public static final double a = 6378245.0;
|
||||
public static final double ee = 0.00669342162296594323;
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
public static double transformLon(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;
|
||||
}
|
||||
|
||||
public static double[] transform(double lon, double lat) {
|
||||
if (outOfChina(lon, lat)) {
|
||||
return new double[]{lon, lat};
|
||||
}
|
||||
double dLat = transformLat(lon - 105.0, lat - 35.0);
|
||||
double dLon = transformLon(lon - 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);
|
||||
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
|
||||
double mgLat = lat + dLat;
|
||||
double mgLon = lon + dLon;
|
||||
return new double[]{mgLon, mgLat};
|
||||
}
|
||||
|
||||
public static boolean outOfChina(double lon, double lat) {
|
||||
if (lon < 72.004 || lon > 137.8347)
|
||||
return true;
|
||||
return lat < 0.8293 || lat > 55.8271;
|
||||
}
|
||||
|
||||
/**
|
||||
* 84 to 火星坐标系 (GCJ-02) World Geodetic System ==> Mars Geodetic System
|
||||
*/
|
||||
public static double[] wgs84ToGcj02(double lon, double lat) {
|
||||
if (outOfChina(lon, lat)) {
|
||||
return new double[]{lon, lat};
|
||||
}
|
||||
double dLat = transformLat(lon - 105.0, lat - 35.0);
|
||||
double dLon = transformLon(lon - 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);
|
||||
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
|
||||
double mgLat = lat + dLat;
|
||||
double mgLon = lon + dLon;
|
||||
return new double[]{mgLon, mgLat};
|
||||
}
|
||||
|
||||
/**
|
||||
* * 火星坐标系 (GCJ-02) to 84 * * @param lon * @param lat * @return
|
||||
*/
|
||||
public static double[] gcj02ToWgs84(double lon, double lat) {
|
||||
double[] gps = transform(lon, lat);
|
||||
double longitude = lon * 2 - gps[0];
|
||||
double latitude = lat * 2 - gps[1];
|
||||
return new double[]{longitude, latitude};
|
||||
}
|
||||
|
||||
/**
|
||||
* 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 将 GCJ-02 坐标转换成 BD-09 坐标
|
||||
*/
|
||||
public static double[] gcj02ToBd09(double lon, double lat) {
|
||||
double z = Math.sqrt(lon * lon + lat * lat) + 0.00002 * Math.sin(lat * xpi);
|
||||
double theta = Math.atan2(lat, lon) + 0.000003 * Math.cos(lon * xpi);
|
||||
double tempLon = z * Math.cos(theta) + 0.0065;
|
||||
double tempLat = z * Math.sin(theta) + 0.006;
|
||||
return new double[]{tempLon, tempLat};
|
||||
}
|
||||
|
||||
/**
|
||||
* * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换算法 * * 将 BD-09 坐标转换成GCJ-02 坐标 * * @param
|
||||
* bdlat * @param bdlon * @return
|
||||
*/
|
||||
public static double[] bd09ToGcj02(double lon, double lat) {
|
||||
double x = lon - 0.0065, y = lat - 0.006;
|
||||
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * xpi);
|
||||
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * xpi);
|
||||
double tempLon = z * Math.cos(theta);
|
||||
double tempLat = z * Math.sin(theta);
|
||||
return new double[]{tempLon, tempLat};
|
||||
}
|
||||
|
||||
/**
|
||||
* 将wgs84转为bd09
|
||||
*/
|
||||
public static double[] wgs84ToBd09(double lon, double lat) {
|
||||
double[] gcj02 = wgs84ToGcj02(lon, lat);
|
||||
return gcj02ToBd09(gcj02[0], gcj02[1]);
|
||||
}
|
||||
|
||||
public static double[] bd09ToWgs84(double lon, double lat) {
|
||||
double[] gcj02 = bd09ToGcj02(lon, lat);
|
||||
double[] wgs84 = gcj02ToWgs84(gcj02[0], gcj02[1]);
|
||||
// 保留小数点后六位
|
||||
wgs84[1] = retain6(wgs84[1]);
|
||||
wgs84[0] = retain6(wgs84[0]);
|
||||
return wgs84;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保留小数点后六位
|
||||
*/
|
||||
private static double retain6(double num) {
|
||||
String result = String.format("%.6f", num);
|
||||
return Double.parseDouble(result);
|
||||
}
|
||||
|
||||
}
|
|
@ -11,6 +11,7 @@ import lombok.Data;
|
|||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
@ -73,6 +74,11 @@ public class EnterprisesUnit extends BaseEntity implements Serializable {
|
|||
*/
|
||||
private String address;
|
||||
|
||||
/**
|
||||
* 坐标
|
||||
*/
|
||||
private Point point;
|
||||
|
||||
/**
|
||||
* 联系人
|
||||
*/
|
||||
|
|
|
@ -7,6 +7,7 @@ import jakarta.validation.constraints.NotBlank;
|
|||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -40,6 +41,9 @@ public class EnterprisesUnitSaveOrUpdateParams {
|
|||
@Schema(description = "详细地址")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "坐标")
|
||||
private Point point;
|
||||
|
||||
@Schema(description = "联系人")
|
||||
private ContactPersonInfo contactPersonInfo;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.changhu.common.db.enums.EnterprisesUnitType;
|
|||
import com.changhu.module.management.pojo.model.ContactPersonInfo;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
|
||||
/**
|
||||
* @author 20252
|
||||
|
@ -46,6 +47,9 @@ public class EnterprisesUnitPagerVo {
|
|||
@Schema(description = "地址")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "坐标")
|
||||
private Point point;
|
||||
|
||||
@Schema(description = "联系方式")
|
||||
private ContactPersonInfo contactPersonInfo;
|
||||
|
||||
|
|
|
@ -8,9 +8,18 @@ import com.alibaba.fastjson2.support.spring6.http.converter.FastJsonHttpMessageC
|
|||
import com.changhu.common.db.BaseEnum;
|
||||
import com.changhu.common.properties.Fastjson2Properties;
|
||||
import com.changhu.support.fastjson2.deserialze.DbEnumDeserializer;
|
||||
import com.changhu.support.fastjson2.deserialze.LatitudeDeserializer;
|
||||
import com.changhu.support.fastjson2.deserialze.LongitudeDeserializer;
|
||||
import com.changhu.support.fastjson2.deserialze.PointDeserializer;
|
||||
import com.changhu.support.fastjson2.filter.DesensitizedFilter;
|
||||
import com.changhu.support.fastjson2.serializer.DbEnumSerializer;
|
||||
import com.changhu.support.fastjson2.serializer.LatitudeSerializer;
|
||||
import com.changhu.support.fastjson2.serializer.LongitudeSerializer;
|
||||
import com.changhu.support.fastjson2.serializer.PointSerializer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.geotools.measure.Latitude;
|
||||
import org.geotools.measure.Longitude;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -47,9 +56,17 @@ public class FastJson2Config {
|
|||
JSON.register(clazz, DbEnumDeserializer.instance);
|
||||
});
|
||||
|
||||
JSON.config(JSONWriter.Feature.WriteLongAsString);
|
||||
JSON.register(Latitude.class, LatitudeSerializer.instance);
|
||||
JSON.register(Longitude.class, LongitudeSerializer.instance);
|
||||
JSON.register(Point.class, PointSerializer.instance);
|
||||
|
||||
//反序列化配置
|
||||
JSON.register(Latitude.class, LatitudeDeserializer.instance);
|
||||
JSON.register(Longitude.class, LongitudeDeserializer.instance);
|
||||
JSON.register(Point.class, PointDeserializer.instance);
|
||||
|
||||
JSON.config(JSONWriter.Feature.WriteLongAsString);
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package com.changhu.support.fastjson2.deserialze;
|
||||
|
||||
import com.alibaba.fastjson2.JSONReader;
|
||||
import com.alibaba.fastjson2.reader.ObjectReader;
|
||||
import org.geotools.measure.Latitude;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* author: luozhun
|
||||
* desc: LatitudeDeserializer
|
||||
* createTime: 2023/8/26 16:10
|
||||
*/
|
||||
public class LatitudeDeserializer implements ObjectReader<Latitude> {
|
||||
|
||||
public static final LatitudeDeserializer instance = new LatitudeDeserializer();
|
||||
|
||||
private LatitudeDeserializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Latitude readObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features) {
|
||||
//读取为BigDecimal来确保精度和避免浮点数的舍入误差
|
||||
BigDecimal value = jsonReader.readBigDecimal();
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return new Latitude(value.doubleValue());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.changhu.support.fastjson2.deserialze;
|
||||
|
||||
import com.alibaba.fastjson2.JSONReader;
|
||||
import com.alibaba.fastjson2.reader.ObjectReader;
|
||||
import org.geotools.measure.Longitude;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* author: luozhun
|
||||
* desc: LongitudeDeserializer
|
||||
* createTime: 2023/8/26 16:15
|
||||
*/
|
||||
public class LongitudeDeserializer implements ObjectReader<Longitude> {
|
||||
|
||||
public static final LongitudeDeserializer instance = new LongitudeDeserializer();
|
||||
|
||||
private LongitudeDeserializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Longitude readObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features) {
|
||||
//读取为BigDecimal来确保精度和避免浮点数的舍入误差
|
||||
BigDecimal value = jsonReader.readBigDecimal();
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return new Longitude(value.doubleValue());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.changhu.support.fastjson2.deserialze;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.alibaba.fastjson2.JSONReader;
|
||||
import com.alibaba.fastjson2.reader.ObjectReader;
|
||||
import com.changhu.common.utils.GeometryUtil;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* author: luozhun
|
||||
* desc: PointDeserializer
|
||||
* createTime: 2023/8/26 16:28
|
||||
*/
|
||||
public class PointDeserializer implements ObjectReader<Point> {
|
||||
|
||||
public static final PointDeserializer instance = new PointDeserializer();
|
||||
|
||||
private PointDeserializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point readObject(JSONReader jsonReader, Type fieldType, Object fieldName, long features) {
|
||||
List<BigDecimal> list = new ArrayList<>();
|
||||
jsonReader.readArray(list, BigDecimal.class);
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return null;
|
||||
}
|
||||
return GeometryUtil.createPoint(list);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.changhu.support.fastjson2.serializer;
|
||||
|
||||
import com.alibaba.fastjson2.JSONWriter;
|
||||
import com.alibaba.fastjson2.writer.ObjectWriter;
|
||||
import org.geotools.measure.Latitude;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* author: luozhun
|
||||
* desc: LatitudeSerializer
|
||||
* createTime: 2023/8/26 15:44
|
||||
*/
|
||||
public class LatitudeSerializer implements ObjectWriter<Latitude> {
|
||||
|
||||
public static final LatitudeSerializer instance = new LatitudeSerializer();
|
||||
|
||||
private LatitudeSerializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) {
|
||||
if (object == null) {
|
||||
jsonWriter.writeNull();
|
||||
return;
|
||||
}
|
||||
jsonWriter.writeDouble(((Latitude) object).degrees());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.changhu.support.fastjson2.serializer;
|
||||
|
||||
import com.alibaba.fastjson2.JSONWriter;
|
||||
import com.alibaba.fastjson2.writer.ObjectWriter;
|
||||
import org.geotools.measure.Longitude;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* author: luozhun
|
||||
* desc: LongitudeSerializer
|
||||
* createTime: 2023/8/26 15:46
|
||||
*/
|
||||
public class LongitudeSerializer implements ObjectWriter<Longitude> {
|
||||
|
||||
public static final LongitudeSerializer instance = new LongitudeSerializer();
|
||||
|
||||
private LongitudeSerializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) {
|
||||
if (object == null) {
|
||||
jsonWriter.writeNull();
|
||||
return;
|
||||
}
|
||||
jsonWriter.writeDouble(((Longitude) object).degrees());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.changhu.support.fastjson2.serializer;
|
||||
|
||||
import com.alibaba.fastjson2.JSONWriter;
|
||||
import com.alibaba.fastjson2.writer.ObjectWriter;
|
||||
import com.changhu.common.utils.GeometryUtil;
|
||||
import org.geotools.measure.Latitude;
|
||||
import org.geotools.measure.Longitude;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* author: luozhun
|
||||
* desc: PointSerializer
|
||||
* createTime: 2023/8/26 15:59
|
||||
*/
|
||||
public class PointSerializer implements ObjectWriter<Point> {
|
||||
|
||||
public static final PointSerializer instance = new PointSerializer();
|
||||
|
||||
private PointSerializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) {
|
||||
if (object == null) {
|
||||
jsonWriter.writeNull();
|
||||
return;
|
||||
}
|
||||
Point point = (Point) object;
|
||||
if (GeometryUtil.equals(GeometryUtil.emptyPoint(), point)) {
|
||||
jsonWriter.writeNull();
|
||||
return;
|
||||
}
|
||||
jsonWriter.writeAny(
|
||||
Arrays.asList(
|
||||
new Longitude(point.getCoordinate().getOrdinate(Coordinate.X)).degrees(),
|
||||
new Latitude(point.getCoordinate().getOrdinate(Coordinate.Y)).degrees()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionIntercepto
|
|||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import com.changhu.support.mybatisplus.interceptor.CustomDataPermissionHandler;
|
||||
import com.changhu.support.mybatisplus.interceptor.GeometryInnerInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
@ -30,6 +31,8 @@ public class CustomMybatisPlusConfig {
|
|||
interceptor.addInnerInterceptor(paginationInterceptor(DbType.MYSQL));
|
||||
// sql性能规范
|
||||
// interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());
|
||||
// 地理位置支持 配合
|
||||
interceptor.addInnerInterceptor(new GeometryInnerInterceptor());
|
||||
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
|
||||
//乐观锁插件
|
||||
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package com.changhu.support.mybatisplus.handler.global.geo;
|
||||
|
||||
import org.apache.ibatis.type.BaseTypeHandler;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
|
||||
/**
|
||||
* author: luozhun
|
||||
* desc: GeometryTypeHandler
|
||||
* createTime: 2023/8/25 17:59
|
||||
*/
|
||||
public abstract class AbstractGeometryTypeHandler<T extends Geometry> extends BaseTypeHandler<T> {
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package com.changhu.support.mybatisplus.handler.global.geo;
|
||||
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.changhu.common.utils.GeometryUtil;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||
import org.apache.ibatis.type.MappedTypes;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* author: luozhun
|
||||
* desc: PointTypeHandler
|
||||
* createTime: 2023/8/26 13:17
|
||||
*/
|
||||
@MappedJdbcTypes(JdbcType.VARCHAR)
|
||||
@MappedTypes(Point.class)
|
||||
public class PointTypeHandler extends AbstractGeometryTypeHandler<Point> {
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Point point, JdbcType jdbcType) throws SQLException {
|
||||
preparedStatement.setString(i, point.toText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getNullableResult(ResultSet resultSet, String s) throws SQLException {
|
||||
return createPoint(resultSet.getString(s), resultSet.getBytes(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getNullableResult(ResultSet resultSet, int i) throws SQLException {
|
||||
return createPoint(resultSet.getString(i), resultSet.getBytes(i));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
|
||||
return createPoint(callableStatement.getString(i), callableStatement.getBytes(i));
|
||||
}
|
||||
|
||||
public Point createPoint(String pointStr, byte[] pointBytes) {
|
||||
if (JSONUtil.isTypeJSON(pointStr)) {
|
||||
return GeometryUtil.createGeometryFromGeoJson(pointStr, Point.class);
|
||||
}
|
||||
if (StrUtil.startWithIgnoreCase(pointStr, ClassUtil.getClassName(Point.class, true))) {
|
||||
return GeometryUtil.createGeometryFromWkt(pointStr, Point.class);
|
||||
}
|
||||
return GeometryUtil.createPoint(pointBytes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package com.changhu.support.mybatisplus.interceptor;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
|
||||
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlCommandType;
|
||||
import org.locationtech.jts.geom.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.Connection;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.changhu.common.utils.GeometryUtil.ST_GeomFromText;
|
||||
|
||||
|
||||
/**
|
||||
* @author 20252
|
||||
* @createTime 2024/6/6 下午4:32
|
||||
* @desc 注意!!!此拦截器只针对mp提供的基本构造方法管用 可以进行参数对象拦截 如果是xml,请自行添加ST_GeomFromText函数
|
||||
*/
|
||||
@Slf4j
|
||||
public class GeometryInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {
|
||||
|
||||
|
||||
public static final List<Class<? extends Geometry>> GEOMETRY_CLASS_LIST = CollUtil.newArrayList(
|
||||
Point.class,
|
||||
MultiPoint.class,
|
||||
Polygon.class,
|
||||
MultiPolygon.class,
|
||||
LineString.class,
|
||||
MultiLineString.class
|
||||
);
|
||||
|
||||
public GeometryInnerInterceptor() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
|
||||
PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
|
||||
MappedStatement ms = mpSh.mappedStatement();
|
||||
//sql类型:INSERT UPDATE DELETE
|
||||
SqlCommandType sct = ms.getSqlCommandType();
|
||||
//mp参数
|
||||
Object parameter = mpSh.parameterHandler().getParameterObject();
|
||||
//参数对象
|
||||
Object object = null;
|
||||
//获取参数的属性
|
||||
switch (sct) {
|
||||
case UPDATE:
|
||||
try {
|
||||
object = BeanUtil.beanToMap(parameter).get(Constants.ENTITY);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
break;
|
||||
case INSERT:
|
||||
object = parameter;
|
||||
break;
|
||||
}
|
||||
//如果没有参数 就不进行sql修改
|
||||
if (object == null) {
|
||||
return;
|
||||
}
|
||||
//获取 parameter 对象 中有关 geometry 的属性名对应
|
||||
List<String> geoFields = this.getGeoFields(object);
|
||||
//如果没有geometry字段 就不进行sql修改
|
||||
if (geoFields.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
//获取原始sql
|
||||
BoundSql boundSql = ms.getBoundSql(parameter);
|
||||
//UPDATE geo_test SET update_by=?,update_time=?,delete_flag=1 WHERE id=? AND delete_flag=0
|
||||
String originalSql = StrUtil.removeAllLineBreaks(boundSql.getSql());
|
||||
switch (sct) {
|
||||
case UPDATE:
|
||||
for (String geometryField : geoFields) {
|
||||
String regex = geometryField + "\\s*=\\s*\\?";
|
||||
originalSql = ReUtil.replaceAll(originalSql, regex, StrUtil.format("{}={}", geometryField, ST_GeomFromText));
|
||||
}
|
||||
break;
|
||||
case INSERT:
|
||||
for (String geometryField : geoFields) {
|
||||
originalSql = this.replaceFindGeo(originalSql, geometryField);
|
||||
}
|
||||
break;
|
||||
}
|
||||
PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
|
||||
//修改sql
|
||||
mpBs.sql(originalSql);
|
||||
}
|
||||
|
||||
private String replaceFindGeo(String originalSql, String geometryField) {
|
||||
List<String> split = StrUtil.split(originalSql, geometryField);
|
||||
//找出geo字段在sql中的位置
|
||||
int geometryFieldIndex = StrUtil.count(split.get(0), ",");
|
||||
StringBuilder stringBuffer = new StringBuilder();
|
||||
Matcher matcher = Pattern.compile("\\?").matcher(originalSql);
|
||||
int count = 0;
|
||||
while (matcher.find()) {
|
||||
if (count == geometryFieldIndex) {
|
||||
matcher.appendReplacement(stringBuffer, ST_GeomFromText);
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
matcher.appendTail(stringBuffer);
|
||||
return stringBuffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象中的geo字段
|
||||
*/
|
||||
private List<String> getGeoFields(Object object) {
|
||||
return Arrays.stream(ReflectUtil.getFields(object.getClass()))
|
||||
.filter(field -> GEOMETRY_CLASS_LIST.contains(field.getType()))
|
||||
.map(Field::getName)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
|
@ -18,3 +18,4 @@ VITE_APP_MINIO_BUCKET=police-security-dev
|
|||
# 高德
|
||||
VITE_APP_GAODE_KEY=f379a3f860a68d7438526275d6a94b05
|
||||
VITE_APP_GAODE_VERSION=2.0
|
||||
VITE_APP_SECURITY_JS_CODE=432125a0f8d8cad2dac38b77d6f6728f
|
||||
|
|
|
@ -18,3 +18,4 @@ VITE_APP_MINIO_BUCKET=police-security
|
|||
# 高德
|
||||
VITE_APP_GAODE_KEY=f379a3f860a68d7438526275d6a94b05
|
||||
VITE_APP_GAODE_VERSION=2.0
|
||||
VITE_APP_SECURITY_JS_CODE=432125a0f8d8cad2dac38b77d6f6728f
|
||||
|
|
|
@ -10,7 +10,7 @@ import {initMap} from "@/utils/aMapUtil";
|
|||
|
||||
const props = withDefaults(defineProps<{
|
||||
plugins?: string[],
|
||||
initCallback?: () => void,
|
||||
initCallback?: (map: AMap.Map) => void,
|
||||
mapOptions?: AMap.MapOptions
|
||||
}>(), {
|
||||
plugins: () => {
|
||||
|
@ -36,8 +36,8 @@ defineExpose({
|
|||
|
||||
onMounted(() => {
|
||||
initMap(props.plugins).then(AMap => {
|
||||
props.initCallback && props.initCallback()
|
||||
map.value = new AMap.Map(mapId, props.mapOptions)
|
||||
props.initCallback && props.initCallback(map.value)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ export interface EnterprisesUnitPagerVo extends BaseTableRowRecord {
|
|||
streetName?: string;
|
||||
/** 地址 **/
|
||||
address?: string;
|
||||
point: [number, number]
|
||||
/** 联系方式 **/
|
||||
contactPersonInfo?: {
|
||||
name: string;
|
||||
|
@ -98,6 +99,7 @@ export interface EnterprisesUnitSaveOrUpdateParams {
|
|||
administrativeDivisionCodes: string[];
|
||||
/** 详细地址 **/
|
||||
address?: string;
|
||||
point?: [number, number]
|
||||
/** 联系人 **/
|
||||
contactPersonInfo?: {
|
||||
name: string;
|
||||
|
|
|
@ -4,7 +4,7 @@ type Amap = typeof AMap;
|
|||
export const initMap = (plugins?: string[]): Promise<Amap> => new Promise((resolve, reject) => {
|
||||
//@ts-ignore
|
||||
window._AMapSecurityConfig = {
|
||||
securityJsCode: '432125a0f8d8cad2dac38b77d6f6728f'
|
||||
securityJsCode: __APP_ENV.VITE_APP_SECURITY_JS_CODE
|
||||
}
|
||||
AMapLoader.load({
|
||||
key: __APP_ENV.VITE_APP_GAODE_KEY,
|
||||
|
|
|
@ -29,6 +29,21 @@ const saveOrUpdateEnterprisesUnit = (params: _FormType, callback: Function) => {
|
|||
|
||||
let city = '';
|
||||
|
||||
const initMarker = (map: AMap.Map) => {
|
||||
//添加maker点 设置point
|
||||
const maker = new AMap.Marker({
|
||||
position: _formParams.value.point,
|
||||
draggable: true
|
||||
})
|
||||
maker.on("dragend", ({lnglat}) => {
|
||||
_formParams.value.point = lnglat
|
||||
})
|
||||
map.clearMap()
|
||||
map.add(maker)
|
||||
map.setFitView()
|
||||
console.log(123);
|
||||
}
|
||||
|
||||
const _formOptions = ref<FormProMaxItemOptions<_FormType>>({
|
||||
name: {
|
||||
type: 'input',
|
||||
|
@ -62,7 +77,7 @@ const saveOrUpdateEnterprisesUnit = (params: _FormType, callback: Function) => {
|
|||
customRender: () => <MapContainer
|
||||
ref={_mapRef}
|
||||
style={{width: '100%', height: '300px', position: 'relative'}}
|
||||
initCallback={() => {
|
||||
initCallback={(map) => {
|
||||
AMap.plugin(['AMap.AutoComplete'], () => {
|
||||
//@ts-ignore
|
||||
const auto = new AMap.AutoComplete({
|
||||
|
@ -76,25 +91,19 @@ const saveOrUpdateEnterprisesUnit = (params: _FormType, callback: Function) => {
|
|||
message.error('所选点位没有经纬度信息 建议选则附近的手动移动!');
|
||||
return
|
||||
}
|
||||
//添加maker点 设置point
|
||||
const maker = new AMap.Marker({
|
||||
position: e.poi.location,
|
||||
draggable: true
|
||||
})
|
||||
console.log(e);
|
||||
maker.on("dragend", (e) => {
|
||||
console.log(e);
|
||||
|
||||
})
|
||||
_mapRef.value.mapInstance.add(maker)
|
||||
_mapRef.value.mapInstance.setFitView()
|
||||
_formParams.value.point = e.poi.location
|
||||
initMarker(map)
|
||||
});
|
||||
})
|
||||
if (_formParams.value.point) {
|
||||
initMarker(map)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div style={{position: 'absolute', left: '10px', top: '10px', zIndex: 9999}}>
|
||||
<input id={'tipinput'}
|
||||
placeholder={'请输入详细地址'}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
</MapContainer>
|
||||
|
@ -176,6 +185,7 @@ export const showEnterprisesUnit = (policeUnitPagerVo: PoliceUnitPagerVo) => {
|
|||
type: record.type.value,
|
||||
administrativeDivisionCodes: [record.province, record.city, record.districts, record.street].filter(Boolean),
|
||||
address: record.address,
|
||||
point: record.point,
|
||||
contactPersonInfoName: record.contactPersonInfo?.name,
|
||||
contactPersonInfoTelephone: record.contactPersonInfo?.telephone,
|
||||
remark: record.remark
|
||||
|
|
|
@ -129,6 +129,29 @@ const searchFormOptions = ref<TableProps["searchFormOptions"]>({
|
|||
}
|
||||
})
|
||||
|
||||
const a = {
|
||||
groupId1: {
|
||||
itemId1: {
|
||||
standardId: 123123,
|
||||
deductionPoints: 2
|
||||
},
|
||||
itemId2: {
|
||||
standardId: 345345,
|
||||
deductionPoints: 4
|
||||
}
|
||||
},
|
||||
groupId2: {
|
||||
itemId1: {
|
||||
standardId: 456456,
|
||||
deductionPoints: 2
|
||||
},
|
||||
itemId2: {
|
||||
standardId: 567567,
|
||||
deductionPoints: 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
|
|
@ -23,6 +23,7 @@ interface ImportMetaEnv {
|
|||
// 高德
|
||||
VITE_APP_GAODE_KEY: string
|
||||
VITE_APP_GAODE_VERSION: string
|
||||
VITE_APP_SECURITY_JS_CODE: string
|
||||
}
|
||||
|
||||
declare module '*.vue' {
|
||||
|
|
Loading…
Reference in New Issue