chuangte_bike_newxcx/common/trackMapUtil.js

132 lines
3.8 KiB
JavaScript
Raw Normal View History

2026-05-21 09:43:14 +08:00
/**
* 轨迹点解析与清洗兼容小程序 map 组件 polyline减少乱线/飘移
* - 仅保留合法经纬度支持 lat/lng 字段别名
* - 修正常见的经纬度字段写反|lat|>90
* - 按时间稳定排序
* - 剔除明显 GPS 飞点短时超高速跳变
*/
function haversineMeters(lat1, lon1, lat2, lon2) {
const R = 6371000
const φ1 = (lat1 * Math.PI) / 180
const φ2 = (lat2 * Math.PI) / 180
const Δφ = ((lat2 - lat1) * Math.PI) / 180
const Δλ = ((lon2 - lon1) * Math.PI) / 180
const a =
Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2)
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
return R * c
}
/**
* 从单行日志解析纬度经度已修正写反
*/
export function parseTrackLatLng(raw) {
if (!raw || typeof raw !== 'object') return null
let lat = parseFloat(raw.latitude != null ? raw.latitude : raw.lat)
let lng = parseFloat(
raw.longitude != null ? raw.longitude : raw.lng != null ? raw.lng : raw.lon
)
if (!isFinite(lat) || !isFinite(lng)) return null
if (lat === 0 && lng === 0) return null
// 设备/接口偶发把经度写进 latitude中国境内经度约 70~140|lat|会>90
if (Math.abs(lat) > 90) {
const t = lat
lat = lng
lng = t
}
if (Math.abs(lat) > 90 || Math.abs(lng) > 180) return null
return { latitude: lat, longitude: lng }
}
/**
* 去掉短时超大跳跃点km/h
*/
export function filterGpsSpikes(points, maxSpeedKmH) {
if (!points || points.length < 2) return points || []
const out = [points[0]]
for (let i = 1; i < points.length; i++) {
const prev = out[out.length - 1]
const cur = points[i]
const tPrev = new Date(prev.time).getTime()
const tCur = new Date(cur.time).getTime()
const dt = Math.max(1, (tCur - tPrev) / 1000)
if (!isFinite(dt) || dt <= 0) {
out.push(cur)
continue
}
const d = haversineMeters(
prev.latitude,
prev.longitude,
cur.latitude,
cur.longitude
)
const speedKmh = (d / dt) * 3.6
if (speedKmh > maxSpeedKmH) continue
out.push(cur)
}
return out
}
/**
* @param {Array} rows 接口返回的 locationLog 列表
* @param {(row: object) => object} mapExtra 合并到轨迹点的额外字段不含 latitude/longitude/time
* @param {{ maxSpeedKmH?: number }} options
*/
export function normalizeLocationLogList(rows, mapExtra, options) {
const maxSpeedKmH = (options && options.maxSpeedKmH) || 180
if (!Array.isArray(rows) || rows.length === 0) return []
const mapped = []
for (let i = 0; i < rows.length; i++) {
const row = rows[i]
const ll = parseTrackLatLng(row)
if (!ll) continue
const extra = typeof mapExtra === 'function' ? mapExtra(row) || {} : {}
mapped.push({
...ll,
...extra,
time: row.at,
_sortIndex: i,
})
}
mapped.sort((a, b) => {
const ta = new Date(a.time).getTime()
const tb = new Date(b.time).getTime()
const na = isNaN(ta)
const nb = isNaN(tb)
if (na && nb) return a._sortIndex - b._sortIndex
if (na) return 1
if (nb) return -1
if (ta !== tb) return ta - tb
return a._sortIndex - b._sortIndex
})
const deduped = []
for (let j = 0; j < mapped.length; j++) {
const p = mapped[j]
const last = deduped[deduped.length - 1]
if (
last &&
last.latitude === p.latitude &&
last.longitude === p.longitude &&
last.time === p.time
) {
continue
}
deduped.push(p)
}
const filtered = filterGpsSpikes(deduped, maxSpeedKmH)
return filtered.map(({ _sortIndex, ...rest }) => rest)
}
/**
* 小程序 map.polyline.points 仅传经纬度避免多余字段干扰原生解析
*/
export function toPolylinePointList(trackPoints) {
if (!trackPoints || !trackPoints.length) return []
return trackPoints.map((p) => ({
latitude: p.latitude,
longitude: p.longitude,
}))
}