mirror of
https://github.com/xweiba/location-spoofer.git
synced 2026-09-21 22:30:46 +08:00
fix: 坐标转换修复、图钉键盘对齐、WiFi监控、UI优化
- 坐标转换: detectType 用地理边界判断瓦片类型,加载路径去掉重复 toStored - 图钉: 放 MKMapView 内部 + keyboard 通知双保险,针尖对齐地理中心 - 缩放控件: 转 UIKit 子视图放地图内,键盘弹起一起移动 - 网络监控: WiFi SSID 轮询 + 重连检测,虚拟定位激活时自动验证 - UI: 去飞行模式弹窗和面板,引导页只在首次安装和设置入口触发 - 实时定位: 蓝点数据优先,不限时缓存
This commit is contained in:
@@ -2,26 +2,105 @@ import Foundation
|
||||
|
||||
/// GCJ-02 (火星坐标) ↔ WGS-84 坐标转换。
|
||||
///
|
||||
/// 在中国地区,MKMapView 使用高德 (AutoNavi) 瓦片数据(GCJ-02 坐标系),
|
||||
/// 因此从 MKMapView 的 `centerCoordinate`、`convert(point:toCoordinateFrom:)`
|
||||
/// 等方法返回的坐标也是 GCJ-02。但 CoreLocation / CLLocationManager 返回的
|
||||
/// 以及 Apple wloc 定位服务使用的都是 WGS-84。
|
||||
///
|
||||
/// 虚拟定位的坐标流中:
|
||||
/// - 地图 UI 层(显示、选点):GCJ-02(与瓦片一致)
|
||||
/// - 代理写出层(wloc 响应改写):WGS-84
|
||||
///
|
||||
/// 因此需要在坐标从地图 UI 进入代理之前做 GCJ-02 → WGS-84 转换。
|
||||
/// MKMapView 根据实时定位动态切换瓦片源:中国境内用高德 GCJ-02,境外用 Apple WGS-84。
|
||||
/// App 内部统一以 WGS-84 存储,仅在地图交互时按当前瓦片类型双向转换。
|
||||
enum CoordinateConverter {
|
||||
// 椭球参数 (Krasovsky 1940)
|
||||
private static let a = 6378245.0
|
||||
private static let ee = 0.00669342162296594323
|
||||
|
||||
/// 坐标类型
|
||||
enum CoordType: String {
|
||||
case gcj02 = "GCJ-02"
|
||||
case wgs84 = "WGS-84"
|
||||
}
|
||||
|
||||
// MARK: - 全局瓦片类型
|
||||
|
||||
/// 当前地图瓦片坐标系(每次从地图拿到坐标后更新)
|
||||
@MainActor static var currentTileType = CoordType.gcj02
|
||||
|
||||
/// 从地图坐标推算当前瓦片类型并更新全局状态
|
||||
@MainActor
|
||||
static func updateTileType(lat: Double, lon: Double) {
|
||||
let type = detectType(lat: lat, lon: lon)
|
||||
if type != currentTileType {
|
||||
currentTileType = type
|
||||
RuntimeLogger.info("APP", "坐标转换", "地图瓦片切换 → \(type.rawValue)", details: [
|
||||
"坐标": "\(lat), \(lon)"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 存取转换
|
||||
|
||||
/// 地图坐标 → WGS-84 存储
|
||||
@MainActor
|
||||
static func toStored(lat: Double, lon: Double) -> (lat: Double, lon: Double) {
|
||||
updateTileType(lat: lat, lon: lon)
|
||||
guard currentTileType == .gcj02 else {
|
||||
RuntimeLogger.info("APP", "坐标转换", "toStored: WGS-84 → 不转", details: [
|
||||
"lat": String(lat), "lon": String(lon)
|
||||
])
|
||||
return (lat, lon)
|
||||
}
|
||||
let wgs = gcj02ToWgs84(lat: lat, lon: lon)
|
||||
let d = distance(lat1: lat, lon1: lon, lat2: wgs.lat, lon2: wgs.lon)
|
||||
RuntimeLogger.info("APP", "坐标转换", "toStored: GCJ-02 → WGS-84", details: [
|
||||
"原始": "\(lat), \(lon)",
|
||||
"结果": "\(wgs.lat), \(wgs.lon)",
|
||||
"偏移": String(format: "%.0fm", d)
|
||||
])
|
||||
return wgs
|
||||
}
|
||||
|
||||
/// WGS-84 存储 → 当前地图瓦片坐标系(显示用)
|
||||
@MainActor
|
||||
static func toDisplay(lat: Double, lon: Double) -> (lat: Double, lon: Double) {
|
||||
guard currentTileType == .gcj02 else {
|
||||
RuntimeLogger.info("APP", "坐标转换", "toDisplay: WGS-84 → 不转", details: [
|
||||
"lat": String(lat), "lon": String(lon)
|
||||
])
|
||||
return (lat, lon)
|
||||
}
|
||||
let gcj = wgs84ToGcj02(lat: lat, lon: lon)
|
||||
let d = distance(lat1: lat, lon1: lon, lat2: gcj.lat, lon2: gcj.lon)
|
||||
RuntimeLogger.info("APP", "坐标转换", "toDisplay: WGS-84 → GCJ-02", details: [
|
||||
"原始": "\(lat), \(lon)",
|
||||
"结果": "\(gcj.lat), \(gcj.lon)",
|
||||
"偏移": String(format: "%.0fm", d)
|
||||
])
|
||||
return gcj
|
||||
}
|
||||
|
||||
// MARK: - 类型检测
|
||||
|
||||
/// Haversine 距离(米)
|
||||
static func distance(lat1: Double, lon1: Double, lat2: Double, lon2: Double) -> Double {
|
||||
let r = 6371000.0
|
||||
let dLat = (lat2 - lat1) * .pi / 180.0
|
||||
let dLon = (lon2 - lon1) * .pi / 180.0
|
||||
let a = sin(dLat / 2) * sin(dLat / 2)
|
||||
+ cos(lat1 * .pi / 180.0) * cos(lat2 * .pi / 180.0)
|
||||
* sin(dLon / 2) * sin(dLon / 2)
|
||||
return r * 2 * atan2(sqrt(a), sqrt(1 - a))
|
||||
}
|
||||
|
||||
/// MKMapView 瓦片坐标系:中国境内 GCJ-02,境外 WGS-84
|
||||
static func detectType(lat: Double, lon: Double) -> CoordType {
|
||||
// GCJ-02 加密只在中国境内生效,用地理边界判断
|
||||
if lat > 17.5 && lat < 54.0 && lon > 72.5 && lon < 136.0 {
|
||||
return .gcj02
|
||||
}
|
||||
return .wgs84
|
||||
}
|
||||
|
||||
// MARK: - 核心转换
|
||||
|
||||
/// GCJ-02 → WGS-84(迭代法,精度优于 0.5 米)
|
||||
static func gcj02ToWgs84(lat: Double, lon: Double) -> (lat: Double, lon: Double) {
|
||||
var wgsLat = lat
|
||||
var wgsLon = lon
|
||||
// 两次迭代足以收敛到亚米级精度
|
||||
for _ in 0..<2 {
|
||||
let d = delta(lat: wgsLat, lon: wgsLon)
|
||||
wgsLat = lat - d.lat
|
||||
@@ -30,6 +109,14 @@ enum CoordinateConverter {
|
||||
return (wgsLat, wgsLon)
|
||||
}
|
||||
|
||||
/// WGS-84 → GCJ-02
|
||||
static func wgs84ToGcj02(lat: Double, lon: Double) -> (lat: Double, lon: Double) {
|
||||
let d = delta(lat: lat, lon: lon)
|
||||
return (lat + d.lat, lon + d.lon)
|
||||
}
|
||||
|
||||
// MARK: - 内部
|
||||
|
||||
/// 计算偏移量 (WGS-84 → GCJ-02 的增量)
|
||||
private static func delta(lat: Double, lon: Double) -> (lat: Double, lon: Double) {
|
||||
let dLat = transformLat(x: lon - 105.0, y: lat - 35.0)
|
||||
|
||||
Reference in New Issue
Block a user