Path.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // 随机扰动函数
  2. function randomPerturbation(scale = 1e-7) {
  3. return (Math.random() * 2 - 1) * scale
  4. }
  5. // Haversine 公式计算两点距离(米)
  6. function haversine(lat1, lon1, lat2, lon2) {
  7. const R = 6371000
  8. const toRad = (deg) => deg * Math.PI / 180
  9. const phi1 = toRad(lat1)
  10. const phi2 = toRad(lat2)
  11. const dphi = toRad(lat2 - lat1)
  12. const dlambda = toRad(lon2 - lon1)
  13. const a = Math.sin(dphi / 2) ** 2 + Math.cos(phi1) * Math.cos(phi2) * Math.sin(dlambda / 2) ** 2
  14. return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
  15. }
  16. /**
  17. * 根据原路径时间,重新生成新的时间路径数据
  18. */
  19. function getPathData(pathlist, runEndTime, useTime) {
  20. const startTime = (runEndTime - useTime) * 1000
  21. const oldStartTime = parseInt(pathlist[0].d.split(' ')[0])
  22. const newPathlist = pathlist.map((item, i) => {
  23. const newItem = { ...item }
  24. const [oldTimeStr, suffix] = item.d.split(' ', 2)
  25. const oldTime = parseInt(oldTimeStr)
  26. if (i === 0) {
  27. newItem.d = `${startTime} ${suffix}`
  28. } else {
  29. const newTime = startTime + oldTime - oldStartTime
  30. newItem.d = `${newTime} ${suffix}`
  31. newItem.a += randomPerturbation()
  32. newItem.o += randomPerturbation()
  33. }
  34. return newItem
  35. })
  36. console.log(newPathlist)
  37. return newPathlist
  38. }
  39. /**
  40. * 选择打卡点
  41. */
  42. function selectCheckpoints(path, checkpoints, runLogNum, pointUpdateDistance, logMaxDistance, runEndTime, pathTime) {
  43. const results = []
  44. let totalDistance = 0
  45. let lastCheckpointDistance = 0
  46. // 筛选有效打卡点
  47. const filteredCheckpoints = checkpoints
  48. .filter(cp => cp.is_del === "0" && cp.is_online === 1 && cp.ctrl_status === "1")
  49. // 打乱顺序
  50. for (let i = filteredCheckpoints.length - 1; i > 0; i--) {
  51. const j = Math.floor(Math.random() * (i + 1))
  52. [filteredCheckpoints[i], filteredCheckpoints[j]] = [filteredCheckpoints[j], filteredCheckpoints[i]]
  53. }
  54. const usedCheckpoints = new Set()
  55. for (const p of path) {
  56. const parts = p.d.split(' ')
  57. const tsMs = parseInt(parts[0])
  58. const stepDistance = parseFloat(parts[1].split('_')[0]) || 0
  59. totalDistance += stepDistance
  60. for (const cp of filteredCheckpoints.slice(0, -1)) {
  61. if (usedCheckpoints.has(cp.id)) continue
  62. const [lat, lon] = cp.jingwei.split(',').map(Number)
  63. const dist = haversine(p.a, p.o, lat, lon)
  64. if (dist < logMaxDistance && (totalDistance - lastCheckpointDistance) > 200) {
  65. console.log(`选中打卡点 ${cp.id} ${cp.address},距离:${dist}`)
  66. results.push({
  67. point_id: cp.id,
  68. distance: +(totalDistance / 1000).toFixed(2),
  69. longitude: p.o,
  70. longtitude: p.o, // 保留原字段
  71. latitude: p.a,
  72. address: cp.address,
  73. jingwei: cp.jingwei.split(','),
  74. time: Math.floor(tsMs / 1000)
  75. })
  76. usedCheckpoints.add(cp.id)
  77. lastCheckpointDistance = totalDistance
  78. break
  79. }
  80. }
  81. }
  82. if (results.length >= runLogNum) {
  83. // 随机抽取 runLogNum 个打卡点
  84. const indices = []
  85. while (indices.length < runLogNum) {
  86. const idx = Math.floor(Math.random() * results.length)
  87. if (!indices.includes(idx)) indices.push(idx)
  88. }
  89. indices.sort((a, b) => a - b)
  90. const selectedResults = indices.map(i => results[i])
  91. const n = selectedResults.length
  92. let d = 0
  93. for (let i = 0; i < n; i++) {
  94. if (selectedResults[i].time >= runEndTime - 5) {
  95. console.log("打卡点时间异常,重新分配时间")
  96. for (let j = 0; j < n; j++) {
  97. selectedResults[j].time = Math.floor(runEndTime - (pathTime / n) * (n - j))
  98. selectedResults[j].distance = d + 0.3 * (j + 1)
  99. }
  100. break
  101. }
  102. }
  103. console.log(`选中打卡点:${JSON.stringify(selectedResults)}`)
  104. return selectedResults
  105. } else {
  106. console.log(`选中打卡点数量不足:${JSON.stringify(results)}`)
  107. return false
  108. }
  109. }
  110. /**
  111. * 根据距离和用时生成步频数据
  112. */
  113. function generateCadence(distanceKm, usedTime) {
  114. const paceSec = usedTime / distanceKm
  115. const paceMin = paceSec / 60
  116. let spmRange
  117. if (paceMin >= 8) spmRange = [85, 110]
  118. else if (paceMin >= 5) spmRange = [150, 170]
  119. else spmRange = [170, 190]
  120. const minutes = Math.ceil(usedTime / 60)
  121. const cadenceList = []
  122. for (let i = 0; i < minutes; i++) {
  123. let spm = Math.floor(Math.random() * (spmRange[1] - spmRange[0] + 1) + spmRange[0])
  124. if (i === minutes - 1) {
  125. const lastDuration = usedTime - (minutes - 1) * 60
  126. spm = Math.round(spm * (lastDuration / 60))
  127. }
  128. cadenceList.push(spm)
  129. }
  130. const totalSteps = cadenceList.reduce((a, b) => a + b, 0)
  131. return {
  132. cadence_list: cadenceList,
  133. total_steps: totalSteps
  134. }
  135. }
  136. module.exports = {
  137. getPathData,
  138. selectCheckpoints,
  139. generateCadence
  140. }