package com.flightfeather.uav.common.utils
|
|
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 {*[]}*/
|
|
/**
|
* 根据坐标点、距离和角度,获取另一个坐标
|
* @param pos 坐标点(GPS系)
|
* @param len 距离(米)
|
* @param radian 弧度
|
*/
|
fun getPointByLen(pos: Pair<Double, Double>, len: Double, radian: Double): Pair<Double, Double> {
|
val dx = len * sin(radian)
|
val dy = len * cos(radian)
|
val ec = Eb + (Ea - Eb) * (90.0 - pos.second) / 90.0
|
val ed = ec * cos(pos.second * PI / 180)
|
val lng = (dx / ed + pos.first * PI / 180.0) * 180.0 / PI
|
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).toDouble() / 1000000;
|
val mglng = Math.round((lng * 2 - lng - dlng) * 1000000).toDouble() / 1000000;
|
return mglng to mglat
|
}
|
}
|
|
/**
|
* 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
|
}
|
}
|
}
|