Lushu.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. /**
  2. * @fileoverview 百度地图的轨迹跟随类,对外开放。
  3. * 用户可以在地图上自定义轨迹运动
  4. * 可以自定义路过某个点的图片,文字介绍等。
  5. * 主入口类是<a href="examples/index.html">LuShu</a>,
  6. * 基于Baidu Map API GL 1.0
  7. *
  8. * @author Baidu Map Api Group
  9. * @version 1.0
  10. */
  11. /**
  12. * @namespace BMapGL的所有library类均放在BMapGLLib命名空间下
  13. */
  14. var BMapGLLib = (window.BMapGLLib = BMapGLLib || {});
  15. (function () {
  16. var T;
  17. var baidu = (T = baidu || { version: "gl 1.0" });
  18. baidu.guid = "$BAIDU$";
  19. (function () {
  20. window[baidu.guid] = window[baidu.guid] || {};
  21. baidu.dom = baidu.dom || {};
  22. baidu.dom.g = function (id) {
  23. if ("string" == typeof id || id instanceof String) {
  24. return document.getElementById(id);
  25. } else if (id && id.nodeName && (id.nodeType == 1 || id.nodeType == 9)) {
  26. return id;
  27. }
  28. return null;
  29. };
  30. baidu.g = baidu.G = baidu.dom.g;
  31. baidu.lang = baidu.lang || {};
  32. baidu.lang.isString = function (source) {
  33. return "[object String]" == Object.prototype.toString.call(source);
  34. };
  35. baidu.isString = baidu.lang.isString;
  36. baidu.dom._g = function (id) {
  37. if (baidu.lang.isString(id)) {
  38. return document.getElementById(id);
  39. }
  40. return id;
  41. };
  42. baidu._g = baidu.dom._g;
  43. baidu.dom.getDocument = function (element) {
  44. element = baidu.dom.g(element);
  45. return element.nodeType == 9
  46. ? element
  47. : element.ownerDocument || element.document;
  48. };
  49. baidu.browser = baidu.browser || {};
  50. baidu.browser.ie = baidu.ie = /msie (\d+\.\d+)/i.test(navigator.userAgent)
  51. ? document.documentMode || +RegExp["\x241"]
  52. : undefined;
  53. baidu.dom.getComputedStyle = function (element, key) {
  54. element = baidu.dom._g(element);
  55. var doc = baidu.dom.getDocument(element),
  56. styles;
  57. if (doc.defaultView && doc.defaultView.getComputedStyle) {
  58. styles = doc.defaultView.getComputedStyle(element, null);
  59. if (styles) {
  60. return styles[key] || styles.getPropertyValue(key);
  61. }
  62. }
  63. return "";
  64. };
  65. baidu.dom._styleFixer = baidu.dom._styleFixer || {};
  66. baidu.dom._styleFilter = baidu.dom._styleFilter || [];
  67. baidu.dom._styleFilter.filter = function (key, value, method) {
  68. var filters = baidu.dom._styleFilter;
  69. var filter;
  70. for (var i = 0; (filter = filters[i]); i++) {
  71. if ((filter = filter[method])) {
  72. value = filter(key, value);
  73. }
  74. }
  75. return value;
  76. };
  77. baidu.string = baidu.string || {};
  78. baidu.string.toCamelCase = function (source) {
  79. if (source.indexOf("-") < 0 && source.indexOf("_") < 0) {
  80. return source;
  81. }
  82. return source.replace(/[-_][^-_]/g, function (match) {
  83. return match.charAt(1).toUpperCase();
  84. });
  85. };
  86. baidu.dom.getStyle = function (element, key) {
  87. var dom = baidu.dom;
  88. element = dom.g(element);
  89. key = baidu.string.toCamelCase(key);
  90. var value =
  91. element.style[key] ||
  92. (element.currentStyle ? element.currentStyle[key] : "") ||
  93. dom.getComputedStyle(element, key);
  94. if (!value) {
  95. var fixer = dom._styleFixer[key];
  96. if (fixer) {
  97. value = fixer.get
  98. ? fixer.get(element)
  99. : baidu.dom.getStyle(element, fixer);
  100. }
  101. }
  102. if ((fixer = dom._styleFilter)) {
  103. value = fixer.filter(key, value, "get");
  104. }
  105. return value;
  106. };
  107. baidu.getStyle = baidu.dom.getStyle;
  108. baidu.dom._NAME_ATTRS = (function () {
  109. var result = {
  110. cellpadding: "cellPadding",
  111. cellspacing: "cellSpacing",
  112. colspan: "colSpan",
  113. rowspan: "rowSpan",
  114. valign: "vAlign",
  115. usemap: "useMap",
  116. frameborder: "frameBorder",
  117. };
  118. if (baidu.browser.ie < 8) {
  119. result["for"] = "htmlFor";
  120. result["class"] = "className";
  121. } else {
  122. result["htmlFor"] = "for";
  123. result["className"] = "class";
  124. }
  125. return result;
  126. })();
  127. baidu.dom.setAttr = function (element, key, value) {
  128. element = baidu.dom.g(element);
  129. if ("style" == key) {
  130. element.style.cssText = value;
  131. } else {
  132. key = baidu.dom._NAME_ATTRS[key] || key;
  133. element.setAttribute(key, value);
  134. }
  135. return element;
  136. };
  137. baidu.setAttr = baidu.dom.setAttr;
  138. baidu.dom.setAttrs = function (element, attributes) {
  139. element = baidu.dom.g(element);
  140. for (var key in attributes) {
  141. baidu.dom.setAttr(element, key, attributes[key]);
  142. }
  143. return element;
  144. };
  145. baidu.setAttrs = baidu.dom.setAttrs;
  146. baidu.dom.create = function (tagName, opt_attributes) {
  147. var el = document.createElement(tagName),
  148. attributes = opt_attributes || {};
  149. return baidu.dom.setAttrs(el, attributes);
  150. };
  151. baidu.object = baidu.object || {};
  152. baidu.extend = baidu.object.extend = function (target, source) {
  153. for (var p in source) {
  154. if (source.hasOwnProperty(p)) {
  155. target[p] = source[p];
  156. }
  157. }
  158. return target;
  159. };
  160. })();
  161. WORLD_SIZE_MC_HALF = 20037726.372307256;
  162. WORLD_SIZE_MC = WORLD_SIZE_MC_HALF * 2;
  163. /**
  164. * @exports LuShu as BMapGLLib.LuShu
  165. */
  166. var LuShu =
  167. /**
  168. * LuShu类的构造函数
  169. * @class LuShu <b>入口</b>。
  170. * 实例化该类后,可调用,start,end,pause等方法控制覆盖物的运动。
  171. * @constructor
  172. * @param {Map} map Baidu map的实例对象.
  173. * @param {Array} path 构成路线的point的数组.
  174. * @param {Json Object} opts 可选的输入参数,非必填项。可输入选项包括:<br />
  175. * {<br />"<b>landmarkPois</b>" : {Array} 要在覆盖物移动过程中,显示的特殊点。格式如下:landmarkPois:[<br />
  176. * {lng:116.314782,lat:39.913508,html:'加油站',pauseTime:2},<br />
  177. * {lng:116.315391,lat:39.964429,html:'高速公路收费站,pauseTime:3}]<br />
  178. * <br />"<b>icon</b>" : {Icon} 覆盖物的icon,
  179. * <br />"<b>speed</b>" : {Number} 覆盖物移动速度,单位米/秒 <br />
  180. * <br />"<b>defaultContent</b>" : {String} 覆盖物中的内容 <br />
  181. * }<br />.
  182. * @example <b>参考示例:</b><br />
  183. * var lushu = new BMapGLLib.LuShu(map,arrPois,{defaultContent:"从北京到天津",landmarkPois:[]});
  184. */
  185. (BMapGLLib.LuShu = function (map, path, opts) {
  186. if (!path || path.length < 1) {
  187. return;
  188. }
  189. this._map = map;
  190. //存储一条路线
  191. if (opts["geodesic"]) {
  192. this.path = getGeodesicPath(path);
  193. } else {
  194. this.path = path;
  195. }
  196. //移动到当前点的索引
  197. this.i = 0;
  198. //控制暂停后开始移动的队列的数组
  199. this._setTimeoutQuene = [];
  200. //进行坐标转换的类
  201. // this._projection = this._map.getMapType().getProjection();
  202. this._opts = {
  203. icon: null,
  204. //默认速度 米/秒
  205. speed: 400,
  206. defaultContent: "",
  207. };
  208. if (!opts["landmarkPois"]) {
  209. opts["landmarkPois"] = [];
  210. }
  211. this._setOptions(opts);
  212. this._rotation = 0; //小车转动的角度
  213. //如果不是默认实例,则使用默认的icon
  214. if (!(this._opts.icon instanceof BMapGL.Icon)) {
  215. this._opts.icon = defaultIcon;
  216. }
  217. });
  218. /**
  219. * 根据用户输入的opts,修改默认参数_opts
  220. * @param {Json Object} opts 用户输入的修改参数.
  221. * @return 无返回值.
  222. */
  223. LuShu.prototype._setOptions = function (opts) {
  224. if (!opts) {
  225. return;
  226. }
  227. for (var p in opts) {
  228. if (opts.hasOwnProperty(p)) {
  229. this._opts[p] = opts[p];
  230. }
  231. }
  232. };
  233. /**
  234. * @description 开始运动
  235. * @param none
  236. * @return 无返回值.
  237. *
  238. * @example <b>参考示例:</b><br />
  239. * lushu.start();
  240. */
  241. LuShu.prototype.start = function () {
  242. var me = this,
  243. len = me.path.length;
  244. //不是第一次点击开始,并且小车还没到达终点
  245. if (me.i && me.i < len - 1) {
  246. //没按pause再按start不做处理
  247. if (!me._fromPause) {
  248. return;
  249. } else if (!me._fromStop) {
  250. //按了pause按钮,并且再按start,直接移动到下一点
  251. //并且此过程中,没有按stop按钮
  252. //防止先stop,再pause,然后连续不停的start的异常
  253. me._moveNext(++me.i);
  254. }
  255. } else {
  256. //第一次点击开始,或者点了stop之后点开始
  257. me._addMarker();
  258. //等待marker动画完毕再加载infowindow
  259. me._timeoutFlag = setTimeout(function () {
  260. me._addInfoWin();
  261. if (me._opts.defaultContent == "") {
  262. me.hideInfoWindow();
  263. }
  264. me._moveNext(me.i);
  265. }, 400);
  266. }
  267. //重置状态
  268. this._fromPause = false;
  269. this._fromStop = false;
  270. };
  271. /**
  272. * 结束运动
  273. * @return 无返回值.
  274. *
  275. * @example <b>参考示例:</b><br />
  276. * lushu.stop();
  277. */
  278. LuShu.prototype.stop = function () {
  279. this.i = 0;
  280. this._fromStop = true;
  281. clearInterval(this._intervalFlag);
  282. this._clearTimeout();
  283. //重置landmark里边的poi为未显示状态
  284. for (var i = 0, t = this._opts.landmarkPois, len = t.length; i < len; i++) {
  285. t[i].bShow = false;
  286. }
  287. };
  288. /**
  289. * 暂停运动
  290. * @return 无返回值.
  291. */
  292. LuShu.prototype.pause = function () {
  293. clearInterval(this._intervalFlag);
  294. //标识是否是按过pause按钮
  295. this._fromPause = true;
  296. this._clearTimeout();
  297. };
  298. LuShu.prototype.clear = function () {
  299. this._clearMark();
  300. };
  301. /**
  302. * 隐藏上方overlay
  303. * @return 无返回值.
  304. *
  305. * @example <b>参考示例:</b><br />
  306. * lushu.hideInfoWindow();
  307. */
  308. LuShu.prototype.hideInfoWindow = function () {
  309. this._overlay._div.style.visibility = "hidden";
  310. };
  311. /**
  312. * 显示上方overlay
  313. * @return 无返回值.
  314. *
  315. * @example <b>参考示例:</b><br />
  316. * lushu.showInfoWindow();
  317. */
  318. LuShu.prototype.showInfoWindow = function () {
  319. this._overlay._div.style.visibility = "visible";
  320. };
  321. //Lushu私有方法
  322. baidu.object.extend(LuShu.prototype, {
  323. /**
  324. * 添加marker到地图上
  325. * @param {Function} 回调函数.
  326. * @return 无返回值.
  327. */
  328. _addMarker: function (callback) {
  329. if (this._marker) {
  330. this.stop();
  331. // 变更
  332. this._map.removeOverlay(this._marker);
  333. this._map.removeOverlay(this._markerL);
  334. this._map.removeOverlay(this._markerR);
  335. clearTimeout(this._timeoutFlag);
  336. }
  337. //移除之前的overlay
  338. this._overlay && this._map.removeOverlay(this._overlay);
  339. var marker = new BMapGL.Marker(this.path[0]);
  340. this._opts.icon && marker.setIcon(this._opts.icon);
  341. this._map.addOverlay(marker);
  342. marker.setAnimation(BMAP_ANIMATION_DROP);
  343. this._marker = marker;
  344. // 变更
  345. var markerL = new BMapGL.Marker(this.path[0], { left: true });
  346. this._opts.icon && markerL.setIcon(this._opts.icon);
  347. this._map.addOverlay(markerL);
  348. markerL.setAnimation(BMAP_ANIMATION_DROP);
  349. this._markerL = markerL;
  350. var markerR = new BMapGL.Marker(this.path[0], { right: true });
  351. this._opts.icon && markerR.setIcon(this._opts.icon);
  352. this._map.addOverlay(markerR);
  353. markerR.setAnimation(BMAP_ANIMATION_DROP);
  354. this._markerR = markerR;
  355. },
  356. /**
  357. * 销毁--(用户自定义)
  358. * @param f
  359. * @private
  360. */
  361. _clearMark: function (f) {
  362. this.stop();
  363. this._map.removeOverlay(this._marker);
  364. this._map.removeOverlay(this._markerL);
  365. this._map.removeOverlay(this._markerR);
  366. clearTimeout(this._timeoutFlag);
  367. this._overlay && this._map.removeOverlay(this._overlay);
  368. },
  369. /**
  370. * 添加上方overlay
  371. * @return 无返回值.
  372. */
  373. _addInfoWin: function () {
  374. var me = this;
  375. var overlay = new CustomOverlay(
  376. me._marker.getPosition(),
  377. me._opts.defaultContent
  378. );
  379. //将当前类的引用传给overlay。
  380. overlay.setRelatedClass(this);
  381. this._overlay = overlay;
  382. this._map.addOverlay(overlay);
  383. },
  384. /**
  385. * 获取墨卡托坐标
  386. * @param {Point} poi 经纬度坐标.
  387. * @return 无返回值.
  388. */
  389. _getMercator: function (poi) {
  390. return this._map.getMapType().getProjection().lngLatToPoint(poi);
  391. },
  392. /**
  393. * 计算两点间的距离
  394. * @param {Point} poi 经纬度坐标A点.
  395. * @param {Point} poi 经纬度坐标B点.
  396. * @return 无返回值.
  397. */
  398. _getDistance: function (pxA, pxB) {
  399. return Math.sqrt(Math.pow(pxA.x - pxB.x, 2) + Math.pow(pxA.y - pxB.y, 2));
  400. },
  401. //目标点的 当前的步长,position,总的步长,动画效果,回调
  402. /**
  403. * 移动小车
  404. * @param {Number} poi 当前的步长.
  405. * @param {Point} initPos 经纬度坐标初始点.
  406. * @param {Point} targetPos 经纬度坐标目标点.
  407. * @param {Function} effect 缓动效果.
  408. * @return 无返回值.
  409. */
  410. _move: function (initPos, targetPos, effect) {
  411. var me = this,
  412. //当前的帧数
  413. currentCount = 0,
  414. //步长,米/秒
  415. timer = 10,
  416. step = this._opts.speed / (1000 / timer),
  417. //初始坐标
  418. init_pos = BMapGL.Projection.convertLL2MC(initPos),
  419. //获取结束点的(x,y)坐标
  420. target_pos = BMapGL.Projection.convertLL2MC(targetPos);
  421. init_pos = new BMapGL.Pixel(init_pos.lng, init_pos.lat);
  422. target_pos = new BMapGL.Pixel(target_pos.lng, target_pos.lat);
  423. // 变更
  424. var mcDis = me._getDistance(init_pos, target_pos);
  425. var direction = null;
  426. if (mcDis > 30037726) {
  427. if (target_pos.x < init_pos.x) {
  428. target_pos.x += WORLD_SIZE_MC;
  429. direction = "right";
  430. } else {
  431. target_pos.x -= WORLD_SIZE_MC;
  432. direction = "left";
  433. }
  434. }
  435. //总的步长
  436. var count = Math.round(me._getDistance(init_pos, target_pos) / step);
  437. //如果小于1直接移动到下一点
  438. if (count < 1) {
  439. me._moveNext(++me.i);
  440. return;
  441. }
  442. //两点之间匀速移动
  443. me._intervalFlag = setInterval(function () {
  444. //两点之间当前帧数大于总帧数的时候,则说明已经完成移动
  445. if (currentCount >= count) {
  446. clearInterval(me._intervalFlag);
  447. //移动的点已经超过总的长度
  448. if (me.i > me.path.length) {
  449. return;
  450. }
  451. //运行下一个点
  452. me._moveNext(++me.i);
  453. } else {
  454. currentCount++;
  455. var x = effect(init_pos.x, target_pos.x, currentCount, count),
  456. y = effect(init_pos.y, target_pos.y, currentCount, count),
  457. pos = BMapGL.Projection.convertMC2LL(new BMapGL.Point(x, y));
  458. if (pos.lng > 180) {
  459. pos.lng = pos.lng - 360;
  460. }
  461. if (pos.lng < -180) {
  462. pos.lng = pos.lng + 360;
  463. }
  464. //设置marker
  465. if (currentCount == 1) {
  466. var proPos = null;
  467. if (me.i - 1 >= 0) {
  468. proPos = me.path[me.i - 1];
  469. }
  470. if (me._opts.enableRotation == true) {
  471. me.setRotation(proPos, initPos, targetPos, direction);
  472. }
  473. if (me._opts.autoView) {
  474. if (!me._map.getBounds().containsPoint(pos)) {
  475. me._map.setCenter(pos);
  476. }
  477. }
  478. }
  479. // 变更
  480. //正在移动
  481. me._marker.setPosition(pos);
  482. me._markerL.setPosition(pos);
  483. me._markerR.setPosition(pos);
  484. //设置自定义overlay的位置
  485. me._setInfoWin(pos);
  486. }
  487. }, timer);
  488. },
  489. /**
  490. * 在每个点的真实步骤中设置小车转动的角度
  491. */
  492. setRotation: function (prePos, curPos, targetPos, direction) {
  493. var me = this;
  494. var deg = 0;
  495. //start!
  496. curPos = me._map.pointToPixel(curPos);
  497. targetPos = me._map.pointToPixel(targetPos);
  498. if (targetPos.x != curPos.x) {
  499. var tan = (targetPos.y - curPos.y) / (targetPos.x - curPos.x),
  500. atan = Math.atan(tan);
  501. deg = (atan * 360) / (2 * Math.PI);
  502. //degree correction;
  503. if ((!direction && targetPos.x < curPos.x) || direction === "left") {
  504. deg = -deg + 90 + 90;
  505. } else {
  506. deg = -deg;
  507. }
  508. // 变更
  509. me._marker.setRotation(-deg);
  510. me._markerL.setRotation(-deg);
  511. me._markerR.setRotation(-deg);
  512. } else {
  513. var disy = targetPos.y - curPos.y;
  514. var bias = 0;
  515. if (disy > 0) {
  516. bias = -1;
  517. } else {
  518. bias = 1;
  519. }
  520. // 变更
  521. me._marker.setRotation(-bias * 90);
  522. me._markerL.setRotation(-bias * 90);
  523. me._markerR.setRotation(-bias * 90);
  524. }
  525. return;
  526. },
  527. linePixellength: function (from, to) {
  528. return Math.sqrt(
  529. Math.abs(from.x - to.x) * Math.abs(from.x - to.x) +
  530. Math.abs(from.y - to.y) * Math.abs(from.y - to.y)
  531. );
  532. },
  533. pointToPoint: function (from, to) {
  534. return (
  535. Math.abs(from.x - to.x) * Math.abs(from.x - to.x) +
  536. Math.abs(from.y - to.y) * Math.abs(from.y - to.y)
  537. );
  538. },
  539. /**
  540. * 移动到下一个点
  541. * @param {Number} index 当前点的索引.
  542. * @return 无返回值.
  543. */
  544. _moveNext: function (index) {
  545. var me = this;
  546. // debugger;
  547. if (index < this.path.length - 1) {
  548. me._move(me.path[index], me.path[index + 1], me._tween.linear);
  549. } else {
  550. me._opts.onComplete && me._opts.onComplete()
  551. }
  552. },
  553. /**
  554. * 设置小车上方infowindow的内容,位置等
  555. * @param {Point} pos 经纬度坐标点.
  556. * @return 无返回值.
  557. */
  558. _setInfoWin: function (pos) {
  559. //设置上方overlay的position
  560. var me = this;
  561. if (!me._overlay) {
  562. return;
  563. }
  564. me._overlay.setPosition(pos, me._marker.getIcon().size);
  565. var index = me._troughPointIndex(pos);
  566. if (index != -1) {
  567. clearInterval(me._intervalFlag);
  568. me._overlay.setHtml(me._opts.landmarkPois[index].html);
  569. me._overlay.setPosition(pos, me._marker.getIcon().size);
  570. me._pauseForView(index);
  571. } else {
  572. me._overlay.setHtml(me._opts.defaultContent);
  573. }
  574. },
  575. /**
  576. * 在某个点暂停的时间
  577. * @param {Number} index 点的索引.
  578. * @return 无返回值.
  579. */
  580. _pauseForView: function (index) {
  581. var me = this;
  582. var t = setTimeout(function () {
  583. //运行下一个点
  584. me._moveNext(++me.i);
  585. }, me._opts.landmarkPois[index].pauseTime * 1000);
  586. me._setTimeoutQuene.push(t);
  587. },
  588. //清除暂停后再开始运行的timeout
  589. _clearTimeout: function () {
  590. for (var i in this._setTimeoutQuene) {
  591. clearTimeout(this._setTimeoutQuene[i]);
  592. }
  593. this._setTimeoutQuene.length = 0;
  594. },
  595. //缓动效果
  596. _tween: {
  597. //初始坐标,目标坐标,当前的步长,总的步长
  598. linear: function (initPos, targetPos, currentCount, count) {
  599. var b = initPos;
  600. var c = targetPos - initPos;
  601. var t = currentCount;
  602. var d = count;
  603. return (c * t) / d + b;
  604. },
  605. },
  606. /**
  607. * 否经过某个点的index
  608. * @param {Point} markerPoi 当前小车的坐标点.
  609. * @return 无返回值.
  610. */
  611. _troughPointIndex: function (markerPoi) {
  612. var t = this._opts.landmarkPois;
  613. var distance;
  614. for (var i = 0, len = t.length; i < len; i++) {
  615. //landmarkPois中的点没有出现过的话
  616. if (!t[i].bShow) {
  617. distance = this._map.getDistance(
  618. new BMapGL.Point(t[i].lng, t[i].lat),
  619. markerPoi
  620. );
  621. //两点距离小于10米,认为是同一个点
  622. if (distance < 10) {
  623. t[i].bShow = true;
  624. return i;
  625. }
  626. }
  627. }
  628. return -1;
  629. },
  630. });
  631. /**
  632. * 获取大圆点
  633. * @return {Array} 大圆点
  634. */
  635. function getGeodesicPath(points) {
  636. var gPath = [];
  637. for (var i = 0; i < points.length - 1; i++) {
  638. var great = calcGreatCirclePath(points[i], points[i + 1]);
  639. gPath = gPath.concat(great);
  640. }
  641. gPath = gPath.concat(points[points.length - 1]);
  642. return gPath;
  643. }
  644. /**
  645. * 计算大圆上的点
  646. * @param {Object} latLng1 点1
  647. * @param {Object} latLng2 点2
  648. * @return {Array} 扩充后的点
  649. */
  650. function calcGreatCirclePath(latLng1, latLng2) {
  651. // 计算需要多少个插值点,根据显示效果,每250公里需要一个。
  652. if (latLng1.equals(latLng2)) {
  653. // 两个相等的坐标通过下面距离计算会得到地球周长,因此提前判断
  654. return [latLng1];
  655. }
  656. var distance = BMapGL.Projection.getDistance(
  657. toRadian(latLng1.lng),
  658. toRadian(latLng1.lat),
  659. toRadian(latLng2.lng),
  660. toRadian(latLng2.lat)
  661. );
  662. var distance = BMapGL.Projection.getDistanceByLL(latLng1, latLng2);
  663. if (distance < 250000) {
  664. return [latLng1];
  665. }
  666. // 清空现有数据
  667. // this.greatCirclePoints.length = 0;
  668. var result = [];
  669. // 间隔设置小于250公里是因为在靠近南北两极同样的公里数所代表的平面跨度增加
  670. var count = Math.round(distance / 150000);
  671. var angularDistance = calcAngularDistance(latLng1, latLng2);
  672. result.push(latLng1);
  673. for (var i = 0; i < count; i++) {
  674. var eachLatLng = calcMiddlePoint(
  675. latLng1,
  676. latLng2,
  677. i / count,
  678. angularDistance
  679. );
  680. result.push(eachLatLng);
  681. }
  682. result.push(latLng2);
  683. return result;
  684. }
  685. /**
  686. * 给两个点,计算地球大圆上的中间点
  687. * https://www.movable-type.co.uk/scripts/latlong.html
  688. *
  689. * @param {Point} latLng1 起点
  690. * @param {Point} latLng2 终点
  691. * @param {number} f fraction along great circle route
  692. * @param {number} delta angular distance d/R between the two points.
  693. * @return {Point} 大圆上的中间点
  694. */
  695. function calcMiddlePoint(latLng1, latLng2, f, delta) {
  696. var lat1 = latLng1.lat;
  697. var lat2 = latLng2.lat;
  698. var lon1 = latLng1.lng;
  699. var lon2 = latLng2.lng;
  700. var phi1 = toRadian(lat1);
  701. var phi2 = toRadian(lat2);
  702. var lambda1 = toRadian(lon1);
  703. var lambda2 = toRadian(lon2);
  704. var a = Math.sin((1 - f) * delta) / Math.sin(delta);
  705. var b = Math.sin(f * delta) / Math.sin(delta);
  706. var x =
  707. a * Math.cos(phi1) * Math.cos(lambda1) +
  708. b * Math.cos(phi2) * Math.cos(lambda2);
  709. var y =
  710. a * Math.cos(phi1) * Math.sin(lambda1) +
  711. b * Math.cos(phi2) * Math.sin(lambda2);
  712. var z = a * Math.sin(phi1) + b * Math.sin(phi2);
  713. var phi = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
  714. var lambda = Math.atan2(y, x);
  715. return new BMapGL.Point(toAngle(lambda), toAngle(phi));
  716. }
  717. function toRadian(angle) {
  718. return (angle * Math.PI) / 180;
  719. }
  720. function toAngle(radian) {
  721. return (radian / Math.PI) * 180;
  722. }
  723. /**
  724. * 计算角距离
  725. *
  726. * @param {Point} latLng1 起点
  727. * @param {Point} latLng2 终点
  728. * @return {number} 角距离
  729. */
  730. function calcAngularDistance(latLng1, latLng2) {
  731. // console.log(latLng1.lat, latLng1.lng, latLng2.lat, latLng2.lng);
  732. var lat1 = toRadian(latLng1.lat);
  733. var lat2 = toRadian(latLng2.lat);
  734. var lng1 = toRadian(latLng1.lng);
  735. var lng2 = toRadian(latLng2.lng);
  736. return Math.acos(
  737. Math.sin(lat1) * Math.sin(lat2) +
  738. Math.cos(lat1) * Math.cos(lat2) * Math.cos(Math.abs(lng2 - lng1))
  739. );
  740. }
  741. /**
  742. * 自定义的overlay,显示在小车的上方
  743. * @param {Point} Point 要定位的点.
  744. * @param {String} html overlay中要显示的东西.
  745. * @return 无返回值.
  746. */
  747. function CustomOverlay(point, html) {
  748. this._point = point;
  749. this._html = html;
  750. }
  751. CustomOverlay.prototype = new BMapGL.Overlay();
  752. CustomOverlay.prototype.initialize = function (map) {
  753. var div = (this._div = baidu.dom.create("div", {
  754. style:
  755. "border:solid 1px #ccc;width:auto;min-width:50px;text-align:center;position:absolute;background:#fff;color:#000;font-size:12px;border-radius: 10px;padding:5px;white-space: nowrap;",
  756. }));
  757. div.innerHTML = this._html;
  758. map.getPanes().floatPane.appendChild(div);
  759. this._map = map;
  760. return div;
  761. };
  762. CustomOverlay.prototype.draw = function () {
  763. this.setPosition(
  764. this.lushuMain._marker.getPosition(),
  765. this.lushuMain._marker.getIcon().size
  766. );
  767. };
  768. baidu.object.extend(CustomOverlay.prototype, {
  769. //设置overlay的position
  770. setPosition: function (poi, markerSize) {
  771. // 此处的bug已修复,感谢 苗冬(diligentcat@gmail.com) 的细心查看和认真指出
  772. var px = this._map.pointToOverlayPixel(poi);
  773. var styleW = baidu.dom.getStyle(this._div, "width");
  774. var styleH = baidu.dom.getStyle(this._div, "height");
  775. var overlayW = parseInt(this._div.clientWidth || styleW, 10);
  776. var overlayH = parseInt(this._div.clientHeight || styleH, 10);
  777. this._div.style.left = px.x - overlayW / 2 + "px";
  778. this._div.style.bottom = -(px.y - markerSize.height) + "px";
  779. },
  780. //设置overlay的内容
  781. setHtml: function (html) {
  782. this._div.innerHTML = html;
  783. },
  784. //跟customoverlay相关的实例的引用
  785. setRelatedClass: function (lushuMain) {
  786. this.lushuMain = lushuMain;
  787. },
  788. });
  789. })();