Commit 5d52878c authored by p x's avatar p x
Browse files

新增通过点移动计算是否通过

parent 745a80a3
...@@ -17,10 +17,11 @@ android { ...@@ -17,10 +17,11 @@ android {
buildTypes { buildTypes {
release { release {
isMinifyEnabled = false isMinifyEnabled = false
proguardFiles( consumerProguardFiles("proguard-rules.pro")
/* proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" "proguard-rules.pro"
) )*/
} }
} }
compileOptions { compileOptions {
......
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
package commons.gis package commons.gis
import android.location.Location
import kotlin.math.atan2 import kotlin.math.atan2
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.pow import kotlin.math.pow
...@@ -11,9 +12,53 @@ object BasicTools { ...@@ -11,9 +12,53 @@ object BasicTools {
/** /**
* 计算2点距离 * 计算2点距离
* Android Location 类自带的计算方法
* @return 返回米 * @return 返回米
* **/ */
fun calculateHaversineDistance(lng1: Double, lat1: Double, lng2: Double,lat2: Double): Double { fun calculateDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Float {
val results = FloatArray(1)
Location.distanceBetween(lat1, lon1, lat2, lon2, results)
return results[0] // 单位:米
}
/**
* 计算2点距离
* Android Location 类自带的计算方法
* @return 返回结果为:Pair(距离, 初始方位角(起点到终点))
*/
fun calculateDistancePair(
lat1: Double,
lon1: Double,
lat2: Double,
lon2: Double
): Pair<Float, Float> {
val results = FloatArray(2)
Location.distanceBetween(lat1, lon1, lat2, lon2, results)
return Pair(results[0], (results[1] + 360) % 360)
// return Pair(results[0], results[1])
}
/**
* 标准化方位角,确保结果在 0.0° ~ 360.0° 范围内
* @param bearing 原始方位角(可能为负数)
* @return 标准化后的方位角
*/
private fun normalizeBearing(bearing: Float): Float {
// 取模运算处理循环,再处理边界值
var temp = bearing % 360
if (temp < 0) {
temp += 360f
}
return temp
}
/**
* 计算2点距离
* 纯 Java 实现 Haversine 公式(近似计算)
* @return 返回米
*/
fun calculateHaversineDistance(lng1: Double, lat1: Double, lng2: Double, lat2: Double): Double {
val earthRadius = 6371000.0 // 地球半径,单位:米 val earthRadius = 6371000.0 // 地球半径,单位:米
val lat1 = Math.toRadians(lat1) val lat1 = Math.toRadians(lat1)
......
package commons.gis;
/**
* 星际旅行概论(坐标系偏移工具类)
* 支持GC02、BD09和WGS84坐标系之间互相转化
* @author marquis
*
*/
public class CrsShiftUtils {
/**
* π
*/
private final static double PI = 3.1415926535897931;
/**
* 长半轴
*/
private final static double aa = 6378245.0;
/**
* 偏心率平方
*/
private final static double ee = 0.00669342162296594323;
private final static double ee1 = 0.0066934216229659433;
/**
* 默认偏移中心点坐标
*/
private final static double centerLng = 105.0;
private final static double centerLat = 35.0;
private final static double centerLng1 = 100.0;
private final static double centerLat1 = 30.0;
/**
* 偏移/反偏移方向
* @author marquis
*
*/
public enum Method {
WGS84_2_GCJ02,
GCJ02_2_WGS84,
WGS84_2_BD09,
BD09_2_WGS84,
GCJ02_2_BD09,
BD09_2_GCJ02
}
/**
* 禁止实例化
*/
private CrsShiftUtils() {
}
/**
* 二维点数据的坐标系转换
* @param lng
* @param lat
* @param method
* @return
*/
public static double[] convert(double lng, double lat, Method method) {
double ret[];
switch (method) {
case WGS84_2_GCJ02:
ret = shift2Mars(lng, lat, Math.PI, aa, ee, lng, lat);
ret[0] = lng + ret[0];
ret[1] = lat + ret[1];
return ret;
case GCJ02_2_WGS84:
ret = shift2Mars(lng, lat, Math.PI, aa, ee, centerLng, centerLat);
ret[0] = lng - ret[0];
ret[1] = lat - ret[1];
return ret;
case WGS84_2_BD09:
ret = shift2Mars(lng, lat, Math.PI, aa, ee, centerLng, centerLat);
ret[0] = lng + ret[0];
ret[1] = lat + ret[1];
return mars2Saturn(ret[0], ret[1], Math.PI);
case BD09_2_WGS84:
ret = saturn2Mars(lng, lat, Math.PI);
ret = shift2Mars(ret[0], ret[1], Math.PI, aa, ee, centerLng, centerLat);
ret[0] = lng - ret[0];
ret[1] = lat - ret[1];
return ret;
case GCJ02_2_BD09:
return mars2Saturn(lng, lat, Math.PI);
case BD09_2_GCJ02:
return saturn2Mars(lng, lat, Math.PI);
default:
ret = new double[2];
ret[0] = lng;
ret[1] = lat;
return ret;
}
}
/**
* 计算火星坐标系的偏移量,正向偏移时坐标加偏移量,反向偏移时减偏移量
* @param lng
* @param lat
* @param pi
* @param a
* @param e
* @param cLng
* @param cLat
* @return
*/
private static double[] shift2Mars(double lng, double lat, double pi, double a, double e, double cLng, double cLat) {
if (outOfChina(lng, lat)) {
double[] ret = new double[2];
ret[0] = lng;
ret[1] = lat;
return ret;
}
double dlat = transformlat(lng - cLng, lat - cLat, pi);
double dlng = transformlng(lng - cLng, lat - cLat, pi);
double radlat = lat / 180.0 * pi;
double magic = Math.sin(radlat);
magic = 1 - e * magic * magic;
double sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);
return new double[]{dlng, dlat};
}
/**
* 计算火星坐标系到土星坐标系
* @param lng
* @param lat
* @param pi
* @return
*/
private static double[] mars2Saturn(double lng, double lat, double pi) {
if (outOfChina(lng, lat)) {
double[] ret = new double[2];
ret[0] = lng;
ret[1] = lat;
return ret;
}
double magic = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * pi);
double theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * pi);
double bdLng = magic * Math.cos(theta) + 0.0065;
double bdLat = magic * Math.sin(theta) + 0.006;
return new double[] { bdLng, bdLat };
}
/**
* 计算土星坐标系到火星坐标系
* @param lng
* @param lat
* @param pi
* @return
*/
private static double[] saturn2Mars(double lng, double lat, double pi) {
if (outOfChina(lng, lat)) {
return new double[] { lng, lat };
}
double bdLng = lng - 0.0065, bdLat = lat - 0.006;
double magic = Math.sqrt(bdLng * bdLng + bdLat * bdLat) - 0.00002 * Math.sin(bdLat * pi);
double theta = Math.atan2(bdLat, bdLng) - 0.000003 * Math.cos(bdLng * pi);
double gcjLng = magic * Math.cos(theta);
double gcjLat = magic * Math.sin(theta);
return new double[] { gcjLng, gcjLat };
}
/**
* 转换经度
* @param lng
* @param lat
* @param pi
* @return
*/
private static double transformlng(double lng, double lat, double pi) {
double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lng * pi) + 40.0 * Math.sin(lng / 3.0 * pi)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(lng / 12.0 * pi) + 300.0 * Math.sin(lng / 30.0 * pi)) * 2.0 / 3.0;
return ret;
}
/**
* 转换维度
* @param lng
* @param lat
* @param pi
* @return
*/
private static double transformlat(double lng, double lat, double pi) {
double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat
+ 0.2 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lat * pi) + 40.0 * Math.sin(lat / 3.0 * pi)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(lat / 12.0 * pi) + 320 * Math.sin(lat * pi / 30.0)) * 2.0 / 3.0;
return ret;
}
/**
* 简单粗暴的判断是否在国内,不在国内不做偏移
* @param lng
* @param lat
* @return
*/
private static boolean outOfChina(double lng, double lat) {
//return false;
return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55);
}
}
package commons.gis
/**
* 利用方位角+距离判断是否经过目标点位的工具类
*/
object PassThroughDetector {
// 距离阈值:可根据场景调整(步行50米,驾车200米)
private val distanceThresholdM: Float = 100.0f
// 方位角变化阈值:允许的误差范围
private val bearingThresholdDeg: Float = 15.0f
private var speedThresholdMps: Float = 1.0f // 速度阈值(米/秒),1m/s ≈ 3.6km/h
// 记录上一次的方位角(-1 表示首次调用)
private var lastBearingToTarget: Float = -1f
// 记录是否曾接近目标点(距离≤阈值)
private var isApproached: Boolean = false
/**
* 判断是否经过目标点位
* @param currentLat 当前位置纬度
* @param currentLng 当前位置经度
* @param targetLat 目标点纬度
* @param targetLng 目标点经度
* @return true=已经过,false=未经过
*/
fun isPassThroughTarget(
currentLat: Double,
currentLng: Double,
targetLat: Double,
targetLng: Double
): Boolean {
/*
* 速度过滤:低于阈值直接返回false,不更新任何状态
* 驾车 5.0 ~ 10.0 换算(km/h) 18 ~ 36 过滤停车 / 堵车状态
*
* */
/* if (currentSpeed < speedThresholdMps) {
return false
}*/
// 1. 计算当前位置到目标点的距离和方位角
var (distanceToTarget, currentBearingToTarget) = BasicTools.calculateDistancePair(
currentLat, currentLng,
targetLat, targetLng,
)
// currentBearingToTarget= normalizeBearing(currentBearingToTarget)
// 2. 判断是否接近目标点
if (distanceToTarget <= distanceThresholdM) {
isApproached = true
}
// 3. 首次调用:记录初始方位角,返回未经过
if (lastBearingToTarget == -1f) {
lastBearingToTarget = currentBearingToTarget
return false
}
// 4. 计算方位角变化(处理360°循环)
var bearingDiff = Math.abs(currentBearingToTarget - lastBearingToTarget)
// bearingDiff = Math.min(bearingDiff, 360 - bearingDiff)
// println("距离: ${distanceToTarget}米 初始方位角: ${currentBearingToTarget} 方位角变化${bearingDiff}")
// 核心判断:曾接近 + 方位角突变≈180° → 判定经过
val isPassThrough = if (isApproached && bearingDiff >= (180 - bearingThresholdDeg)
// && bearingDiff <= (180 + bearingThresholdDeg)
) {
reset() // 重置状态避免重复判定
true
} else {
false
}
// 5. 更新上一次的方位角
lastBearingToTarget = currentBearingToTarget
return isPassThrough
}
/**
* 标准化方位角为 0~360°(处理负数)
*/
private fun normalizeBearing(bearing: Float): Float {
var normalizedBearing = bearing % 360
if (normalizedBearing < 0) {
normalizedBearing += 360
}
return normalizedBearing
}
/**
* 重置检测器状态(开始新的判断时调用)
*/
fun reset() {
lastBearingToTarget = -1f
isApproached = false
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment