mapUtils.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /**
  2. * 地图工具函数模块
  3. * 提供腾讯地图相关的通用工具函数
  4. *
  5. * 主要功能:
  6. * - 坐标格式转换
  7. * - 坐标有效性验证
  8. * - 地图实例管理辅助函数
  9. */
  10. /**
  11. * 验证坐标是否有效
  12. * @param {number} lng 经度
  13. * @param {number} lat 纬度
  14. * @returns {boolean} 坐标是否有效
  15. */
  16. // export function isValidCoordinate(lng, lat) { // 高德
  17. // return (
  18. // lng !== null &&
  19. // lng !== undefined &&
  20. // lat !== null &&
  21. // lat !== undefined &&
  22. // lng >= -180 &&
  23. // lng <= 180 &&
  24. // lat >= -90 &&
  25. // lat <= 90 &&
  26. // !isNaN(lng) &&
  27. // !isNaN(lat)
  28. // )
  29. // }
  30. export function isValidCoordinate(lat, lng) {
  31. return (
  32. lat !== null &&
  33. lat !== undefined &&
  34. lng !== null &&
  35. lng !== undefined &&
  36. lat >= -90 &&
  37. lat <= 90 &&
  38. lng >= -180 &&
  39. lng <= 180 &&
  40. !isNaN(lat) &&
  41. !isNaN(lng)
  42. )
  43. }
  44. /**
  45. * 将点对象数组转换为 TMap.LatLng 对象数组
  46. * @param {Array<{lng: number, lat: number}>} points 点对象数组
  47. * @returns {Array<TMap.LatLng>} TMap.LatLng 对象数组
  48. */
  49. export function convertPointsToLatLng(points) {
  50. if (!points || !Array.isArray(points)) {
  51. return []
  52. }
  53. return points
  54. .filter(p => p && isValidCoordinate(p.lng, p.lat))
  55. .map(p => new TMap.LatLng(p.lat, p.lng))
  56. }
  57. /**
  58. * 将经纬度数组转换为 TMap.LatLng 对象数组
  59. * @param {Array<[number, number]>} lngLatArray 经纬度数组 [[lng, lat], ...]
  60. * @returns {Array<TMap.LatLng>} TMap.LatLng 对象数组
  61. */
  62. export function convertLngLatArrayToLatLng(lngLatArray) {
  63. if (!lngLatArray || !Array.isArray(lngLatArray)) {
  64. return []
  65. }
  66. return lngLatArray
  67. .filter(([lng, lat]) => isValidCoordinate(lng, lat))
  68. .map(([lng, lat]) => new TMap.LatLng(lat, lng))
  69. }
  70. /**
  71. * 安全地设置地图中心点
  72. * @param {Object} map 地图实例
  73. * @param {number} lng 经度
  74. * @param {number} lat 纬度
  75. * @param {number} zoom 缩放级别(可选)
  76. * @returns {boolean} 是否设置成功
  77. */
  78. export function safeSetMapCenter(map, lat, lng, zoom) {
  79. if (!map || !isValidCoordinate(lat, lng)) {
  80. console.warn('[mapUtils] 无效的地图实例或坐标')
  81. return false
  82. }
  83. try {
  84. const center = new TMap.LatLng(lat, lng)
  85. map.setCenter(center)
  86. if (typeof zoom === 'number' && zoom >= 3 && zoom <= 20) {
  87. map.setZoom(zoom)
  88. }
  89. return true
  90. } catch (err) {
  91. console.error('[mapUtils] 设置地图中心失败', err)
  92. return false
  93. }
  94. }
  95. /**
  96. * 安全地获取地图中心点
  97. * @param {Object} map 地图实例
  98. * @returns {Object|null} 包含 lng, lat 的对象,失败返回 null
  99. */
  100. export function safeGetMapCenter(map) {
  101. if (!map || typeof map.getCenter !== 'function') {
  102. return null
  103. }
  104. try {
  105. const center = map.getCenter()
  106. return {
  107. lat: center.lat,
  108. lng: center.lng
  109. }
  110. } catch (err) {
  111. console.error('[mapUtils] 获取地图中心失败', err)
  112. return null
  113. }
  114. }
  115. /**
  116. * 格式化坐标显示
  117. * @param {Object} point 点对象 {lng, lat}
  118. * @param {number} precision 精度(小数位数),默认 5
  119. * @returns {string} 格式化后的坐标字符串
  120. */
  121. export function formatCoordinate(point, precision = 5) {
  122. if (!point || !isValidCoordinate(point.lng, point.lat)) {
  123. return '--'
  124. }
  125. return `${point.lng.toFixed(precision)}, ${point.lat.toFixed(precision)}`
  126. }
  127. /**
  128. * 计算两点之间的距离(米)
  129. * 使用 Haversine 公式
  130. * @param {Object} point1 点1 {lng, lat}
  131. * @param {Object} point2 点2 {lng, lat}
  132. * @returns {number} 距离(米)
  133. */
  134. export function calculateDistance(point1, point2) {
  135. if (!isValidCoordinate(point1.lng, point1.lat) ||
  136. !isValidCoordinate(point2.lng, point2.lat)) {
  137. return 0
  138. }
  139. const R = 6371000 // 地球半径(米)
  140. const lat1 = point1.lat * Math.PI / 180
  141. const lat2 = point2.lat * Math.PI / 180
  142. const deltaLat = (point2.lat - point1.lat) * Math.PI / 180
  143. const deltaLng = (point2.lng - point1.lng) * Math.PI / 180
  144. const a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
  145. Math.cos(lat1) * Math.cos(lat2) *
  146. Math.sin(deltaLng / 2) * Math.sin(deltaLng / 2)
  147. const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
  148. return R * c
  149. }
  150. /**
  151. * 判断点是否在多边形内
  152. * 使用射线法
  153. * @param {Object} point 点 {lng, lat}
  154. * @param {Array<Object>} polygon 多边形顶点数组 [{lng, lat}, ...]
  155. * @returns {boolean} 点是否在多边形内
  156. */
  157. export function isPointInPolygon(point, polygon) {
  158. if (!isValidCoordinate(point.lng, point.lat) ||
  159. !polygon || polygon.length < 3) {
  160. return false
  161. }
  162. let inside = false
  163. const x = point.lng
  164. const y = point.lat
  165. for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
  166. const xi = polygon[i].lng
  167. const yi = polygon[i].lat
  168. const xj = polygon[j].lng
  169. const yj = polygon[j].lat
  170. const intersect = ((yi > y) !== (yj > y)) &&
  171. (x < (xj - xi) * (y - yi) / (yj - yi) + xi)
  172. if (intersect) {
  173. inside = !inside
  174. }
  175. }
  176. return inside
  177. }