| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- // 随机扰动函数
- function randomPerturbation(scale = 1e-7) {
- return (Math.random() * 2 - 1) * scale
- }
- // Haversine 公式计算两点距离(米)
- function haversine(lat1, lon1, lat2, lon2) {
- const R = 6371000
- const toRad = (deg) => deg * Math.PI / 180
- const phi1 = toRad(lat1)
- const phi2 = toRad(lat2)
- const dphi = toRad(lat2 - lat1)
- const dlambda = toRad(lon2 - lon1)
- const a = Math.sin(dphi / 2) ** 2 + Math.cos(phi1) * Math.cos(phi2) * Math.sin(dlambda / 2) ** 2
- return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
- }
- /**
- * 根据原路径时间,重新生成新的时间路径数据
- */
- function getPathData(pathlist, runEndTime, useTime) {
- const startTime = (runEndTime - useTime) * 1000
- const oldStartTime = parseInt(pathlist[0].d.split(' ')[0])
- const newPathlist = pathlist.map((item, i) => {
- const newItem = { ...item }
- const [oldTimeStr, suffix] = item.d.split(' ', 2)
- const oldTime = parseInt(oldTimeStr)
- if (i === 0) {
- newItem.d = `${startTime} ${suffix}`
- } else {
- const newTime = startTime + oldTime - oldStartTime
- newItem.d = `${newTime} ${suffix}`
- newItem.a += randomPerturbation()
- newItem.o += randomPerturbation()
- }
- return newItem
- })
- return newPathlist
- }
- /**
- * 选择打卡点
- */
- function selectCheckpoints(path, checkpoints, runLogNum, pointUpdateDistance, logMaxDistance, runEndTime, pathTime) {
- const results = []
- let totalDistance = 0
- let lastCheckpointDistance = 0
- // 筛选有效打卡点
- const filteredCheckpoints = checkpoints
- .filter(cp => cp.is_del === "0" && cp.is_online === 1 && cp.ctrl_status === "1")
- // 打乱顺序
- for (let i = filteredCheckpoints.length - 1; i > 0; i--) {
- const j = Math.floor(Math.random() * (i + 1))
- // 避免解构交换在部分运行环境下触发 TDZ 相关异常
- const temp = filteredCheckpoints[i]
- filteredCheckpoints[i] = filteredCheckpoints[j]
- filteredCheckpoints[j] = temp
- }
- const usedCheckpoints = new Set()
- for (const p of path) {
- const parts = p.d.split(' ')
- const tsMs = parseInt(parts[0])
- const stepDistance = parseFloat(parts[1].split('_')[0]) || 0
- totalDistance += stepDistance
- for (const cp of filteredCheckpoints.slice(0, -1)) {
- if (usedCheckpoints.has(cp.id)) continue
- const [lat, lon] = cp.jingwei.split(',').map(Number)
- const dist = haversine(p.a, p.o, lat, lon)
- if (dist < logMaxDistance && (totalDistance - lastCheckpointDistance) > 200) {
- console.log(`选中打卡点 ${cp.id} ${cp.address},距离:${dist}`)
- results.push({
- point_id: cp.id,
- distance: (totalDistance / 1000).toFixed(2),
- longitude: p.o,
- longtitude: p.o, // 保留原字段
- latitude: p.a,
- address: cp.address,
- jingwei: cp.jingwei.split(','),
- time: Math.floor(tsMs / 1000)
- })
- usedCheckpoints.add(cp.id)
- lastCheckpointDistance = totalDistance
- break
- }
- }
- }
- if (results.length >= runLogNum) {
- // 随机抽取 runLogNum 个打卡点
- const indices = []
- while (indices.length < runLogNum) {
- const idx = Math.floor(Math.random() * results.length)
- if (!indices.includes(idx)) indices.push(idx)
- }
- indices.sort((a, b) => a - b)
- const selectedResults = indices.map(i => results[i])
- const n = selectedResults.length
- let d = 0
- for (let i = 0; i < n; i++) {
- if (selectedResults[i].time >= runEndTime - 5) {
- console.log("打卡点时间异常,重新分配时间")
- for (let j = 0; j < n; j++) {
- selectedResults[j].time = Math.floor(runEndTime - (pathTime / n) * (n - j))
- selectedResults[j].distance = d + 0.3 * (j + 1)
- }
- break
- }
- }
- console.log(`选中打卡点:${JSON.stringify(selectedResults)}`)
- return selectedResults
- } else {
- console.log(`选中打卡点数量不足:${JSON.stringify(results)}`)
- return false
- }
- }
- /**
- * 根据距离和用时生成步频数据
- */
- function generateCadence(distanceKm, usedTime) {
- const paceSec = usedTime / distanceKm
- const paceMin = paceSec / 60
- let spmRange
- if (paceMin >= 8) spmRange = [85, 110]
- else if (paceMin >= 5) spmRange = [150, 170]
- else spmRange = [170, 190]
- const minutes = Math.ceil(usedTime / 60)
- const cadenceList = []
- for (let i = 0; i < minutes; i++) {
- let spm = Math.floor(Math.random() * (spmRange[1] - spmRange[0] + 1) + spmRange[0])
- if (i === minutes - 1) {
- const lastDuration = usedTime - (minutes - 1) * 60
- spm = Math.round(spm * (lastDuration / 60))
- }
- cadenceList.push(spm)
- }
- const totalSteps = cadenceList.reduce((a, b) => a + b, 0)
- return {
- cadence_list: cadenceList,
- total_steps: totalSteps
- }
- }
- module.exports = {
- getPathData,
- selectCheckpoints,
- generateCadence
- }
|