您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

BaiduMercatorProjection.js 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. /**
  2. * @Author: Caven
  3. * @Date: 2021-01-30 22:41:41
  4. */
  5. const EARTH_RADIUS = 6370996.81
  6. const MC_BAND = [12890594.86, 8362377.87, 5591021, 3481989.83, 1678043.12, 0]
  7. const LL_BAND = [75, 60, 45, 30, 15, 0]
  8. const MC2LL = [
  9. [
  10. 1.410526172116255e-8, 8.98305509648872e-6, -1.9939833816331,
  11. 2.009824383106796e2, -1.872403703815547e2, 91.6087516669843,
  12. -23.38765649603339, 2.57121317296198, -0.03801003308653, 1.73379812e7,
  13. ],
  14. [
  15. -7.435856389565537e-9, 8.983055097726239e-6, -0.78625201886289,
  16. 96.32687599759846, -1.85204757529826, -59.36935905485877, 47.40033549296737,
  17. -16.50741931063887, 2.28786674699375, 1.026014486e7,
  18. ],
  19. [
  20. -3.030883460898826e-8, 8.98305509983578e-6, 0.30071316287616,
  21. 59.74293618442277, 7.357984074871, -25.38371002664745, 13.45380521110908,
  22. -3.29883767235584, 0.32710905363475, 6.85681737e6,
  23. ],
  24. [
  25. -1.981981304930552e-8, 8.983055099779535e-6, 0.03278182852591,
  26. 40.31678527705744, 0.65659298677277, -4.44255534477492, 0.85341911805263,
  27. 0.12923347998204, -0.04625736007561, 4.48277706e6,
  28. ],
  29. [
  30. 3.09191371068437e-9, 8.983055096812155e-6, 0.00006995724062,
  31. 23.10934304144901, -0.00023663490511, -0.6321817810242, -0.00663494467273,
  32. 0.03430082397953, -0.00466043876332, 2.5551644e6,
  33. ],
  34. [
  35. 2.890871144776878e-9, 8.983055095805407e-6, -0.00000003068298,
  36. 7.47137025468032, -0.00000353937994, -0.02145144861037, -0.00001234426596,
  37. 0.00010322952773, -0.00000323890364, 8.260885e5,
  38. ],
  39. ]
  40. const LL2MC = [
  41. [
  42. -0.0015702102444, 1.113207020616939e5, 1.704480524535203e15,
  43. -1.033898737604234e16, 2.611266785660388e16, -3.51496691766537e16,
  44. 2.659570071840392e16, -1.072501245418824e16, 1.800819912950474e15, 82.5,
  45. ],
  46. [
  47. 8.277824516172526e-4, 1.113207020463578e5, 6.477955746671608e8,
  48. -4.082003173641316e9, 1.077490566351142e10, -1.517187553151559e10,
  49. 1.205306533862167e10, -5.124939663577472e9, 9.133119359512032e8, 67.5,
  50. ],
  51. [
  52. 0.00337398766765, 1.113207020202162e5, 4.481351045890365e6,
  53. -2.339375119931662e7, 7.968221547186455e7, -1.159649932797253e8,
  54. 9.723671115602145e7, -4.366194633752821e7, 8.477230501135234e6, 52.5,
  55. ],
  56. [
  57. 0.00220636496208, 1.113207020209128e5, 5.175186112841131e4,
  58. 3.796837749470245e6, 9.920137397791013e5, -1.22195221711287e6,
  59. 1.340652697009075e6, -6.209436990984312e5, 1.444169293806241e5, 37.5,
  60. ],
  61. [
  62. -3.441963504368392e-4, 1.113207020576856e5, 2.782353980772752e2,
  63. 2.485758690035394e6, 6.070750963243378e3, 5.482118345352118e4,
  64. 9.540606633304236e3, -2.71055326746645e3, 1.405483844121726e3, 22.5,
  65. ],
  66. [
  67. -3.218135878613132e-4, 1.113207020701615e5, 0.00369383431289,
  68. 8.237256402795718e5, 0.46104986909093, 2.351343141331292e3,
  69. 1.58060784298199, 8.77738589078284, 0.37238884252424, 7.45,
  70. ],
  71. ]
  72. class BaiduMercatorProjection {
  73. constructor() {
  74. this.isWgs84 = false
  75. }
  76. /**
  77. *
  78. * @param point1
  79. * @param point2
  80. * @returns {number}
  81. */
  82. getDistanceByMC(point1, point2) {
  83. if (!point1 || !point2) {
  84. return 0
  85. }
  86. point1 = this.convertMC2LL(point1)
  87. if (!point1) {
  88. return 0
  89. }
  90. let x1 = this.toRadians(point1['lng'])
  91. let y1 = this.toRadians(point1['lat'])
  92. point2 = this.convertMC2LL(point2)
  93. if (!point2) {
  94. return 0
  95. }
  96. let x2 = this.toRadians(point2['lng'])
  97. let y2 = this.toRadians(point2['lat'])
  98. return this.getDistance(x1, x2, y1, y2)
  99. }
  100. /**
  101. * Calculate the distance between two points according to the latitude and longitude coordinates
  102. * @param point1
  103. * @param point2
  104. * @returns {number|*}
  105. */
  106. getDistanceByLL(point1, point2) {
  107. if (!point1 || !point2) {
  108. return 0
  109. }
  110. point1['lng'] = this.getLoop(point1['lng'], -180, 180)
  111. point1['lat'] = this.getRange(point1['lat'], -74, 74)
  112. point2['lng'] = this.getLoop(point2['lng'], -180, 180)
  113. point2['lat'] = this.getRange(point2['lat'], -74, 74)
  114. let x1 = this.toRadians(point1['lng'])
  115. let y1 = this.toRadians(point1['lat'])
  116. let x2 = this.toRadians(point2['lng'])
  117. let y2 = this.toRadians(point2['lat'])
  118. return this.getDistance(x1, x2, y1, y2)
  119. }
  120. /**
  121. * The plane cartesian coordinates are converted to latitude and longitude coordinates
  122. * @param point
  123. * @returns {Point|{lng: number, lat: number}}
  124. */
  125. convertMC2LL(point) {
  126. if (!point) {
  127. return { lng: 0, lat: 0 }
  128. }
  129. let lnglat = {}
  130. if (this.isWgs84) {
  131. lnglat.lng = (point.lng / 20037508.34) * 180
  132. let mmy = (point.lat / 20037508.34) * 180
  133. lnglat.lat =
  134. (180 / Math.PI) *
  135. (2 * Math.atan(Math.exp((mmy * Math.PI) / 180)) - Math.PI / 2)
  136. return {
  137. lng: lnglat['lng'].toFixed(6),
  138. lat: lnglat['lat'].toFixed(6),
  139. }
  140. }
  141. let temp = {
  142. lng: Math.abs(point['lng']),
  143. lat: Math.abs(point['lat']),
  144. }
  145. let factor = undefined
  146. for (let i = 0; i < MC_BAND.length; i++) {
  147. if (temp['lat'] >= MC_BAND[i]) {
  148. factor = MC2LL[i]
  149. break
  150. }
  151. }
  152. lnglat = this.convertor(point, factor)
  153. return {
  154. lng: lnglat['lng'].toFixed(6),
  155. lat: lnglat['lat'].toFixed(6),
  156. }
  157. }
  158. /**
  159. * The latitude and longitude coordinates are converted to plane cartesian coordinates
  160. * @param point
  161. * @returns {{lng: number, lat: number}|*}
  162. */
  163. convertLL2MC(point) {
  164. if (!point) {
  165. return { lng: 0, lat: 0 }
  166. }
  167. if (
  168. point['lng'] > 180 ||
  169. point['lng'] < -180 ||
  170. point['lat'] > 90 ||
  171. point['lat'] < -90
  172. ) {
  173. return point
  174. }
  175. if (this.isWgs84) {
  176. let mercator = {}
  177. let earthRad = 6378137.0
  178. mercator.lng = ((point.lng * Math.PI) / 180) * earthRad
  179. let a = (point.lat * Math.PI) / 180
  180. mercator.lat =
  181. (earthRad / 2) * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)))
  182. return {
  183. lng: parseFloat(mercator['lng'].toFixed(2)),
  184. lat: parseFloat(mercator['lat'].toFixed(2)),
  185. }
  186. }
  187. point['lng'] = this.getLoop(point['lng'], -180, 180)
  188. point['lat'] = this.getRange(point['lat'], -74, 74)
  189. let temp = { lng: point['lng'], lat: point['lat'] }
  190. let factor = undefined
  191. for (let i = 0; i < LL_BAND.length; i++) {
  192. if (temp['lat'] >= LL_BAND[i]) {
  193. factor = LL2MC[i]
  194. break
  195. }
  196. }
  197. if (!factor) {
  198. for (let i = 0; i < LL_BAND.length; i++) {
  199. if (temp['lat'] <= -LL_BAND[i]) {
  200. factor = LL2MC[i]
  201. break
  202. }
  203. }
  204. }
  205. let mc = this.convertor(point, factor)
  206. return {
  207. lng: parseFloat(mc['lng'].toFixed(2)),
  208. lat: parseFloat(mc['lat'].toFixed(2)),
  209. }
  210. }
  211. /**
  212. *
  213. * @param fromPoint
  214. * @param factor
  215. * @returns {{lng: *, lat: *}}
  216. */
  217. convertor(fromPoint, factor) {
  218. if (!fromPoint || !factor) {
  219. return { lng: 0, lat: 0 }
  220. }
  221. let x = factor[0] + factor[1] * Math.abs(fromPoint['lng'])
  222. let temp = Math.abs(fromPoint['lat']) / factor[9]
  223. let y =
  224. factor[2] +
  225. factor[3] * temp +
  226. factor[4] * temp * temp +
  227. factor[5] * temp * temp * temp +
  228. factor[6] * temp * temp * temp * temp +
  229. factor[7] * temp * temp * temp * temp * temp +
  230. factor[8] * temp * temp * temp * temp * temp * temp
  231. x *= fromPoint['lng'] < 0 ? -1 : 1
  232. y *= fromPoint['lat'] < 0 ? -1 : 1
  233. return {
  234. lng: x,
  235. lat: y,
  236. }
  237. }
  238. /**
  239. *
  240. * @param x1
  241. * @param x2
  242. * @param y1
  243. * @param y2
  244. * @returns {number}
  245. */
  246. getDistance(x1, x2, y1, y2) {
  247. return (
  248. EARTH_RADIUS *
  249. Math.acos(
  250. Math.sin(y1) * Math.sin(y2) +
  251. Math.cos(y1) * Math.cos(y2) * Math.cos(x2 - x1)
  252. )
  253. )
  254. }
  255. /**
  256. *
  257. * @param deg
  258. * @returns {number}
  259. */
  260. toRadians(deg) {
  261. return (Math.PI * deg) / 180
  262. }
  263. /**
  264. *
  265. * @param rad
  266. * @returns {number}
  267. */
  268. toDegrees(rad) {
  269. return (180 * rad) / Math.PI
  270. }
  271. /**
  272. *
  273. * @param v
  274. * @param a
  275. * @param b
  276. * @returns {number}
  277. */
  278. getRange(v, a, b) {
  279. if (a != null) {
  280. v = Math.max(v, a)
  281. }
  282. if (b != null) {
  283. v = Math.min(v, b)
  284. }
  285. return v
  286. }
  287. /**
  288. *
  289. * @param v
  290. * @param a
  291. * @param b
  292. * @returns {*}
  293. */
  294. getLoop(v, a, b) {
  295. while (v > b) {
  296. v -= b - a
  297. }
  298. while (v < a) {
  299. v += b - a
  300. }
  301. return v
  302. }
  303. /**
  304. *
  305. * @param point
  306. * @returns {{lng: number, lat: number}|*}
  307. */
  308. lngLatToMercator(point) {
  309. return this.convertLL2MC(point)
  310. }
  311. /**
  312. *
  313. * @param point
  314. * @returns {{x: (number|*), y: (number|*)}}
  315. */
  316. lngLatToPoint(point) {
  317. let mercator = this.convertLL2MC(point)
  318. return {
  319. x: mercator['lng'],
  320. y: mercator['lat'],
  321. }
  322. }
  323. /**
  324. * WebMercator transforms to latitude and longitude
  325. * @param point
  326. * @returns {Point|{lng: number, lat: number}}
  327. */
  328. mercatorToLngLat(point) {
  329. return this.convertMC2LL(point)
  330. }
  331. /**
  332. *
  333. * @param point
  334. * @returns {Point|{lng: number, lat: number}}
  335. */
  336. pointToLngLat(point) {
  337. let mercator = { lng: point.x, lat: point.y }
  338. return this.convertMC2LL(mercator)
  339. }
  340. /**
  341. * Latitude and longitude coordinates transforms to pixel coordinates
  342. * @param point
  343. * @param zoom
  344. * @param mapCenter
  345. * @param mapSize
  346. * @returns {{x: number, y: number}}
  347. */
  348. pointToPixel(point, zoom, mapCenter, mapSize) {
  349. if (!point) {
  350. return { x: 0, y: 0 }
  351. }
  352. point = this.lngLatToMercator(point)
  353. let zoomUnits = this.getZoomUnits(zoom)
  354. let x = Math.round(
  355. (point['lng'] - mapCenter['lng']) / zoomUnits + mapSize.width / 2
  356. )
  357. let y = Math.round(
  358. (mapCenter['lat'] - point['lat']) / zoomUnits + mapSize.height / 2
  359. )
  360. return { x, y }
  361. }
  362. /**
  363. * Pixel coordinates transforms to latitude and longitude coordinates
  364. * @param pixel
  365. * @param zoom
  366. * @param mapCenter
  367. * @param mapSize
  368. * @returns {Point|{lng: number, lat: number}}
  369. */
  370. pixelToPoint(pixel, zoom, mapCenter, mapSize) {
  371. if (!pixel) {
  372. return { lng: 0, lat: 0 }
  373. }
  374. let zoomUnits = this.getZoomUnits(zoom)
  375. let lng = mapCenter['lng'] + zoomUnits * (pixel.x - mapSize.width / 2)
  376. let lat = mapCenter['lat'] - zoomUnits * (pixel.y - mapSize.height / 2)
  377. let point = { lng, lat }
  378. return this.mercatorToLngLat(point)
  379. }
  380. /**
  381. *
  382. * @param zoom
  383. * @returns {number}
  384. */
  385. getZoomUnits(zoom) {
  386. return Math.pow(2, 18 - zoom)
  387. }
  388. }
  389. export default BaiduMercatorProjection