You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BaiduMercatorProjection.js 11KB

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