From f1ea263462d1f3f10a886fb083536a38b03a1ceb Mon Sep 17 00:00:00 2001
From: feiyu02 <risaku@163.com>
Date: 星期三, 14 五月 2025 17:33:04 +0800
Subject: [PATCH] 1. 新增动态污染溯源的数据异常判断逻辑 2. 新增动态污染溯源websocket连接功能

---
 src/main/kotlin/com/flightfeather/uav/common/utils/MapUtil.kt |  270 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 267 insertions(+), 3 deletions(-)

diff --git a/src/main/kotlin/com/flightfeather/uav/common/utils/MapUtil.kt b/src/main/kotlin/com/flightfeather/uav/common/utils/MapUtil.kt
index f45b316..5ae24c7 100644
--- a/src/main/kotlin/com/flightfeather/uav/common/utils/MapUtil.kt
+++ b/src/main/kotlin/com/flightfeather/uav/common/utils/MapUtil.kt
@@ -1,13 +1,15 @@
 package com.flightfeather.uav.common.utils
 
-import kotlin.math.PI
-import kotlin.math.cos
-import kotlin.math.sin
+import kotlin.math.*
 
 object MapUtil {
 
     private const val Ea = 6378137 //璧ら亾鍗婂緞
     private const val Eb = 6356725 //鏋佸崐寰�
+
+    // 鍧愭爣杞崲鍙傛暟
+    const val a = 6378245.0; //闀垮崐杞�
+    const val ee = 0.00669342162296594323; //鎵佺巼/*** GCJ02 杞崲涓� WGS84* @param lng* @param lat* @returns {*[]}*/
 
     /**
      * 鏍规嵁鍧愭爣鐐广�佽窛绂诲拰瑙掑害锛岃幏鍙栧彟涓�涓潗鏍�
@@ -24,4 +26,266 @@
         val lat = (dy / ec + pos.second * PI / 180.0) * 180.0 / PI
         return Pair(lng, lat)
     }
+
+    /**
+     * 鑾峰彇涓や釜缁忕含搴︿箣闂寸殑瑙掑害锛�0搴�-360搴︼級
+     */
+    fun getAngle(lngA: Double, latA: Double, lngB: Double, latB: Double): Double {
+        val a = ((90 - latB) * Math.PI) / 180;
+        val b = ((90 - latA) * Math.PI) / 180;
+        val AOC_BOC = ((lngB - lngA) * Math.PI) / 180;
+        val cosc = cos(a) * Math.cos(b) + Math.sin(a) * Math.sin(b) * Math.cos(AOC_BOC);
+        val sinc = Math.sqrt(1 - cosc * cosc);
+        val sinA = (Math.sin(a) * Math.sin(AOC_BOC)) / sinc;
+        val A = (Math.asin(sinA) * 180) / Math.PI;
+        var res = 0.0;
+        if (lngB > lngA && latB > latA) res = A;
+        else if (lngB > lngA && latB < latA) res = 180 - A;
+        else if (lngB < lngA && latB < latA) res = 180 - A;
+        else if (lngB < lngA && latB > latA) res = 360 + A;
+        else if (lngB > lngA && latB == latA) res = 90.0;
+        else if (lngB < lngA && latB == latA) res = 270.0;
+        else if (lngB == lngA && latB > latA) res = 0.0;
+        else if (lngB == lngA && latB < latA) res = 180.0;
+        return res;
+    }
+
+    /**
+     * 鑾峰彇涓ょ粡绾害闂寸殑璺濈
+     * @return 杩斿洖涓ょ偣闂磋窛绂伙紝鍗曚綅锛氱背
+     */
+    fun getDistance(lng1: Double, lat1: Double, lng2: Double, lat2: Double): Double {
+//        lat1 = lat1 || 0;
+//        lng1 = lng1 || 0;
+//        lat2 = lat2 || 0;
+//        lng2 = lng2 || 0;
+
+        val rad1 = (lat1 * Math.PI) / 180.0;
+        val rad2 = (lat2 * Math.PI) / 180.0;
+        val a = rad1 - rad2;
+        val b = (lng1 * Math.PI) / 180.0 - (lng2 * Math.PI) / 180.0;
+        val distance =
+            Ea * 2 * asin(
+                Math.sqrt(
+                    Math.pow(
+                        Math.sin(a / 2),
+                        2.0
+                    ) + Math.cos(rad1) * Math.cos(rad2) * Math.pow(Math.sin(b / 2), 2.0)
+                )
+            );
+
+        return distance;
+    }
+
+    /**
+     * 瑙掑害澧炲噺锛岀‘淇濊搴﹀浜�0 - 360搴︿箣闂�
+     * @param angle 鍘熻搴�
+     * @param offset 鍋忕Щ閲�
+     */
+    fun plusAngle(angle: Double, offset: Double): Double {
+        val result = angle + offset;
+        return if (result > 360) {
+            result - 360;
+        } else if (result < 0) {
+            result + 360;
+        } else {
+            result;
+        }
+    }
+
+    /**
+     * 璁$畻澶氳竟褰㈢殑鍥涜嚦鑼冨洿
+     * @param polygon 澶氳竟褰㈠潗鏍囩偣鏁扮粍
+     * @return 鍥涜嚦鑼冨洿锛岄『搴忎负鏈�灏忕粡搴︼紝鏈�澶х粡搴�, 鏈�灏忕含搴︼紝鏈�澶х含搴�
+     */
+    fun calFourBoundaries(polygon: List<Pair<Double, Double>>): List<Double> {
+        // 璁$畻澶氳竟褰㈤《鐐圭粡搴﹁寖鍥村拰绾害鑼冨洿
+        val xsSort = polygon.map { it.first }.sorted()
+        val ysSort = polygon.map { it.second }.sorted()
+
+        val xMin = xsSort[0]
+        val yMin = ysSort[0]
+        val xMax = xsSort[xsSort.lastIndex]
+        val yMax = ysSort[ysSort.lastIndex]
+
+        return listOf(xMin, xMax, yMin, yMax)
+    }
+
+    /**
+     * 鍒ゆ柇鍧愭爣鐐规槸鍚﹀湪澶氳竟褰㈢殑鍥涜嚦鑼冨洿鍐�
+     * @param point 鍧愭爣鐐�
+     * @param polygon 澶氳竟褰㈠潗鏍囩偣鏁扮粍
+     */
+    fun inBBox(point: Pair<Double, Double>, polygon: List<Pair<Double, Double>>): Boolean {
+
+        val x = point.first
+        val y = point.second
+        // 璁$畻澶氳竟褰㈤《鐐圭粡搴﹁寖鍥村拰绾害鑼冨洿
+        val fb = calFourBoundaries(polygon)
+//        val xsSort = polygon.map { it.first }.sorted()
+//        val ysSort = polygon.map { it.second }.sorted()
+//
+//        val xMin = xsSort[0]
+//        val yMin = ysSort[0]
+//        val xMax = xsSort[xsSort.lastIndex]
+//        val yMax = ysSort[ysSort.lastIndex]
+
+        val xMin = fb[0]
+        val xMax = fb[1]
+        val yMin = fb[2]
+        val yMax = fb[3]
+
+        return x >= xMin && x <= xMax && y >= yMin && y <= yMax
+    }
+
+    /**
+     * 鍒ゆ柇鍧愭爣鐐规槸鍚﹀湪澶氳竟褰㈢殑杈逛笂
+     * @param point 鍧愭爣鐐�
+     * @param polygon 澶氳竟褰㈠潗鏍囩偣鏁扮粍
+     */
+    fun onBorder(point: Pair<Double, Double>, polygon: List<Pair<Double, Double>>): Boolean {
+        var res = false
+        // 寰幆鍒ゆ柇姣忎竴鏉¤竟
+        for (i in polygon.indices) {
+            val p1 = polygon[i]
+            val p2 = if (i + 1 == polygon.size) {
+                polygon[0]
+            } else {
+                polygon[i + 1]
+            }
+            // 璁$畻杈圭殑涓や釜椤剁偣绾害宸拰缁忓害宸殑姣斿��
+            val k1 = (p2.second - p1.second) / (p2.first - p1.first)
+            // 璁$畻鍧愭爣鐐瑰拰鍏朵腑涓�涓《鐐圭殑绾害宸拰缁忓害宸殑姣斿��
+            val k2 = (p2.second - point.second) / (p2.first - point.first)
+            // 濡傛灉姣斿�肩浉鍚岋紝璇存槑涓変釜鐐瑰湪鍚屼竴鐩寸嚎涓婏紝鍗冲潗鏍囩偣鍦ㄨ竟涓�
+            if (k1 == k2) {
+                res = true
+                break
+            }
+        }
+        return res
+    }
+
+    /**
+     * 鍒ゆ柇鍧愭爣鐐规槸鍚﹀湪澶氳竟褰㈠唴閮�(灏勭嚎娉�)
+     * @param point 鍧愭爣鐐�
+     * @param polygon 澶氳竟褰㈠潗鏍囩偣鏁扮粍
+     */
+    fun inPolygon(point: Pair<Double, Double>, polygon: List<Pair<Double, Double>>): Boolean {
+        val x = point.first
+        val y = point.second
+        var j = polygon.size - 1
+        var odd = false
+        for (i in polygon.indices) {
+            if (
+                ((polygon[i].second > y) != (polygon[j].second > y))
+                && (x < ((polygon[j].first - polygon[i].first) * (y - polygon[i].second)
+                        / (polygon[j].second - polygon[i].second) + polygon[i].first))
+            ) {
+                odd = !odd;
+            }
+            j = i;
+        }
+        return odd
+    }
+
+    /**
+     * 鍒ゆ柇鍧愭爣鐐规槸鍚﹀湪澶氳竟褰㈠唴閮�
+     */
+    fun isPointInPolygon(point: Pair<Double, Double>, polygon: List<Pair<Double, Double>>): Boolean {
+        if (polygon.size < 3) throw IllegalArgumentException("not a polygon")
+
+        // 涓嶅湪鍥涜嚦鑼冨洿鍐咃紝鍒欎竴瀹氫笉鍦ㄥ杈瑰舰鍐�
+        if (!inBBox(point, polygon)) return false
+        // 鍦ㄥ杈瑰舰杈逛笂锛屼篃璁や负鍦ㄥ杈瑰舰鍐�
+        if (onBorder(point, polygon)) return true
+        // 璁$畻鏄惁鍦ㄥ杈瑰舰鍐呴儴
+        return inPolygon(point, polygon)
+    }
+
+    /**
+     * 鍒ゆ柇缁忕含搴︽槸鍚﹀湪鍥藉唴
+     * @return true: 缁忕含搴︿笉鍦ㄥ浗鍐咃紝false锛氱粡绾害鍦ㄥ浗鍐�
+     */
+    fun outOfChina(point: Pair<Double, Double>): Boolean {
+        val lng = point.first
+        val lat = point.second
+        return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55)
+    }
+
+    private fun transformLat(point: Pair<Double, Double>): Double {
+        val lng = point.first
+        val lat = point.second
+        var ret = -100.0 +
+                2.0 * lng +
+                3.0 * lat +
+                0.2 * lat * lat +
+                0.1 * lng * lat +
+                0.2 * sqrt(abs(lng))
+        ret += ((20.0 * sin(6.0 * lng * PI) + 20.0 * sin(2.0 * lng * PI)) * 2.0) / 3.0
+        ret += ((20.0 * sin(lat * PI) + 40.0 * sin((lat / 3.0) * PI)) * 2.0) / 3.0
+        ret += ((160.0 * sin((lat / 12.0) * PI) + 320 * sin((lat * PI) / 30.0)) * 2.0) / 3.0
+        return ret;
+    }
+
+    private fun transformLng(point: Pair<Double, Double>): Double {
+        val lng = point.first
+        val lat = point.second
+        var ret = 300.0 +
+                lng +
+                2.0 * lat +
+                0.1 * lng * lng +
+                0.1 * lng * lat +
+                0.1 * sqrt(abs(lng));
+        ret += ((20.0 * sin(6.0 * lng * PI) + 20.0 * sin(2.0 * lng * PI)) * 2.0) / 3.0;
+        ret += ((20.0 * sin(lng * PI) + 40.0 * sin((lng / 3.0) * PI)) * 2.0) / 3.0;
+        ret += ((150.0 * sin((lng / 12.0) * PI) + 300.0 * sin((lng / 30.0) * PI)) * 2.0) / 3.0
+        return ret;
+    }
+
+    /**
+     * 鐏槦鍧愭爣绯昏浆WGS84鍧愭爣绯�
+     */
+    fun gcj02ToWgs84(point: Pair<Double, Double>): Pair<Double, Double> {
+        if (outOfChina(point)) {
+            return point;
+        } else {
+            val lng = point.first
+            val lat = point.second
+            var dlat = transformLat(lng - 105.0 to lat - 35.0);
+            var dlng = transformLng(lng - 105.0 to lat - 35.0);
+            val radlat = (lat / 180.0) * PI;
+            var magic = sin(radlat);
+            magic = 1 - ee * magic * magic;
+            val sqrtmagic = sqrt(magic);
+            dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI);
+            dlng = (dlng * 180.0) / ((a / sqrtmagic) * cos(radlat) * PI);
+            val mglat = Math.round((lat * 2 - lat - dlat) * 1000000) / 1000000;
+            val mglng = Math.round((lng * 2 - lng - dlng) * 1000000) / 1000000;
+            return mglng.toDouble() to mglat.toDouble()
+        }
+    }
+
+    /**
+     * WGS84鍧愭爣绯昏浆鐏槦鍧愭爣绯�
+     */
+    fun wgs84ToGcj02(point: Pair<Double, Double>): Pair<Double, Double> {
+        if (outOfChina(point)) {
+            return point
+        } else {
+            val lng = point.first
+            val lat = point.second
+            var dLat = transformLat(lng - 105.0 to lat - 35.0);
+            var dLon = transformLng(lng - 105.0 to lat - 35.0);
+            val radLat = (lat / 180.0) * PI;
+            var magic = sin(radLat);
+            magic = 1 - ee * magic * magic;
+            val sqrtMagic = sqrt(magic);
+            dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * PI);
+            dLon = (dLon * 180.0) / ((a / sqrtMagic) * cos(radLat) * PI);
+            val mgLat = lat + dLat;
+            val mgLon = lng + dLon;
+            return mgLon to mgLat
+        }
+    }
 }
\ No newline at end of file

--
Gitblit v1.9.3