package org.sxkj.common.geo.utils; import org.sxkj.common.geo.entity.Feature; import org.sxkj.common.geo.entity.FlyAreaParam; import org.sxkj.common.geo.entity.GeoJson; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.sxkj.common.geo.entity.PointPOJO; import java.io.File; import java.io.IOException; import java.util.*; /** * 空间计算工具类 */ public class GeoUtils { /** * 默认地球半径 */ private static double EARTH_RADIUS = 6371000;//赤道半径(单位m) /** * 转化为弧度(rad) */ private static double rad(double d) { return d * Math.PI / 180.0; } /** * 计算中心经纬度与目标经纬度的距离(米) * * @param centerLon 中心精度 * @param centerLat 中心纬度 * @param targetLon 需要计算的精度 * @param targetLat 需要计算的纬度 * @return 米 */ public static double distance(double centerLon, double centerLat, double targetLon, double targetLat) { double jl_jd = 102834.74258026089786013677476285;// 每经度单位米; double jl_wd = 111712.69150641055729984301412873;// 每纬度单位米; double b = Math.abs((centerLat - targetLat) * jl_jd); double a = Math.abs((centerLon - targetLon) * jl_wd); return Math.sqrt((a * a + b * b)); } /** * 根据多个点计算中心点 * @param list * @return */ public static PointPOJO getCenterPoint(List list) { int total = list.size(); double X = 0, Y = 0, Z = 0; for (int i = 0; i < list.size(); i++) { PointPOJO point = list.get(i); double lat, lon, x, y, z; lon = point.getLon() * Math.PI / 180; lat = point.getLat() * Math.PI / 180; x = Math.cos(lat) * Math.cos(lon); y = Math.cos(lat) * Math.sin(lon); z = Math.sin(lat); X += x; Y += y; Z += z; } X = X / total; Y = Y / total; Z = Z / total; double Lon = Math.atan2(Y, X); double Hyp = Math.sqrt(X * X + Y * Y); double Lat = Math.atan2(Z, Hyp); PointPOJO centerPoint = new PointPOJO(); centerPoint.setLon( Lon * 180 / Math.PI); centerPoint.setLat(Lat * 180 / Math.PI); return centerPoint; } /** * 获取点区域并按从近到远排序 * @param flyAreaParam * @return */ public static List caculatePointList(FlyAreaParam flyAreaParam){ List> areaList = flyAreaParam.getAreaList(); PointPOJO dockPoint = flyAreaParam.getDockPoint(); Double radius = flyAreaParam.getRadius(); List centerPointList = new ArrayList<>(); //根据区域获取中心点 areaList.forEach(area -> { PointPOJO centerPoint = GeoUtils.getCenterPoint(area); centerPointList.add(centerPoint); }); //巡逻目标集合 List targetList = new ArrayList<>(); //获取所有中心点之后, centerPointList.forEach(centerPoint -> { //判断各个中心点和机场点的距离 double distance = GeoUtils.distance(dockPoint.getLon(), dockPoint.getLat(), centerPoint.getLon(), centerPoint.getLat()); //当距离小于半径时,添加到目标集合中 if (distance <= radius) { centerPoint.setDistance(distance); targetList.add(centerPoint); } }); //按距离从近到远排序 targetList.sort(Comparator.comparing(PointPOJO::getDistance)); return targetList; } /** * 根据geojson获取点区域并按从近到远排序 * @param geoJson * @return */ public static List caculatePointList(GeoJson geoJson,PointPOJO dockPoint,Double radius){ List features = geoJson.getFeatures(); List> areaList = new ArrayList<>(); features.forEach(feature -> { List list = new ArrayList<>(); List> coordinates = feature.getGeometry().getCoordinates().get(0); coordinates.forEach(coordinate ->{ PointPOJO pointPOJO = new PointPOJO(); pointPOJO.setLon(coordinate.get(0)); pointPOJO.setLat(coordinate.get(1)); list.add(pointPOJO); }); areaList.add(list); }); FlyAreaParam flyAreaParam = new FlyAreaParam(); flyAreaParam.setDockPoint(dockPoint); flyAreaParam.setRadius(radius); flyAreaParam.setAreaList(areaList); List pointPOJOS = caculatePointList(flyAreaParam); return pointPOJOS; } /** * 读取json文件 * @param path * @return */ public static GeoJson readJsonFile(String path){ try { File file = new File(path); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return objectMapper.readValue(file, GeoJson.class); } catch (IOException e) { e.printStackTrace(); return null; } } /** * 根据起点经纬度、方向和距离计算目标点经纬度 * * @param lat1 起点纬度(单位:度) * @param lon1 起点经度(单位:度) * @param bearing 方向角(单位:度,正北为0度,顺时针增加) * lat1:起点纬度(单位:度)。 * * lon1:起点经度(单位:度)。 * * bearing:方向角(单位:度,正北为0度,顺时针增加)。 * * distance:距离(单位:米) * @param distance 距离(单位:米) * @return 目标点的经纬度数组,格式为 [纬度, 经度] */ public static PointPOJO calculateDestination(double lat1, double lon1, double bearing, double distance) { // 将经纬度从度数转换为弧度 double lat1Rad = Math.toRadians(lat1); double lon1Rad = Math.toRadians(lon1); // 将方向角从度数转换为弧度 double bearingRad = Math.toRadians(bearing); // 计算目标点纬度 double lat2Rad = Math.asin(Math.sin(lat1Rad) * Math.cos(distance / EARTH_RADIUS) + Math.cos(lat1Rad) * Math.sin(distance / EARTH_RADIUS) * Math.cos(bearingRad)); // 计算目标点经度 double lon2Rad = lon1Rad + Math.atan2( Math.sin(bearingRad) * Math.sin(distance / EARTH_RADIUS) * Math.cos(lat1Rad), Math.cos(distance / EARTH_RADIUS) - Math.sin(lat1Rad) * Math.sin(lat2Rad)); // 将结果从弧度转换回度数 double lat2 = Math.toDegrees(lat2Rad); double lon2 = Math.toDegrees(lon2Rad); PointPOJO pointPOJO=new PointPOJO(); pointPOJO.setLat(lat2); pointPOJO.setLon(lon2); return pointPOJO; } /** * 方向与角度的关系 */ public static final Map DIRECTION_ANGLES = new HashMap<>(); static { DIRECTION_ANGLES.put("北", 0.0); DIRECTION_ANGLES.put("东北", 45.0); DIRECTION_ANGLES.put("东", 90.0); DIRECTION_ANGLES.put("东南", 135.0); DIRECTION_ANGLES.put("南", 180.0); DIRECTION_ANGLES.put("西南", 225.0); DIRECTION_ANGLES.put("西", 270.0); DIRECTION_ANGLES.put("西北", 315.0); } }