diff --git a/common-ruoyi/ruoyi-common/pom.xml b/common-ruoyi/ruoyi-common/pom.xml index 299a341..d4c90fb 100644 --- a/common-ruoyi/ruoyi-common/pom.xml +++ b/common-ruoyi/ruoyi-common/pom.xml @@ -15,8 +15,56 @@ common通用工具 + + + osgeo + OSGeo Release Repository + https://repo.osgeo.org/repository/release/ + false + true + + + + + + + org.geotools + gt-geojson + 24.0 + + + + + org.geotools + gt-epsg-hsql + 24.0 + test + + + + + org.geotools + gt-shapefile + 24.0 + + + + + org.geotools + gt-swing + 24.0 + + + + + org.geotools.jdbc + gt-jdbc-postgis + 24.0 + test + + commons-lang commons-lang @@ -193,4 +241,5 @@ + diff --git a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/GeoUtils.java b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/GeoUtils.java new file mode 100644 index 0000000..4c7a6e4 --- /dev/null +++ b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/GeoUtils.java @@ -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 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 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); // 返回给定点到多边形的最短距离 + } + +} diff --git a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/GpsCoordinateUtils.java b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/GpsCoordinateUtils.java new file mode 100644 index 0000000..715c8a9 --- /dev/null +++ b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/GpsCoordinateUtils.java @@ -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 CHINA_POLYGON = new ArrayList<>(); + + static { + CHINA_POLYGON.add(MAINLAND); + CHINA_POLYGON.add(TAIWAN); + CHINA_POLYGON.add(HAINAN); + CHINA_POLYGON.add(CHONGMING); + } + + +} diff --git a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/MapUtils.java b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/MapUtils.java new file mode 100644 index 0000000..a33118c --- /dev/null +++ b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/MapUtils.java @@ -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 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 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= 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)); + } +} diff --git a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/Point.java b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/Point.java new file mode 100644 index 0000000..a5a8d7a --- /dev/null +++ b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/Point.java @@ -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; + } +} diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/area/domain/Area.java b/ruoyi-service/src/main/java/com/ruoyi/bst/area/domain/Area.java index ac9d65a..69e5444 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/area/domain/Area.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/area/domain/Area.java @@ -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 = "边界值") diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/area/domain/AreaVO.java b/ruoyi-service/src/main/java/com/ruoyi/bst/area/domain/AreaVO.java index bd27eae..dc4493b 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/area/domain/AreaVO.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/area/domain/AreaVO.java @@ -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; } diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/area/mapper/AreaMapper.xml b/ruoyi-service/src/main/java/com/ruoyi/bst/area/mapper/AreaMapper.xml index 1051548..d64b7c6 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/area/mapper/AreaMapper.xml +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/area/mapper/AreaMapper.xml @@ -125,7 +125,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{userId}, #{name}, - #{boundary}, + ST_GeomFromText(#{boundary}), #{boundaryStr}, #{longitude}, #{latitude}, @@ -169,7 +169,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" user_id = #{data.userId}, name = #{data.name}, - boundary = #{data.boundary}, + boundary = ST_GeomFromText(#{data.boundary}), boundary_str = #{data.boundaryStr}, longitude = #{data.longitude}, latitude = #{data.latitude}, diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/AreaAssembler.java b/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/AreaAssembler.java new file mode 100644 index 0000000..0ef94db --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/AreaAssembler.java @@ -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 list); + +} diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/impl/AreaAssemblerImpl.java b/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/impl/AreaAssemblerImpl.java new file mode 100644 index 0000000..0ebafc4 --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/impl/AreaAssemblerImpl.java @@ -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 list) { + if (CollectionUtils.isEmptyElement(list)) { + return; + } + + AreaSubQuery query = new AreaSubQuery(); + query.setAreaIds(CollectionUtils.map(list, AreaVO::getId)); + List 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); + } + } + } + + +} \ No newline at end of file diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/impl/AreaServiceImpl.java b/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/impl/AreaServiceImpl.java index 12b862b..f795df6 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/impl/AreaServiceImpl.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/impl/AreaServiceImpl.java @@ -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); } diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/AreaSubQuery.java b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/AreaSubQuery.java index 2879a21..a7eff67 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/AreaSubQuery.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/AreaSubQuery.java @@ -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 areaIds; + + } diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/enums/AreaSubType.java b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/enums/AreaSubType.java new file mode 100644 index 0000000..e9e3fb0 --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/enums/AreaSubType.java @@ -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; +} diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/vo/AreaSubCountTypeByAreaVO.java b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/vo/AreaSubCountTypeByAreaVO.java new file mode 100644 index 0000000..3abbf56 --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/vo/AreaSubCountTypeByAreaVO.java @@ -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; + +} diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/mapper/AreaSubMapper.java b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/mapper/AreaSubMapper.java index ee278db..93e88b5 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/mapper/AreaSubMapper.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/mapper/AreaSubMapper.java @@ -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 list); - - /** - * 批量修改子区域 - */ - int batchUpdate(@Param("list") List list); - /** * 修改子区域 * @@ -71,4 +64,6 @@ public interface AreaSubMapper * @return 结果 */ public int deleteAreaSubByIds(Long[] ids); + + List selectTypeCountGroupByAreaId(@Param("query") AreaSubQuery query); } diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/mapper/AreaSubMapper.xml b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/mapper/AreaSubMapper.xml index 46ad74f..ada23fb 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/mapper/AreaSubMapper.xml +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/mapper/AreaSubMapper.xml @@ -79,7 +79,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{name}, #{type}, #{areaId}, - #{boundary}, + ST_GeomFromText(#{boundary}), #{boundaryStr}, #{error}, #{longitude}, @@ -91,183 +91,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" - - insert into bst_area_sub - - name, - type, - area_id, - boundary, - boundary_str, - error, - longitude, - latitude, - create_time, - picture, - remark, - status, - - values - - - #{i.name}, - default, - #{i.type}, - default, - #{i.areaId}, - default, - #{i.boundary}, - default, - #{i.boundaryStr}, - default, - #{i.error}, - default, - #{i.longitude}, - default, - #{i.latitude}, - default, - #{i.createTime}, - default, - #{i.picture}, - default, - #{i.remark}, - default, - #{i.status}, - default, - - - - - - update bst_area_sub - - - - - WHEN #{item.id} THEN #{item.name} - - - WHEN #{item.id} THEN `name` - - - - - - - WHEN #{item.id} THEN #{item.type} - - - WHEN #{item.id} THEN `type` - - - - - - - WHEN #{item.id} THEN #{item.areaId} - - - WHEN #{item.id} THEN `area_id` - - - - - - - WHEN #{item.id} THEN #{item.boundary} - - - WHEN #{item.id} THEN `boundary` - - - - - - - WHEN #{item.id} THEN #{item.boundaryStr} - - - WHEN #{item.id} THEN `boundary_str` - - - - - - - WHEN #{item.id} THEN #{item.error} - - - WHEN #{item.id} THEN `error` - - - - - - - WHEN #{item.id} THEN #{item.longitude} - - - WHEN #{item.id} THEN `longitude` - - - - - - - WHEN #{item.id} THEN #{item.latitude} - - - WHEN #{item.id} THEN `latitude` - - - - - - - WHEN #{item.id} THEN #{item.createTime} - - - WHEN #{item.id} THEN `create_time` - - - - - - - WHEN #{item.id} THEN #{item.picture} - - - WHEN #{item.id} THEN `picture` - - - - - - - WHEN #{item.id} THEN #{item.remark} - - - WHEN #{item.id} THEN `remark` - - - - - - - WHEN #{item.id} THEN #{item.status} - - - WHEN #{item.id} THEN `status` - - - - - where id in - - #{item.id} - - - update bst_area_sub @@ -280,7 +103,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" name = #{data.name}, type = #{data.type}, area_id = #{data.areaId}, - boundary = #{data.boundary}, + boundary = ST_GeomFromText(#{data.boundary}), boundary_str = #{data.boundaryStr}, error = #{data.error}, longitude = #{data.longitude}, @@ -301,4 +124,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{id} + + + + + diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/AreaSubDashboard.java b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/AreaSubDashboard.java new file mode 100644 index 0000000..119d644 --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/AreaSubDashboard.java @@ -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 selectTypeCountGroupByAreaId(AreaSubQuery query); + + +} \ No newline at end of file diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/impl/AreaSubDashboardImpl.java b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/impl/AreaSubDashboardImpl.java new file mode 100644 index 0000000..fd2a6b8 --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/impl/AreaSubDashboardImpl.java @@ -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 selectTypeCountGroupByAreaId(AreaSubQuery query) { + return areaSubMapper.selectTypeCountGroupByAreaId(query); + } + + + +} diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/impl/AreaSubServiceImpl.java b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/impl/AreaSubServiceImpl.java index ce8a7be..73be489 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/impl/AreaSubServiceImpl.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/impl/AreaSubServiceImpl.java @@ -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); } diff --git a/ruoyi-web/src/main/java/com/ruoyi/web/bst/AreaController.java b/ruoyi-web/src/main/java/com/ruoyi/web/bst/AreaController.java index 2a44b1e..c194451 100644 --- a/ruoyi-web/src/main/java/com/ruoyi/web/bst/AreaController.java +++ b/ruoyi-web/src/main/java/com/ruoyi/web/bst/AreaController.java @@ -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 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)); }