运营区

This commit is contained in:
磷叶 2025-03-17 14:34:58 +08:00
parent 77139780fb
commit 97e5a2e782
20 changed files with 1681 additions and 194 deletions

View File

@ -15,8 +15,56 @@
common通用工具
</description>
<repositories>
<repository>
<id>osgeo</id>
<name>OSGeo Release Repository</name>
<url>https://repo.osgeo.org/repository/release/</url>
<snapshots><enabled>false</enabled></snapshots>
<releases><enabled>true</enabled></releases>
</repository>
</repositories>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.geotools/gt-geojson -->
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geojson</artifactId>
<version>24.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.geotools/gt-epsg-hsql -->
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-epsg-hsql</artifactId>
<version>24.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.geotools/gt-shapefile -->
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>24.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.geotools/gt-swing -->
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-swing</artifactId>
<version>24.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.geotools.jdbc/gt-jdbc-postgis -->
<dependency>
<groupId>org.geotools.jdbc</groupId>
<artifactId>gt-jdbc-postgis</artifactId>
<version>24.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
@ -193,4 +241,5 @@
</dependencies>
</project>

View File

@ -0,0 +1,375 @@
package com.ruoyi.common.utils.map;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.geotools.data.DataUtilities;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.operation.MathTransform;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* GeoUtils.
*
* @author qiuzhenzhao
* @date 2024/04/19
*/
@Slf4j
@UtilityClass
public class GeoUtils {
private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory();
private static final String AUTO = "AUTO:42001,%s,%s";
private static final WKTWriter WKT_WRITER = new WKTWriter();
private static final WKTReader WKT_READER = new WKTReader();
private static final GeometryJSON GEOMETRY_JSON = new GeometryJSON(15);
private final static FeatureJSON FEATURE_JSON = new FeatureJSON(GEOMETRY_JSON);
private static final double EARTH_RADIUS = 6371000; // 地球半径单位
/**
* 合并多边形 Geographic
* */
public Geometry union(Collection<Geometry> geometries) {
return GEOMETRY_FACTORY.buildGeometry(geometries).union();
}
/**
* geometry转geojson
* */
@SneakyThrows
public String geoJson(Geometry geometry) {
SimpleFeatureType type = DataUtilities.createType("Link", "geometry:" + geometry.getGeometryType());
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(type);
featureBuilder.add(geometry);
SimpleFeature feature = featureBuilder.buildFeature(null);
FeatureCollection<?, ?> featureCollection = new ListFeatureCollection(type, Collections.singletonList(feature));
return FEATURE_JSON.toString(featureCollection);
}
/**
* 定的geometry对象转换为GeoJSON格式并将其写入到指定的file中
* */
@SneakyThrows
public void geoJson(Geometry geometry, File file) {
SimpleFeatureType type = DataUtilities.createType("Link", "geometry:" + geometry.getGeometryType());
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(type);
featureBuilder.add(geometry);
SimpleFeature feature = featureBuilder.buildFeature(null);
FeatureCollection<?, ?> featureCollection = new ListFeatureCollection(type, Collections.singletonList(feature));
FEATURE_JSON.writeFeatureCollection(featureCollection, FileUtils.openOutputStream(file));
}
/**
* wkt转Geometry对象
* wkt格式POLYGON((x1 y1,x2 y2,x3 y3,x4 y4,x1 y1))
* */
public Geometry fromWkt(String wkt) {
if (StringUtils.isBlank(wkt)) {
return null;
}
try {
return WKT_READER.read(wkt);
} catch (Exception e) {
log.warn(e.getMessage(), e);
return null;
}
}
/**
* json转Geometry对象
* 格式[[120.356267,26.941506],[120.357168,26.941262],[120.35697,26.940564]]
* */
public Geometry toGeometry(String json) {
if (StringUtils.isBlank(json)) {
return null;
}
try {
// 将json转成二位数组并形成闭合
double[][] coordinates = transform(json);
log.info("coordinates:{}", JSON.toJSONString(coordinates));
// 构建坐标数组
Coordinate[] points = new Coordinate[coordinates.length];
for (int i = 0; i < coordinates.length; i++) {
double lon = coordinates[i][0];
double lat = coordinates[i][1];
points[i] = new Coordinate(lon, lat);
}
// 创建LinearRing对象
LinearRing linearRing = GEOMETRY_FACTORY.createLinearRing(points);
// 创建Polygon对象
Polygon polygon = GEOMETRY_FACTORY.createPolygon(linearRing);
return polygon;
} catch (Exception e) {
log.warn(e.getMessage(), e);
return null;
}
}
/**
* json转Geometry对象 直线
* 格式[[120.356267,26.941506],[120.357168,26.941262],[120.35697,26.940564]]
* */
public Geometry toGeometryByLinearRing(String json) {
if (StringUtils.isBlank(json)) {
return null;
}
try {
// 将json转成二位数组
double[][] coordinates = transform(json);
log.info("coordinates:{}", JSON.toJSONString(coordinates));
// 构建坐标数组
Coordinate[] points = new Coordinate[coordinates.length];
for (int i = 0; i < coordinates.length; i++) {
double lon = coordinates[i][0];
double lat = coordinates[i][1];
points[i] = new Coordinate(lon, lat);
}
// 创建LineString对象
LineString lineString = GEOMETRY_FACTORY.createLineString(points);
return lineString;
} catch (Exception e) {
log.warn(e.getMessage(), e);
return null;
}
}
/**
* 将json转成二位数组
* */
public double[][] transform(String json) {
// 使用 Jackson 库解析 JSON 字符串为二维数组
ObjectMapper objectMapper = new ObjectMapper();
double[][] coordinates;
try {
coordinates = objectMapper.readValue(json, double[][].class);
} catch (Exception e) {
e.printStackTrace();
return null;
}
// 添加闭合多边形的最后一个点
int numPoints = coordinates.length;
double[][] closedCoordinates = new double[numPoints + 1][2];
for (int i = 0; i < numPoints; i++) {
closedCoordinates[i] = coordinates[i];
}
closedCoordinates[numPoints] = coordinates[0]; // 将第一个点复制到最后一个点
return closedCoordinates;
}
/**
* Geometry对象转wkt
* */
public String wkt(Geometry geometry) {
if (geometry == null) {
return null;
}
try {
return WKT_WRITER.write(geometry);
} catch (Exception e) {
log.warn(e.getMessage(), e);
return null;
}
}
/**
* JSON转Geometry
* */
public Geometry fromJson(String json) {
if (StringUtils.isBlank(json)) {
return null;
}
try {
return GEOMETRY_JSON.read(json);
} catch (Exception e) {
log.warn(e.getMessage(), e);
return null;
}
}
/**
* 计算Geometry的面积
* */
@SneakyThrows
public Double calcArea(Geometry geometry) {
Point centroid = geometry.getCentroid();
MathTransform mathTransform = CRS.findMathTransform(DefaultGeographicCRS.WGS84,
CRS.decode(String.format(AUTO, centroid.getX(), centroid.getY())));
Geometry transform = JTS.transform(geometry, mathTransform);
return transform.getArea();
}
/**
* 判断经纬度是否在多边形区域内
* */
public boolean isInCircle(String longitude, String latitude,Geometry polygon){
/**创建一个点*/
GeometryFactory geometryFactory = new GeometryFactory();
Coordinate coordinate = new Coordinate(Double.parseDouble(longitude), Double.parseDouble(latitude));
Point point = geometryFactory.createPoint(coordinate);
return polygon.contains(point);
}
/**
* 判断一个点是否在一个圆形区域内考虑误差距离
* */
public static boolean isInPolygonWithTolerance(String longitude, String latitude, Geometry polygon, double tolerance) {
double lon = Double.parseDouble(longitude);
double lat = Double.parseDouble(latitude);
GeometryFactory geometryFactory = new GeometryFactory();
Coordinate coordinate = new Coordinate(lon, lat);
Point point = geometryFactory.createPoint(coordinate);
if (polygon.contains(point)) {
return true;
} else {
// 获取多边形的外边界
Coordinate[] coordinates = polygon.getCoordinates();
for (Coordinate coord : coordinates) {
double distance = calculateDistance(lat, lon, coord.y, coord.x);
// log.info("距离----distance:{}",distance);
if (distance <= tolerance) {
// log.info("最小距离----distance:{}",distance);
return true;
}
}
return false;
}
}
/**
* 判断一个点是否在一个缩短后的圆形区域内
* */
public static boolean isInPolygonWithShorten(String longitude, String latitude, Geometry polygon, double shortenDistance) {
double lon = Double.parseDouble(longitude);
double lat = Double.parseDouble(latitude);
GeometryFactory geometryFactory = new GeometryFactory();
Coordinate coordinate = new Coordinate(lon, lat);
Point point = geometryFactory.createPoint(coordinate);
if (polygon.contains(point)) {
// 获取多边形的外边界
Coordinate[] coordinates = polygon.getCoordinates();
for (Coordinate coord : coordinates) {
double distance = calculateDistance(lat, lon, coord.y, coord.x);
// log.info("距离----distance:{}",distance);
if (shortenDistance >= distance) {
// log.info("最小距离----distance:{}",distance);
return false;
}
}
return true;
} else {
return false;
}
}
// 将角度转换为弧度
private static double deg2rad(double deg) {
return deg * (Math.PI / 180);
}
// 使用 Haversine 公式计算两点间的距离
public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
double dLat = deg2rad(lat2 - lat1);
double dLon = deg2rad(lon1 - lon2); // 修正此处为 (lon1 - lon2)
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return EARTH_RADIUS * c;
}
public static double[][] parseJsonTrack(String jsonString) {
JSONArray jsonArray = JSONArray.parseArray(jsonString);
double[][] track = new double[jsonArray.size()][2];
for (int i = 0; i < jsonArray.size(); i++) {
JSONArray coord = jsonArray.getJSONArray(i);
track[i][0] = coord.getDouble(0);
track[i][1] = coord.getDouble(1);
}
return track;
}
/**
* 计算一段轨迹的总距离
* */
public static double calculateTotalDistance(double[][] track) {
double totalDistance = 0;
for (int i = 0; i < track.length - 1; i++) {
totalDistance += haversineDistance(track[i], track[i + 1]);
}
return totalDistance;
}
public static double haversineDistance(double[] coord1, double[] coord2) {
double lat1 = deg2rad(coord1[1]);
double lon1 = deg2rad(coord1[0]);
double lat2 = deg2rad(coord2[1]);
double lon2 = deg2rad(coord2[0]);
double dLat = lat2 - lat1;
double dLon = lon2 - lon1;
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1) * Math.cos(lat2) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return EARTH_RADIUS * c; // 距离
}
/**
* 判获取到最近一个运营区
* */
public static boolean getNearestOperatingArea (String longitude, String latitude, List<Geometry> polygon) {
double lon = Double.parseDouble(longitude);
double lat = Double.parseDouble(latitude);
GeometryFactory geometryFactory = new GeometryFactory();
Coordinate coordinate = new Coordinate(lon, lat);
Point point = geometryFactory.createPoint(coordinate);
for (Geometry geometry : polygon) {
if (geometry.contains(point)) {
return true;
}else{
}
}
return false;
}
/**
* 计算给定点到多边形的最短距离
* */
public double calculateMinDistanceToPolygon(Geometry polygon, double lon, double lat) {
Coordinate coord = new Coordinate(lon, lat);
Point point = new GeometryFactory().createPoint(coord);
return polygon.distance(point); // 返回给定点到多边形的最短距离
}
}

View File

@ -0,0 +1,823 @@
package com.ruoyi.common.utils.map;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
/**
* 坐标系转换
* @author Mr.Li
* @date 2023-08-02
*/
@Slf4j
public class GpsCoordinateUtils {
private static final double PI = 3.1415926535897932384626433832795;
private static final double A = 6378245.0;
private static final double EE = 0.00669342162296594323;
/**
* 地球坐标系 WGS-84 to 火星坐标系 GCJ-02
*
* @param latitude 纬度
* @param longitude 经度
* @return [纬度, 经度]
*/
public static double[] calWGS84toGCJ02(double latitude, double longitude) {
Point dev = calDev(latitude, longitude);
double retLat = latitude + dev.getLatitude();
double retLon = longitude + dev.getLongitude();
return new double[]{retLat, retLon};
}
/**
* 地球坐标系 WGS-84 to 百度坐标系 BD-09
*
* @param latitude 纬度
* @param longitude 经度
* @return [纬度, 经度]
*/
public static double[] calWGS84toBD09(double latitude, double longitude) {
Point dev = calDev(latitude, longitude);
double retLat = latitude + dev.getLatitude();
double retLon = longitude + dev.getLongitude();
return calGCJ02toBD09(retLat, retLon);
}
/**
* 火星坐标系 GCJ-02 to 地球坐标系 WGS-84
*
* @param latitude 纬度
* @param longitude 经度
* @return [纬度, 经度]
*/
public static double[] calGCJ02toWGS84(double latitude, double longitude) {
Point dev = calDev(latitude, longitude);
double retLat = latitude - dev.getLatitude();
double retLon = longitude - dev.getLongitude();
dev = calDev(retLat, retLon);
retLat = latitude - dev.getLatitude();
retLon = longitude - dev.getLongitude();
return new double[]{retLat, retLon};
}
/**
* 百度坐标系 BD-09 to 地球坐标系 WGS-84
*
* @param latitude 纬度
* @param longitude 经度
* @return [纬度, 经度]
*/
public static double[] calBD09toWGS84(double latitude, double longitude) {
double[] gcj = calBD09toGCJ02(latitude, longitude);
return calGCJ02toWGS84(gcj[0], gcj[1]);
}
private static Point calDev(double latitude, double longitude) {
if (isOutOfChina(latitude, longitude, false)) {
return new Point(latitude, latitude);
}
double dLat = calLat(longitude - 105.0, latitude - 35.0);
double dLon = calLon(longitude - 105.0, latitude - 35.0);
double radLat = latitude / 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);
return new Point(dLat, dLon);
}
private static double calLat(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 calLon(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;
}
/**
* 火星坐标系 GCJ-02 to 百度坐标系 BD-09
*
* @param latitude 纬度
* @param longitude 经度
* @return [纬度, 经度]
*/
public static double[] calGCJ02toBD09(double latitude, double longitude) {
double x = longitude, y = latitude;
double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * PI);
double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * PI);
double retLat = z * Math.sin(theta) + 0.006;
double retLon = z * Math.cos(theta) + 0.0065;
return new double[]{retLat, retLon};
}
/**
* 百度坐标系 BD-09 to 火星坐标系 GCJ-02
*
* @param latitude 纬度
* @param longitude 经度
* @return [纬度, 经度]
*/
public static double[] calBD09toGCJ02(double latitude, double longitude) {
double x = longitude - 0.0065, y = latitude - 0.006;
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * PI);
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * PI);
double retLat = z * Math.sin(theta);
double retLon = z * Math.cos(theta);
return new double[]{retLat, retLon};
}
/**
* 判断坐标是否在国内
*
* @param latitude
* @param longitude
* @param precision 是否精确判断范围
* @return true 在国外false 在国内
*/
public static boolean isOutOfChina(double latitude, double longitude, boolean precision) {
if (precision) {
return CHINA_POLYGON.stream().noneMatch(point -> pointInPolygon(point, latitude, longitude));
} else {
if (longitude < 72.004 || longitude > 137.8347) {
return true;
}
if (latitude < 0.8293 || latitude > 55.8271) {
return true;
}
return false;
}
}
/**
* 检查坐标点是否在多边形区域内
*
* @param polygon 多边形
* @param latitude 纬度
* @param longitude 经度
* @return true 在多边形区域内false 在多边形区域外
*/
private static boolean pointInPolygon(Point[] polygon, double latitude, double longitude) {
int i, j = polygon.length - 1;
boolean oddNodes = false;
for (i = 0; i < polygon.length; i++) {
if ((polygon[i].getLatitude() < latitude && polygon[j].getLatitude() >= latitude
|| polygon[j].getLatitude() < latitude && polygon[i].getLatitude() >= latitude)
&& (polygon[i].getLongitude() <= longitude || polygon[j].getLongitude() <= longitude)) {
if (polygon[i].getLongitude()
+ (latitude - polygon[i].getLatitude()) / (polygon[j].getLatitude() - polygon[i].getLatitude())
* (polygon[j].getLongitude() - polygon[i].getLongitude())
< longitude) {
oddNodes = !oddNodes;
}
}
j = i;
}
return oddNodes;
}
static class Point {
private double longitude;
private double latitude;
Point(double latitude, double longitude) {
this.longitude = longitude;
this.latitude = latitude;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
@Override
public String toString() {
return longitude + "," + latitude;
}
}
//region 中国行政边界的WGS84坐标数据
/**
* 大陆边境线
*/
private static final Point[] MAINLAND = new Point[]{
new Point(27.32083, 88.91693),
new Point(27.54243, 88.76464),
new Point(28.00805, 88.83575),
new Point(28.1168, 88.62435),
new Point(27.86605, 88.14279),
new Point(27.82305, 87.19275),
new Point(28.11166, 86.69527),
new Point(27.90888, 86.45137),
new Point(28.15805, 86.19769),
new Point(27.88625, 86.0054),
new Point(28.27916, 85.72137),
new Point(28.30666, 85.11095),
new Point(28.59104, 85.19518),
new Point(28.54444, 84.84665),
new Point(28.73402, 84.48623),
new Point(29.26097, 84.11651),
new Point(29.18902, 83.5479),
new Point(29.63166, 83.19109),
new Point(30.06923, 82.17525),
new Point(30.33444, 82.11123),
new Point(30.385, 81.42623),
new Point(30.01194, 81.23221),
new Point(30.20435, 81.02536),
new Point(30.57552, 80.207),
new Point(30.73374, 80.25423),
new Point(30.96583, 79.86304),
new Point(30.95708, 79.55429),
new Point(31.43729, 79.08082),
new Point(31.30895, 78.76825),
new Point(31.96847, 78.77075),
new Point(32.24304, 78.47594),
new Point(32.5561, 78.40595),
new Point(32.63902, 78.74623),
new Point(32.35083, 78.9711),
new Point(32.75666, 79.52874),
new Point(33.09944, 79.37511),
new Point(33.42863, 78.93623),
new Point(33.52041, 78.81387),
new Point(34.06833, 78.73581),
new Point(34.35001, 78.98535),
new Point(34.6118, 78.33707),
new Point(35.28069, 78.02305),
new Point(35.49902, 78.0718),
new Point(35.50133, 77.82393),
new Point(35.6125, 76.89526),
new Point(35.90665, 76.55304),
new Point(35.81458, 76.18061),
new Point(36.07082, 75.92887),
new Point(36.23751, 76.04166),
new Point(36.66343, 75.85984),
new Point(36.73169, 75.45179),
new Point(36.91156, 75.39902),
new Point(36.99719, 75.14787),
new Point(37.02782, 74.56543),
new Point(37.17, 74.39089),
new Point(37.23733, 74.91574),
new Point(37.40659, 75.18748),
new Point(37.65243, 74.9036),
new Point(38.47256, 74.85442),
new Point(38.67438, 74.35471),
new Point(38.61271, 73.81401),
new Point(38.88653, 73.70818),
new Point(38.97256, 73.85235),
new Point(39.23569, 73.62005),
new Point(39.45483, 73.65569),
new Point(39.59965, 73.95471),
new Point(39.76896, 73.8429),
new Point(40.04202, 73.99096),
new Point(40.32792, 74.88089),
new Point(40.51723, 74.8588),
new Point(40.45042, 75.23394),
new Point(40.64452, 75.58284),
new Point(40.298, 75.70374),
new Point(40.35324, 76.3344),
new Point(41.01258, 76.87067),
new Point(41.04079, 78.08083),
new Point(41.39286, 78.39554),
new Point(42.03954, 80.24513),
new Point(42.19622, 80.23402),
new Point(42.63245, 80.15804),
new Point(42.81565, 80.25796),
new Point(42.88545, 80.57226),
new Point(43.02906, 80.38405),
new Point(43.1683, 80.81526),
new Point(44.11378, 80.36887),
new Point(44.6358, 80.38499),
new Point(44.73408, 80.51589),
new Point(44.90282, 79.87106),
new Point(45.3497, 81.67928),
new Point(45.15748, 81.94803),
new Point(45.13303, 82.56638),
new Point(45.43581, 82.64624),
new Point(45.5831, 82.32179),
new Point(47.20061, 83.03443),
new Point(46.97332, 83.93026),
new Point(46.99361, 84.67804),
new Point(46.8277, 84.80318),
new Point(47.0591, 85.52257),
new Point(47.26221, 85.70139),
new Point(47.93721, 85.53707),
new Point(48.39333, 85.76596),
new Point(48.54277, 86.59791),
new Point(49.1102, 86.87602),
new Point(49.09262, 87.34821),
new Point(49.17295, 87.8407),
new Point(48.98304, 87.89291),
new Point(48.88103, 87.7611),
new Point(48.73499, 88.05942),
new Point(48.56541, 87.99194),
new Point(48.40582, 88.51679),
new Point(48.21193, 88.61179),
new Point(47.99374, 89.08514),
new Point(47.88791, 90.07096),
new Point(46.95221, 90.9136),
new Point(46.57735, 91.07027),
new Point(46.29694, 90.92151),
new Point(46.01735, 91.02651),
new Point(45.57972, 90.68193),
new Point(45.25305, 90.89694),
new Point(45.07729, 91.56088),
new Point(44.95721, 93.5547),
new Point(44.35499, 94.71735),
new Point(44.29416, 95.41061),
new Point(44.01937, 95.34109),
new Point(43.99311, 95.53339),
new Point(43.28388, 95.87901),
new Point(42.73499, 96.38206),
new Point(42.79583, 97.1654),
new Point(42.57194, 99.51012),
new Point(42.67707, 100.8425),
new Point(42.50972, 101.8147),
new Point(42.23333, 102.0772),
new Point(41.88721, 103.4164),
new Point(41.87721, 104.5267),
new Point(41.67068, 104.5237),
new Point(41.58666, 105.0065),
new Point(42.46624, 107.4758),
new Point(42.42999, 109.3107),
new Point(42.64576, 110.1064),
new Point(43.31694, 110.9897),
new Point(43.69221, 111.9583),
new Point(44.37527, 111.4214),
new Point(45.04944, 111.873),
new Point(45.08055, 112.4272),
new Point(44.8461, 112.853),
new Point(44.74527, 113.638),
new Point(45.38943, 114.5453),
new Point(45.4586, 115.7019),
new Point(45.72193, 116.2104),
new Point(46.29583, 116.5855),
new Point(46.41888, 117.3755),
new Point(46.57069, 117.425),
new Point(46.53645, 117.8455),
new Point(46.73638, 118.3147),
new Point(46.59895, 119.7068),
new Point(46.71513, 119.9315),
new Point(46.90221, 119.9225),
new Point(47.66499, 119.125),
new Point(47.99475, 118.5393),
new Point(48.01125, 117.8046),
new Point(47.65741, 117.3827),
new Point(47.88805, 116.8747),
new Point(47.87819, 116.2624),
new Point(47.69186, 115.9231),
new Point(47.91749, 115.5944),
new Point(48.14353, 115.5491),
new Point(48.25249, 115.8358),
new Point(48.52055, 115.8111),
new Point(49.83047, 116.7114),
new Point(49.52058, 117.8747),
new Point(49.92263, 118.5746),
new Point(50.09631, 119.321),
new Point(50.33028, 119.36),
new Point(50.39027, 119.1386),
new Point(51.62083, 120.0641),
new Point(52.115, 120.7767),
new Point(52.34423, 120.6259),
new Point(52.54267, 120.7122),
new Point(52.58805, 120.0819),
new Point(52.76819, 120.0314),
new Point(53.26374, 120.8307),
new Point(53.54361, 123.6147),
new Point(53.18832, 124.4933),
new Point(53.05027, 125.62),
new Point(52.8752, 125.6573),
new Point(52.75722, 126.0968),
new Point(52.5761, 125.9943),
new Point(52.12694, 126.555),
new Point(51.99437, 126.4412),
new Point(51.38138, 126.9139),
new Point(51.26555, 126.8176),
new Point(51.31923, 126.9689),
new Point(51.05825, 126.9331),
new Point(50.74138, 127.2919),
new Point(50.31472, 127.334),
new Point(50.20856, 127.5861),
new Point(49.80588, 127.515),
new Point(49.58665, 127.838),
new Point(49.58443, 128.7119),
new Point(49.34676, 129.1118),
new Point(49.4158, 129.4902),
new Point(48.86464, 130.2246),
new Point(48.86041, 130.674),
new Point(48.60576, 130.5236),
new Point(48.3268, 130.824),
new Point(48.10839, 130.6598),
new Point(47.68721, 130.9922),
new Point(47.71027, 132.5211),
new Point(48.09888, 133.0827),
new Point(48.06888, 133.4843),
new Point(48.39112, 134.4153),
new Point(48.26713, 134.7408),
new Point(47.99207, 134.5576),
new Point(47.70027, 134.7608),
new Point(47.32333, 134.1825),
new Point(46.64017, 133.9977),
new Point(46.47888, 133.8472),
new Point(46.25363, 133.9016),
new Point(45.82347, 133.4761),
new Point(45.62458, 133.4702),
new Point(45.45083, 133.1491),
new Point(45.05694, 133.0253),
new Point(45.34582, 131.8684),
new Point(44.97388, 131.4691),
new Point(44.83649, 130.953),
new Point(44.05193, 131.298),
new Point(43.53624, 131.1912),
new Point(43.38958, 131.3104),
new Point(42.91645, 131.1285),
new Point(42.74485, 130.4327),
new Point(42.42186, 130.6044),
new Point(42.71416, 130.2468),
new Point(42.88794, 130.2514),
new Point(43.00457, 129.9046),
new Point(42.43582, 129.6955),
new Point(42.44624, 129.3493),
new Point(42.02736, 128.9269),
new Point(42.00124, 128.0566),
new Point(41.58284, 128.3002),
new Point(41.38124, 128.1529),
new Point(41.47249, 127.2708),
new Point(41.79222, 126.9047),
new Point(41.61176, 126.5661),
new Point(40.89694, 126.0118),
new Point(40.47037, 124.8851),
new Point(40.09362, 124.3736),
new Point(39.82777, 124.128),
new Point(39.8143, 123.2422),
new Point(39.67388, 123.2167),
new Point(38.99638, 121.648),
new Point(38.8611, 121.6982),
new Point(38.71909, 121.1873),
new Point(38.91221, 121.0887),
new Point(39.09013, 121.6794),
new Point(39.2186, 121.5994),
new Point(39.35166, 121.7511),
new Point(39.52847, 121.2283),
new Point(39.62322, 121.533),
new Point(39.81138, 121.4683),
new Point(40.00305, 121.881),
new Point(40.50562, 122.2987),
new Point(40.73874, 122.0521),
new Point(40.92194, 121.1775),
new Point(40.1961, 120.4468),
new Point(39.87242, 119.5264),
new Point(39.15693, 118.9715),
new Point(39.04083, 118.3273),
new Point(39.19846, 117.889),
new Point(38.67555, 117.5364),
new Point(38.38666, 117.6722),
new Point(38.16721, 118.0281),
new Point(38.1529, 118.8378),
new Point(37.87832, 119.0355),
new Point(37.30054, 118.9566),
new Point(37.14361, 119.2328),
new Point(37.15138, 119.7672),
new Point(37.35228, 119.8529),
new Point(37.83499, 120.7371),
new Point(37.42458, 121.58),
new Point(37.55256, 122.1282),
new Point(37.41833, 122.1814),
new Point(37.39624, 122.5586),
new Point(37.20999, 122.5972),
new Point(37.02583, 122.4005),
new Point(37.01978, 122.5392),
new Point(36.89361, 122.5047),
new Point(36.84298, 122.1923),
new Point(37.00027, 121.9566),
new Point(36.75889, 121.5944),
new Point(36.61666, 120.7764),
new Point(36.52638, 120.96),
new Point(36.37582, 120.8753),
new Point(36.42277, 120.7062),
new Point(36.14075, 120.6956),
new Point(36.0419, 120.3436),
new Point(36.26345, 120.3078),
new Point(36.19998, 120.0889),
new Point(35.95943, 120.2378),
new Point(35.57893, 119.6475),
new Point(34.88499, 119.1761),
new Point(34.31145, 120.2487),
new Point(32.97499, 120.8858),
new Point(32.63889, 120.8375),
new Point(32.42958, 121.3348),
new Point(32.11333, 121.4412),
new Point(32.02166, 121.7066),
new Point(31.67833, 121.8275),
new Point(31.86639, 120.9444),
new Point(32.09361, 120.6019),
new Point(31.94555, 120.099),
new Point(32.30638, 119.8267),
new Point(32.26277, 119.6317),
new Point(31.90388, 120.1364),
new Point(31.98833, 120.7026),
new Point(31.81944, 120.7196),
new Point(31.30889, 121.6681),
new Point(30.97986, 121.8828),
new Point(30.85305, 121.8469),
new Point(30.56889, 120.9915),
new Point(30.33555, 120.8144),
new Point(30.39298, 120.4586),
new Point(30.19694, 120.15),
new Point(30.31027, 120.5082),
new Point(30.06465, 120.7916),
new Point(30.30458, 121.2808),
new Point(29.96305, 121.6778),
new Point(29.88211, 122.1196),
new Point(29.51167, 121.4483),
new Point(29.58916, 121.9744),
new Point(29.19527, 121.9336),
new Point(29.18388, 121.8119),
new Point(29.37236, 121.7969),
new Point(29.19729, 121.7444),
new Point(29.29111, 121.5611),
new Point(29.1634, 121.4135),
new Point(29.02194, 121.6914),
new Point(28.9359, 121.4908),
new Point(28.72798, 121.6113),
new Point(28.84215, 121.1464),
new Point(28.66993, 121.4844),
new Point(28.34722, 121.6417),
new Point(28.13889, 121.3419),
new Point(28.38277, 121.1651),
new Point(27.98222, 120.9353),
new Point(28.07944, 120.5908),
new Point(27.87229, 120.84),
new Point(27.59319, 120.5812),
new Point(27.45083, 120.6655),
new Point(27.20777, 120.5075),
new Point(27.28278, 120.1896),
new Point(27.14764, 120.4211),
new Point(26.89805, 120.0332),
new Point(26.64465, 120.128),
new Point(26.51778, 119.8603),
new Point(26.78823, 120.0733),
new Point(26.64888, 119.8668),
new Point(26.79611, 119.7879),
new Point(26.75625, 119.5503),
new Point(26.44222, 119.8204),
new Point(26.47388, 119.5775),
new Point(26.33861, 119.658),
new Point(26.36777, 119.9489),
new Point(25.99694, 119.4253),
new Point(26.14041, 119.0975),
new Point(25.93788, 119.354),
new Point(25.99069, 119.7058),
new Point(25.67996, 119.5807),
new Point(25.68222, 119.4522),
new Point(25.35333, 119.6454),
new Point(25.60649, 119.3149),
new Point(25.42097, 119.1053),
new Point(25.25319, 119.3526),
new Point(25.17208, 119.2726),
new Point(25.2426, 118.8749),
new Point(24.97194, 118.9866),
new Point(24.88291, 118.5729),
new Point(24.75673, 118.7631),
new Point(24.52861, 118.5953),
new Point(24.53638, 118.2397),
new Point(24.68194, 118.1688),
new Point(24.44024, 118.0199),
new Point(24.46019, 117.7947),
new Point(24.25875, 118.1237),
new Point(23.62437, 117.1957),
new Point(23.65919, 116.9179),
new Point(23.355, 116.7603),
new Point(23.42024, 116.5322),
new Point(23.23666, 116.7871),
new Point(23.21083, 116.5139),
new Point(22.93902, 116.4817),
new Point(22.73916, 115.7978),
new Point(22.88416, 115.6403),
new Point(22.65889, 115.5367),
new Point(22.80833, 115.1614),
new Point(22.70277, 114.8889),
new Point(22.53305, 114.8722),
new Point(22.64027, 114.718),
new Point(22.81402, 114.7782),
new Point(22.69972, 114.5208),
new Point(22.50423, 114.6136),
new Point(22.55004, 114.2223),
new Point(22.42993, 114.3885),
new Point(22.26056, 114.2961),
new Point(22.36736, 113.9056),
new Point(22.50874, 114.0337),
new Point(22.47444, 113.8608),
new Point(22.83458, 113.606),
new Point(23.05027, 113.5253),
new Point(23.11724, 113.8219),
new Point(23.05083, 113.4793),
new Point(22.87986, 113.3629),
new Point(22.54944, 113.5648),
new Point(22.18701, 113.5527),
new Point(22.56701, 113.1687),
new Point(22.17965, 113.3868),
new Point(22.04069, 113.2226),
new Point(22.20485, 113.0848),
new Point(21.8693, 112.94),
new Point(21.96472, 112.824),
new Point(21.70139, 112.2819),
new Point(21.91611, 111.8921),
new Point(21.75139, 111.9669),
new Point(21.77819, 111.6762),
new Point(21.61264, 111.7832),
new Point(21.5268, 111.644),
new Point(21.52528, 111.0285),
new Point(21.21138, 110.5328),
new Point(21.37322, 110.3944),
new Point(20.84381, 110.1594),
new Point(20.84083, 110.3755),
new Point(20.64, 110.3239),
new Point(20.48618, 110.5274),
new Point(20.24611, 110.2789),
new Point(20.2336, 109.9244),
new Point(20.4318, 110.0069),
new Point(20.92416, 109.6629),
new Point(21.44694, 109.9411),
new Point(21.50569, 109.6605),
new Point(21.72333, 109.5733),
new Point(21.49499, 109.5344),
new Point(21.39666, 109.1428),
new Point(21.58305, 109.1375),
new Point(21.61611, 108.911),
new Point(21.79889, 108.8702),
new Point(21.59888, 108.7403),
new Point(21.93562, 108.4692),
new Point(21.59014, 108.5125),
new Point(21.68999, 108.3336),
new Point(21.51444, 108.2447),
new Point(21.54241, 107.99),
new Point(21.66694, 107.7831),
new Point(21.60526, 107.3627),
new Point(22.03083, 106.6933),
new Point(22.45682, 106.5517),
new Point(22.76389, 106.7875),
new Point(22.86694, 106.7029),
new Point(22.91253, 105.8771),
new Point(23.32416, 105.3587),
new Point(23.18027, 104.9075),
new Point(22.81805, 104.7319),
new Point(22.6875, 104.3747),
new Point(22.79812, 104.1113),
new Point(22.50387, 103.9687),
new Point(22.78287, 103.6538),
new Point(22.58436, 103.5224),
new Point(22.79451, 103.3337),
new Point(22.43652, 103.0304),
new Point(22.77187, 102.4744),
new Point(22.39629, 102.1407),
new Point(22.49777, 101.7415),
new Point(22.20916, 101.5744),
new Point(21.83444, 101.7653),
new Point(21.14451, 101.786),
new Point(21.17687, 101.2919),
new Point(21.57264, 101.1482),
new Point(21.76903, 101.099),
new Point(21.47694, 100.6397),
new Point(21.43546, 100.2057),
new Point(21.72555, 99.97763),
new Point(22.05018, 99.95741),
new Point(22.15592, 99.16785),
new Point(22.93659, 99.56484),
new Point(23.08204, 99.5113),
new Point(23.18916, 98.92747),
new Point(23.97076, 98.67991),
new Point(24.16007, 98.89073),
new Point(23.92999, 97.54762),
new Point(24.26055, 97.7593),
new Point(24.47666, 97.54305),
new Point(24.73992, 97.55255),
new Point(25.61527, 98.19109),
new Point(25.56944, 98.36137),
new Point(25.85597, 98.7104),
new Point(26.12527, 98.56944),
new Point(26.18472, 98.73109),
new Point(26.79166, 98.77777),
new Point(27.52972, 98.69699),
new Point(27.6725, 98.45888),
new Point(27.54014, 98.31992),
new Point(28.14889, 98.14499),
new Point(28.54652, 97.55887),
new Point(28.22277, 97.34888),
new Point(28.46749, 96.65387),
new Point(28.35111, 96.40193),
new Point(28.525, 96.34027),
new Point(28.79569, 96.61373),
new Point(29.05666, 96.47083),
new Point(28.90138, 96.17532),
new Point(29.05972, 96.14888),
new Point(29.25757, 96.39172),
new Point(29.46444, 96.08315),
new Point(29.03527, 95.38777),
new Point(29.33346, 94.64751),
new Point(29.07348, 94.23456),
new Point(28.6692, 93.96172),
new Point(28.61876, 93.35194),
new Point(28.3193, 93.22205),
new Point(28.1419, 92.71044),
new Point(27.86194, 92.54498),
new Point(27.76472, 91.65776),
new Point(27.945, 91.66277),
new Point(28.08111, 91.30138),
new Point(27.96999, 91.08693),
new Point(28.07958, 90.3765),
new Point(28.24257, 90.38898),
new Point(28.32369, 89.99819),
new Point(28.05777, 89.48749),
new Point(27.32083, 88.91693)
};
/**
* 台湾区域
*/
private static final Point[] TAIWAN = new Point[]{
new Point(25.13474, 121.4441),
new Point(25.28361, 121.5632),
new Point(25.00722, 122.0004),
new Point(24.85028, 121.8182),
new Point(24.47638, 121.8397),
new Point(23.0875, 121.3556),
new Point(21.92791, 120.7196),
new Point(22.31277, 120.6103),
new Point(22.54044, 120.3071),
new Point(23.04437, 120.0539),
new Point(23.61708, 120.1112),
new Point(25.00166, 121.0017),
new Point(25.13474, 121.4441)
};
/**
* 海南区域
*/
private static final Point[] HAINAN = new Point[]{
new Point(19.52888, 110.855),
new Point(19.16761, 110.4832),
new Point(18.80083, 110.5255),
new Point(18.3852, 110.0503),
new Point(18.39152, 109.7594),
new Point(18.19777, 109.7036),
new Point(18.50562, 108.6871),
new Point(19.28028, 108.6283),
new Point(19.76, 109.2939),
new Point(19.7236, 109.1653),
new Point(19.89972, 109.2572),
new Point(19.82861, 109.4658),
new Point(19.99389, 109.6108),
new Point(20.13361, 110.6655),
new Point(19.97861, 110.9425),
new Point(19.63829, 111.0215),
new Point(19.52888, 110.855)
};
/**
* 崇明县
*/
private static final Point[] CHONGMING = new Point[]{
new Point(31.80054, 121.2039),
new Point(31.49972, 121.8736),
new Point(31.53111, 121.5464),
new Point(31.80054, 121.2039)
};
//endregion
/**
* 中国行政边界的WGS84坐标数据
* 光线投射算法 (Ray casting algorithm) 获得
* 沿海国界周边地区可能会有误差更高精度需要调整坐标点
*/
private static final List<Point[]> CHINA_POLYGON = new ArrayList<>();
static {
CHINA_POLYGON.add(MAINLAND);
CHINA_POLYGON.add(TAIWAN);
CHINA_POLYGON.add(HAINAN);
CHINA_POLYGON.add(CHONGMING);
}
}

View File

@ -0,0 +1,170 @@
package com.ruoyi.common.utils.map;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class MapUtils {
public static final class MapU {
public static final double EARTH_RADIUS = 6378.137;
public static final int N_2 = 2;
public static final int N_1000 = 1000;
public static final double N_180 = 180.0;
}
/**
* 判断一个点是否在圆形区域内
* @param pointLon 判断点经度
* @param pointLat 判断点维度
* @param centerLon 圆心经度
* @param centerLat 圆心维度
* @param radius 半径
* @return boolean
*/
public static boolean isInCircle(double pointLon, double pointLat, double centerLon, double centerLat, double radius) {
return getDistance(pointLon, pointLat, centerLon, centerLat) < radius;
}
/**
* 判断是否在多边形区域内
*
* @param pointLon 要判断的点的经度
* @param pointLat 要判断的点的维度
* @param lon 区域各顶点的经度数组
* @param lat 区域各顶点的维度数组
* @return
*/
public static boolean isInPolygon(double pointLon, double pointLat, double[] lon, double[] lat) {
// 将要判断的横纵坐标组成一个点
Point2D.Double point = new Point2D.Double(pointLon, pointLat);
// 将区域各顶点的横纵坐标放到一个点集合里面
List<Point2D.Double> pointList = new ArrayList<>();
double polygonPoint_x, polygonPoint_y;
for (int i = 0; i < lon.length; i++) {
polygonPoint_x = lon[i];
polygonPoint_y = lat[i];
Point2D.Double polygonPoint = new Point2D.Double(polygonPoint_x, polygonPoint_y);
pointList.add(polygonPoint);
}
return areaCheck(point, pointList);
}
/**
* 判断是否在多边形区域内
*
* @param point 要判断的点
* @param polygon 区域点集合
* @return
*/
public static boolean areaCheck(Point2D.Double point, List<Point2D.Double> polygon) {
java.awt.geom.GeneralPath generalPath = new java.awt.geom.GeneralPath();
Point2D.Double first = polygon.get(0);
// 通过移动到指定坐标以双精度指定将一个点添加到路径中
generalPath.moveTo(first.x, first.y);
polygon.remove(0);
for (Point2D.Double d : polygon) {
// 通过绘制一条从当前坐标到新指定坐标以双精度指定的直线将一个点添加到路径中
generalPath.lineTo(d.x, d.y);
}
// 将几何多边形封闭
generalPath.lineTo(first.x, first.y);
generalPath.closePath();
// 测试指定的 Point2D 是否在 Shape 的边界内
return generalPath.contains(point);
}
/**
* 获取两个点间的距离
*
* @param lonA1 A1点的经度
* @param latA1 A1点的纬度
* @param lonA2 A2点的经度
* @param latA2 A2点的纬度
* @return
*/
public static double getDistance(double lonA1, double latA1, double lonA2, double latA2) {
// 单位()
double lon1 = lonA1 * Math.PI / MapU.N_180;
double lat1 = latA1 * Math.PI / MapU.N_180;
double lon2 = lonA2 * Math.PI / MapU.N_180;
double lat2 = latA2 * Math.PI / MapU.N_180;
double dlon = lon2 - lon1;
double dlat = lat2 - lat1;
double a = Math.pow(Math.sin(dlat / MapU.N_2), MapU.N_2) +
Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(dlon / MapU.N_2), MapU.N_2);
double c = MapU.N_2 * Math.asin(Math.sqrt(a));
return c * MapU.EARTH_RADIUS * MapU.N_1000;
}
public static boolean isPtInPoly(double ALon , double ALat , Point[] ps) {
int iSum, iCount, iIndex;
double dLon1 = 0, dLon2 = 0, dLat1 = 0, dLat2 = 0, dLon;
if (ps.length < 3) {
return false;
}
iSum = 0;
iCount = ps.length;
for (iIndex = 0; iIndex<iCount;iIndex++) {
if (iIndex == iCount - 1) {
dLon1 = ps[iIndex].getX();
dLat1 = ps[iIndex].getY();
dLon2 = ps[0].getX();
dLat2 = ps[0].getY();
} else {
dLon1 = ps[iIndex].getX();
dLat1 = ps[iIndex].getY();
dLon2 = ps[iIndex + 1].getX();
dLat2 = ps[iIndex + 1].getY();
}
// 以下语句判断A点是否在边的两端点的水平平行线之间在则可能有交点开始判断交点是否在左射线上
if (((ALat >= dLat1) && (ALat < dLat2)) || ((ALat >= dLat2) && (ALat < dLat1))) {
if (Math.abs(dLat1 - dLat2) > 0) {
//得到 A点向左射线与边的交点的x坐标
dLon = dLon1 - ((dLon1 - dLon2) * (dLat1 - ALat) ) / (dLat1 - dLat2);
// 如果交点在A点左侧说明是做射线与 边的交点则射线与边的全部交点数加一
if (dLon < ALon) {
iSum++;
}
}
}
}
if ((iSum % 2) != 0) {
return true;
}
return false;
}
public static void main(String[] args) {
//114.331951,30.64091#114.341049,30.610185#114.331436,30.588058#114.312038,30.56393#114.293498,30.558609#114.267922,30.563784#114.231185,30.57945#114.212303,30.601616#114.235649,30.626878#114.280624,30.646818#
Map[] map=new Map[]{};
Point[] ps = new Point[] { new Point(114.309914,30.599556),//114.309914,30.599556
new Point(114.295688,30.592879),//114.295688,30.592879
new Point(114.292812,30.587726), //114.292812,30.587726
new Point(114.292812,30.587726), //114.292812,30.587726
new Point(114.30058,30.580318),//114.30058,30.580318
new Point(114.303606,30.586959),//114.303606,30.586959
new Point(114.304534,30.594751),//114.304534,30.594751
new Point(114.30838,30.590131),//114.30838,30.590131
new Point(114.308651,30.584182),//114.308651,30.584182
new Point(114.304495,30.584015),//114.304495,30.584015
new Point(114.301301,30.578759),//114.301301,30.578759
new Point(114.309437,30.578528),//114.309437,30.578528
new Point(114.323282,30.592786)};//114.323282,30.592786
Point n1 = new Point(114.303217,30.583553);
Point n2 = new Point(114.307336,30.597592);
Point n3 = new Point(114.286565,30.590056);
Point y1 = new Point(114.227342,30.587987);
Point y2 = new Point(120.1866 , 30.2672);
Point y4 = new Point(120.1869 , 30.2718);
System.out.println( "n1:" + isPtInPoly(n1.getX() , n1.getY() , ps));
System.out.println( "n2:" + isPtInPoly(n2.getX() , n2.getY() , ps));
System.out.println( "n3:" + isPtInPoly(n3.getX() , n3.getY() , ps));
System.out.println( "y1:" + isPtInPoly(y1.getX() , y1.getY() , ps));
System.out.println( "y2:" + isPtInPoly(y2.getX() , y2.getY() , ps));
System.out.println( "y4:" + isPtInPoly(y4.getX() , y4.getY() , ps));
}
}

View File

@ -0,0 +1,23 @@
package com.ruoyi.common.utils.map;
public class Point {
private Double x;
private Double y;
public Point (Double x , Double y) {
this.x = x;
this.y = y;
}
public Double getX() {
return x;
}
public void setX(Double x) {
this.x = x;
}
public Double getY() {
return y;
}
public void setY(Double y) {
this.y = y;
}
}

View File

@ -2,8 +2,12 @@ package com.ruoyi.bst.area.domain;
import java.math.BigDecimal;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.validate.ValidGroup;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ -27,6 +31,8 @@ public class Area extends BaseEntity
@Excel(name = "区域名称")
@ApiModelProperty("区域名称")
@NotBlank(message = "区域名称不能为空", groups = {ValidGroup.Create.class})
@Size(max = 50, message = "区域名称不能超过50个字符")
private String name;
@Excel(name = "边界值")

View File

@ -14,4 +14,12 @@ public class AreaVO extends Area {
@ApiModelProperty("创建人名称")
private String createName;
@ApiModelProperty("停车区数量")
private Integer parkingAreaCount;
@ApiModelProperty("禁停区数量")
private Integer noParkingAreaCount;
@ApiModelProperty("禁行区数量")
private Integer noRideAreaCount;
}

View File

@ -125,7 +125,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">#{userId},</if>
<if test="name != null and name != ''">#{name},</if>
<if test="boundary != null">#{boundary},</if>
<if test="boundary != null">ST_GeomFromText(#{boundary}),</if>
<if test="boundaryStr != null">#{boundaryStr},</if>
<if test="longitude != null">#{longitude},</if>
<if test="latitude != null">#{latitude},</if>
@ -169,7 +169,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<sql id="updateColumns">
<if test="data.userId != null">user_id = #{data.userId},</if>
<if test="data.name != null and data.name != ''">name = #{data.name},</if>
<if test="data.boundary != null">boundary = #{data.boundary},</if>
<if test="data.boundary != null">boundary = ST_GeomFromText(#{data.boundary}),</if>
<if test="data.boundaryStr != null">boundary_str = #{data.boundaryStr},</if>
<if test="data.longitude != null">longitude = #{data.longitude},</if>
<if test="data.latitude != null">latitude = #{data.latitude},</if>

View File

@ -0,0 +1,14 @@
package com.ruoyi.bst.area.service;
import java.util.List;
import com.ruoyi.bst.area.domain.AreaVO;
public interface AreaAssembler {
/**
* 组装子区域数量
*/
void assembleSubAreaCount(List<AreaVO> list);
}

View File

@ -0,0 +1,68 @@
package com.ruoyi.bst.area.service.impl;
import java.util.List;
import java.util.Objects;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.bst.area.domain.AreaVO;
import com.ruoyi.bst.area.service.AreaAssembler;
import com.ruoyi.bst.areaSub.domain.AreaSubQuery;
import com.ruoyi.bst.areaSub.domain.enums.AreaSubType;
import com.ruoyi.bst.areaSub.domain.vo.AreaSubCountTypeByAreaVO;
import com.ruoyi.bst.areaSub.service.AreaSubDashboard;
import com.ruoyi.common.utils.collection.CollectionUtils;
@Service
public class AreaAssemblerImpl implements AreaAssembler {
@Autowired
private AreaSubDashboard areaSubDashboard;
@Override
public void assembleSubAreaCount(List<AreaVO> list) {
if (CollectionUtils.isEmptyElement(list)) {
return;
}
AreaSubQuery query = new AreaSubQuery();
query.setAreaIds(CollectionUtils.map(list, AreaVO::getId));
List<AreaSubCountTypeByAreaVO> countList = areaSubDashboard.selectTypeCountGroupByAreaId(query);
for (AreaVO area : list) {
// 停车区
AreaSubCountTypeByAreaVO parkingAreaCount = countList.stream()
.filter(item -> Objects.equals(item.getAreaId(), area.getId()) && AreaSubType.PARKING.getCode().equals(item.getType()))
.findFirst().orElse(null);
if (parkingAreaCount != null) {
area.setParkingAreaCount(parkingAreaCount.getCount());
} else {
area.setParkingAreaCount(0);
}
// 禁停区
AreaSubCountTypeByAreaVO noParkingAreaCount = countList.stream()
.filter(item -> Objects.equals(item.getAreaId(), area.getId()) && AreaSubType.NO_PARKING.getCode().equals(item.getType()))
.findFirst().orElse(null);
if (noParkingAreaCount != null) {
area.setNoParkingAreaCount(noParkingAreaCount.getCount());
} else {
area.setNoParkingAreaCount(0);
}
// 禁行区
AreaSubCountTypeByAreaVO noRideAreaCount = countList.stream()
.filter(item -> Objects.equals(item.getAreaId(), area.getId()) && AreaSubType.NO_RIDE.getCode().equals(item.getType()))
.findFirst().orElse(null);
if (noRideAreaCount != null) {
area.setNoRideAreaCount(noRideAreaCount.getCount());
} else {
area.setNoRideAreaCount(0);
}
}
}
}

View File

@ -2,6 +2,9 @@ package com.ruoyi.bst.area.service.impl;
import java.util.List;
import com.ruoyi.common.utils.map.GeoUtils;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.geom.Geometry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -19,6 +22,7 @@ import com.ruoyi.common.utils.DateUtils;
* @date 2025-03-14
*/
@Service
@Slf4j
public class AreaServiceImpl implements AreaService
{
@Autowired
@ -57,6 +61,11 @@ public class AreaServiceImpl implements AreaService
@Override
public int insertArea(Area area)
{
// 将边界值转Geometry对象
if (area.getBoundaryStr() != null) {
Geometry geometry = GeoUtils.toGeometry(area.getBoundaryStr());
area.setBoundary(GeoUtils.wkt(geometry));
}
area.setCreateTime(DateUtils.getNowDate());
return areaMapper.insertArea(area);
}
@ -70,6 +79,11 @@ public class AreaServiceImpl implements AreaService
@Override
public int updateArea(Area area)
{
// 将边界值转Geometry对象
if (area.getBoundaryStr() != null) {
Geometry geometry = GeoUtils.toGeometry(area.getBoundaryStr());
area.setBoundary(GeoUtils.wkt(geometry));
}
return areaMapper.updateArea(area);
}

View File

@ -1,5 +1,8 @@
package com.ruoyi.bst.areaSub.domain;
import java.util.List;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -7,4 +10,8 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
public class AreaSubQuery extends AreaSubVO {
@ApiModelProperty(value = "区域ID列表")
private List<Long> areaIds;
}

View File

@ -0,0 +1,18 @@
package com.ruoyi.bst.areaSub.domain.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum AreaSubType {
PARKING("1", "停车区"),
NO_PARKING("2", "禁停区"),
NO_RIDE("3", "禁行区");
private String code;
private String name;
}

View File

@ -0,0 +1,18 @@
package com.ruoyi.bst.areaSub.domain.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class AreaSubCountTypeByAreaVO {
@ApiModelProperty("区域ID")
private Long areaId;
@ApiModelProperty("类型")
private String type;
@ApiModelProperty("数量")
private Integer count;
}

View File

@ -1,11 +1,14 @@
package com.ruoyi.bst.areaSub.mapper;
import java.util.List;
import com.ruoyi.bst.areaSub.domain.AreaSub;
import com.ruoyi.bst.areaSub.domain.AreaSubVO;
import com.ruoyi.bst.areaSub.domain.AreaSubQuery;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.bst.areaSub.domain.AreaSub;
import com.ruoyi.bst.areaSub.domain.AreaSubQuery;
import com.ruoyi.bst.areaSub.domain.AreaSubVO;
import com.ruoyi.bst.areaSub.domain.vo.AreaSubCountTypeByAreaVO;
/**
* 子区域Mapper接口
*
@ -38,16 +41,6 @@ public interface AreaSubMapper
*/
int insertAreaSub(AreaSub areaSub);
/**
* 批量新增子区域
*/
int batchInsert(@Param("list") List<? extends AreaSub> list);
/**
* 批量修改子区域
*/
int batchUpdate(@Param("list") List<? extends AreaSub> list);
/**
* 修改子区域
*
@ -71,4 +64,6 @@ public interface AreaSubMapper
* @return 结果
*/
public int deleteAreaSubByIds(Long[] ids);
List<AreaSubCountTypeByAreaVO> selectTypeCountGroupByAreaId(@Param("query") AreaSubQuery query);
}

View File

@ -79,7 +79,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="name != null">#{name},</if>
<if test="type != null">#{type},</if>
<if test="areaId != null">#{areaId},</if>
<if test="boundary != null">#{boundary},</if>
<if test="boundary != null">ST_GeomFromText(#{boundary}),</if>
<if test="boundaryStr != null">#{boundaryStr},</if>
<if test="error != null">#{error},</if>
<if test="longitude != null">#{longitude},</if>
@ -91,183 +91,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</trim>
</insert>
<insert id="batchInsert" parameterType="AreaSub" useGeneratedKeys="true" keyProperty="id">
insert into bst_area_sub
<trim prefix="(" suffix=")" suffixOverrides=",">
name,
type,
area_id,
boundary,
boundary_str,
error,
longitude,
latitude,
create_time,
picture,
remark,
status,
</trim>
values
<foreach collection="list" item="i" separator=",">
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="i.name != null ">#{i.name},</if>
<if test="i.name == null ">default,</if>
<if test="i.type != null ">#{i.type},</if>
<if test="i.type == null ">default,</if>
<if test="i.areaId != null ">#{i.areaId},</if>
<if test="i.areaId == null ">default,</if>
<if test="i.boundary != null ">#{i.boundary},</if>
<if test="i.boundary == null ">default,</if>
<if test="i.boundaryStr != null ">#{i.boundaryStr},</if>
<if test="i.boundaryStr == null ">default,</if>
<if test="i.error != null ">#{i.error},</if>
<if test="i.error == null ">default,</if>
<if test="i.longitude != null ">#{i.longitude},</if>
<if test="i.longitude == null ">default,</if>
<if test="i.latitude != null ">#{i.latitude},</if>
<if test="i.latitude == null ">default,</if>
<if test="i.createTime != null ">#{i.createTime},</if>
<if test="i.createTime == null ">default,</if>
<if test="i.picture != null ">#{i.picture},</if>
<if test="i.picture == null ">default,</if>
<if test="i.remark != null ">#{i.remark},</if>
<if test="i.remark == null ">default,</if>
<if test="i.status != null and i.status != ''">#{i.status},</if>
<if test="i.status == null or i.status == ''">default,</if>
</trim>
</foreach>
</insert>
<update id="batchUpdate">
update bst_area_sub
<trim prefix="SET" suffixOverrides=",">
<foreach open="name = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.name != null ">
WHEN #{item.id} THEN #{item.name}
</when>
<otherwise>
WHEN #{item.id} THEN `name`
</otherwise>
</choose>
</foreach>
<foreach open="type = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.type != null ">
WHEN #{item.id} THEN #{item.type}
</when>
<otherwise>
WHEN #{item.id} THEN `type`
</otherwise>
</choose>
</foreach>
<foreach open="area_id = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.areaId != null ">
WHEN #{item.id} THEN #{item.areaId}
</when>
<otherwise>
WHEN #{item.id} THEN `area_id`
</otherwise>
</choose>
</foreach>
<foreach open="boundary = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.boundary != null ">
WHEN #{item.id} THEN #{item.boundary}
</when>
<otherwise>
WHEN #{item.id} THEN `boundary`
</otherwise>
</choose>
</foreach>
<foreach open="boundary_str = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.boundaryStr != null ">
WHEN #{item.id} THEN #{item.boundaryStr}
</when>
<otherwise>
WHEN #{item.id} THEN `boundary_str`
</otherwise>
</choose>
</foreach>
<foreach open="error = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.error != null ">
WHEN #{item.id} THEN #{item.error}
</when>
<otherwise>
WHEN #{item.id} THEN `error`
</otherwise>
</choose>
</foreach>
<foreach open="longitude = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.longitude != null ">
WHEN #{item.id} THEN #{item.longitude}
</when>
<otherwise>
WHEN #{item.id} THEN `longitude`
</otherwise>
</choose>
</foreach>
<foreach open="latitude = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.latitude != null ">
WHEN #{item.id} THEN #{item.latitude}
</when>
<otherwise>
WHEN #{item.id} THEN `latitude`
</otherwise>
</choose>
</foreach>
<foreach open="create_time = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.createTime != null ">
WHEN #{item.id} THEN #{item.createTime}
</when>
<otherwise>
WHEN #{item.id} THEN `create_time`
</otherwise>
</choose>
</foreach>
<foreach open="picture = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.picture != null ">
WHEN #{item.id} THEN #{item.picture}
</when>
<otherwise>
WHEN #{item.id} THEN `picture`
</otherwise>
</choose>
</foreach>
<foreach open="remark = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.remark != null ">
WHEN #{item.id} THEN #{item.remark}
</when>
<otherwise>
WHEN #{item.id} THEN `remark`
</otherwise>
</choose>
</foreach>
<foreach open="status = CASE id" collection="list" item="item" close="END,">
<choose>
<when test="item.status != null and item.status != ''">
WHEN #{item.id} THEN #{item.status}
</when>
<otherwise>
WHEN #{item.id} THEN `status`
</otherwise>
</choose>
</foreach>
</trim>
where id in
<foreach item="item" collection="list" open="(" separator="," close=")">
#{item.id}
</foreach>
</update>
<update id="updateAreaSub" parameterType="AreaSub">
update bst_area_sub
<trim prefix="SET" suffixOverrides=",">
@ -280,7 +103,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="data.name != null">name = #{data.name},</if>
<if test="data.type != null">type = #{data.type},</if>
<if test="data.areaId != null">area_id = #{data.areaId},</if>
<if test="data.boundary != null">boundary = #{data.boundary},</if>
<if test="data.boundary != null">boundary = ST_GeomFromText(#{data.boundary}),</if>
<if test="data.boundaryStr != null">boundary_str = #{data.boundaryStr},</if>
<if test="data.error != null">error = #{data.error},</if>
<if test="data.longitude != null">longitude = #{data.longitude},</if>
@ -301,4 +124,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{id}
</foreach>
</delete>
<!-- selectTypeCountGroupByAreaId -->
<select id="selectTypeCountGroupByAreaId" resultType="AreaSubCountTypeByAreaVO">
select
bas.area_id,
bas.type,
count(bas.id) as `count`
from bst_area_sub bas
<where>
<include refid="searchCondition"/>
</where>
group by bas.area_id, bas.type
</select>
</mapper>

View File

@ -0,0 +1,16 @@
package com.ruoyi.bst.areaSub.service;
import java.util.List;
import com.ruoyi.bst.areaSub.domain.AreaSubQuery;
import com.ruoyi.bst.areaSub.domain.vo.AreaSubCountTypeByAreaVO;
public interface AreaSubDashboard {
/**
* 根据区域id查询类型统计
*/
List<AreaSubCountTypeByAreaVO> selectTypeCountGroupByAreaId(AreaSubQuery query);
}

View File

@ -0,0 +1,27 @@
package com.ruoyi.bst.areaSub.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.bst.areaSub.domain.AreaSubQuery;
import com.ruoyi.bst.areaSub.domain.vo.AreaSubCountTypeByAreaVO;
import com.ruoyi.bst.areaSub.mapper.AreaSubMapper;
import com.ruoyi.bst.areaSub.service.AreaSubDashboard;
@Service
public class AreaSubDashboardImpl implements AreaSubDashboard {
@Autowired
private AreaSubMapper areaSubMapper;
@Override
public List<AreaSubCountTypeByAreaVO> selectTypeCountGroupByAreaId(AreaSubQuery query) {
return areaSubMapper.selectTypeCountGroupByAreaId(query);
}
}

View File

@ -3,6 +3,8 @@ package com.ruoyi.bst.areaSub.service.impl;
import java.util.Collections;
import java.util.List;
import com.ruoyi.common.utils.map.GeoUtils;
import org.locationtech.jts.geom.Geometry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -59,6 +61,11 @@ public class AreaSubServiceImpl implements AreaSubService
public int insertAreaSub(AreaSub areaSub)
{
areaSub.setCreateTime(DateUtils.getNowDate());
// 将边界值转Geometry对象
if (areaSub.getBoundaryStr() != null) {
Geometry geometry = GeoUtils.toGeometry(areaSub.getBoundaryStr());
areaSub.setBoundary(GeoUtils.wkt(geometry));
}
return areaSubMapper.insertAreaSub(areaSub);
}
@ -71,6 +78,11 @@ public class AreaSubServiceImpl implements AreaSubService
@Override
public int updateAreaSub(AreaSub areaSub)
{
// 将边界值转Geometry对象
if (areaSub.getBoundaryStr() != null) {
Geometry geometry = GeoUtils.toGeometry(areaSub.getBoundaryStr());
areaSub.setBoundary(GeoUtils.wkt(geometry));
}
return areaSubMapper.updateAreaSub(areaSub);
}

View File

@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.bst.area.domain.Area;
import com.ruoyi.bst.area.domain.AreaQuery;
import com.ruoyi.bst.area.domain.AreaVO;
import com.ruoyi.bst.area.service.AreaAssembler;
import com.ruoyi.bst.area.service.AreaService;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
@ -41,6 +42,9 @@ public class AreaController extends BaseController
@Autowired
private AreaService areaService;
@Autowired
private AreaAssembler areaAssembler;
/**
* 查询运营区列表
*/
@ -51,6 +55,7 @@ public class AreaController extends BaseController
startPage();
startOrderBy();
List<AreaVO> list = areaService.selectAreaList(query);
areaAssembler.assembleSubAreaCount(list);
return getDataTable(list);
}
@ -85,6 +90,7 @@ public class AreaController extends BaseController
@PostMapping
public AjaxResult add(@RequestBody @Validated(ValidGroup.Create.class) Area area)
{
area.setCreateId(getUserId());
return toAjax(areaService.insertArea(area));
}