chuangte_bike_newxcx/common/trackMapUtil.js
2026-05-21 09:43:14 +08:00

132 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 轨迹点解析与清洗:兼容小程序 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,
}))
}