index.js 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782
  1. /**
  2. * @fileoverview 百度地图的测距工具类,对外开放。
  3. * 允许用户在地图上点击完成距离的测量。
  4. * 使用者可以自定义测距线段的相关样式,例如线宽、颜色、测距结果所用的单位制等等。
  5. * 主入口类是<a href="symbols/BMapLib.DistanceTool.html">DistanceTool</a>,
  6. * 基于Baidu Map API 1.2。
  7. *
  8. * @author Baidu Map Api Group
  9. * @version 1.2
  10. */
  11. /**
  12. * @namespace BMap的所有library类均放在BMapLib命名空间下
  13. */
  14. (function (root, factory) {
  15. if (typeof exports === 'object') {
  16. module.exports = factory();
  17. } else if (typeof define === 'function' && define.amd) {
  18. define(factory);
  19. } else {
  20. root.BMapLib = root.BMapLib || {};
  21. root.BMapLib.Heatmap = root.BMapLib.DistanceTool || factory();
  22. }
  23. })(this, function() {
  24. var baidu = baidu || {guid : "$BAIDU$"};
  25. var context = {};
  26. (function() {
  27. // 一些页面级别唯一的属性,需要挂载在window[baidu.guid]上
  28. context[baidu.guid] = {};
  29. /**
  30. * 将源对象的所有属性拷贝到目标对象中
  31. * @name baidu.extend
  32. * @function
  33. * @grammar baidu.extend(target, source)
  34. * @param {Object} target 目标对象
  35. * @param {Object} source 源对象
  36. * @returns {Object} 目标对象
  37. */
  38. baidu.extend = function (target, source) {
  39. for (var p in source) {
  40. if (source.hasOwnProperty(p)) {
  41. target[p] = source[p];
  42. }
  43. }
  44. return target;
  45. };
  46. /**
  47. * @ignore
  48. * @namespace
  49. * @baidu.lang 对语言层面的封装,包括类型判断、模块扩展、继承基类以及对象自定义事件的支持。
  50. * @property guid 对象的唯一标识
  51. */
  52. baidu.lang = baidu.lang || {};
  53. /**
  54. * 返回一个当前页面的唯一标识字符串。
  55. * @function
  56. * @grammar baidu.lang.guid()
  57. * @returns {String} 当前页面的唯一标识字符串
  58. */
  59. baidu.lang.guid = function() {
  60. return "TANGRAM__" + (context[baidu.guid]._counter ++).toString(36);
  61. };
  62. context[baidu.guid]._counter = context[baidu.guid]._counter || 1;
  63. /**
  64. * 所有类的实例的容器
  65. * key为每个实例的guid
  66. */
  67. context[baidu.guid]._instances = context[baidu.guid]._instances || {};
  68. /**
  69. * Tangram继承机制提供的一个基类,用户可以通过继承baidu.lang.Class来获取它的属性及方法。
  70. * @function
  71. * @name baidu.lang.Class
  72. * @grammar baidu.lang.Class(guid)
  73. * @param {string} guid 对象的唯一标识
  74. * @meta standard
  75. * @remark baidu.lang.Class和它的子类的实例均包含一个全局唯一的标识guid。
  76. * guid是在构造函数中生成的,因此,继承自baidu.lang.Class的类应该直接或者间接调用它的构造函数。<br>
  77. * baidu.lang.Class的构造函数中产生guid的方式可以保证guid的唯一性,及每个实例都有一个全局唯一的guid。
  78. */
  79. baidu.lang.Class = function(guid) {
  80. this.guid = guid || baidu.lang.guid();
  81. context[baidu.guid]._instances[this.guid] = this;
  82. };
  83. context[baidu.guid]._instances = context[baidu.guid]._instances || {};
  84. /**
  85. * 判断目标参数是否string类型或String对象
  86. * @name baidu.lang.isString
  87. * @function
  88. * @grammar baidu.lang.isString(source)
  89. * @param {Any} source 目标参数
  90. * @shortcut isString
  91. * @meta standard
  92. *
  93. * @returns {boolean} 类型判断结果
  94. */
  95. baidu.lang.isString = function (source) {
  96. return '[object String]' == Object.prototype.toString.call(source);
  97. };
  98. /**
  99. * 判断目标参数是否为function或Function实例
  100. * @name baidu.lang.isFunction
  101. * @function
  102. * @grammar baidu.lang.isFunction(source)
  103. * @param {Any} source 目标参数
  104. * @returns {boolean} 类型判断结果
  105. */
  106. baidu.lang.isFunction = function (source) {
  107. return '[object Function]' == Object.prototype.toString.call(source);
  108. };
  109. /**
  110. * 重载了默认的toString方法,使得返回信息更加准确一些。
  111. * @return {string} 对象的String表示形式
  112. */
  113. baidu.lang.Class.prototype.toString = function(){
  114. return "[object " + (this._className || "Object" ) + "]";
  115. };
  116. /**
  117. * 释放对象所持有的资源,主要是自定义事件。
  118. * @name dispose
  119. * @grammar obj.dispose()
  120. */
  121. baidu.lang.Class.prototype.dispose = function(){
  122. delete context[baidu.guid]._instances[this.guid];
  123. for(var property in this){
  124. if (!baidu.lang.isFunction(this[property])) {
  125. delete this[property];
  126. }
  127. }
  128. this.disposed = true;
  129. };
  130. /**
  131. * 自定义的事件对象。
  132. * @function
  133. * @name baidu.lang.Event
  134. * @grammar baidu.lang.Event(type[, target])
  135. * @param {string} type 事件类型名称。为了方便区分事件和一个普通的方法,事件类型名称必须以"on"(小写)开头。
  136. * @param {Object} [target]触发事件的对象
  137. * @meta standard
  138. * @remark 引入该模块,会自动为Class引入3个事件扩展方法:addEventListener、removeEventListener和dispatchEvent。
  139. * @see baidu.lang.Class
  140. */
  141. baidu.lang.Event = function (type, target) {
  142. this.type = type;
  143. this.returnValue = true;
  144. this.target = target || null;
  145. this.currentTarget = null;
  146. };
  147. /**
  148. * 注册对象的事件监听器。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
  149. * @grammar obj.addEventListener(type, handler[, key])
  150. * @param {string} type 自定义事件的名称
  151. * @param {Function} handler 自定义事件被触发时应该调用的回调函数
  152. * @param {string} [key] 为事件监听函数指定的名称,可在移除时使用。如果不提供,方法会默认为它生成一个全局唯一的key。
  153. * @remark 事件类型区分大小写。如果自定义事件名称不是以小写"on"开头,该方法会给它加上"on"再进行判断,即"click"和"onclick"会被认为是同一种事件。
  154. */
  155. baidu.lang.Class.prototype.addEventListener = function (type, handler, key) {
  156. if (!baidu.lang.isFunction(handler)) {
  157. return;
  158. }
  159. !this.__listeners && (this.__listeners = {});
  160. var t = this.__listeners, id;
  161. if (typeof key == "string" && key) {
  162. if (/[^\w\-]/.test(key)) {
  163. throw("nonstandard key:" + key);
  164. } else {
  165. handler.hashCode = key;
  166. id = key;
  167. }
  168. }
  169. type.indexOf("on") != 0 && (type = "on" + type);
  170. typeof t[type] != "object" && (t[type] = {});
  171. id = id || baidu.lang.guid();
  172. handler.hashCode = id;
  173. t[type][id] = handler;
  174. };
  175. /**
  176. * 移除对象的事件监听器。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
  177. * @grammar obj.removeEventListener(type, handler)
  178. * @param {string} type 事件类型
  179. * @param {Function|string} handler 要移除的事件监听函数或者监听函数的key
  180. * @remark 如果第二个参数handler没有被绑定到对应的自定义事件中,什么也不做。
  181. */
  182. baidu.lang.Class.prototype.removeEventListener = function (type, handler) {
  183. if (baidu.lang.isFunction(handler)) {
  184. handler = handler.hashCode;
  185. } else if (!baidu.lang.isString(handler)) {
  186. return;
  187. }
  188. !this.__listeners && (this.__listeners = {});
  189. type.indexOf("on") != 0 && (type = "on" + type);
  190. var t = this.__listeners;
  191. if (!t[type]) {
  192. return;
  193. }
  194. t[type][handler] && delete t[type][handler];
  195. };
  196. /**
  197. * 派发自定义事件,使得绑定到自定义事件上面的函数都会被执行。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
  198. * @grammar obj.dispatchEvent(event, options)
  199. * @param {baidu.lang.Event|String} event Event对象,或事件名称(1.1.1起支持)
  200. * @param {Object} options 扩展参数,所含属性键值会扩展到Event对象上(1.2起支持)
  201. * @remark 处理会调用通过addEventListenr绑定的自定义事件回调函数之外,还会调用直接绑定到对象上面的自定义事件。
  202. * 例如:<br>
  203. * myobj.onMyEvent = function(){}<br>
  204. * myobj.addEventListener("onMyEvent", function(){});
  205. */
  206. baidu.lang.Class.prototype.dispatchEvent = function (event, options) {
  207. if (baidu.lang.isString(event)) {
  208. event = new baidu.lang.Event(event);
  209. }
  210. !this.__listeners && (this.__listeners = {});
  211. options = options || {};
  212. for (var i in options) {
  213. event[i] = options[i];
  214. }
  215. var i, t = this.__listeners, p = event.type;
  216. event.target = event.target || this;
  217. event.currentTarget = this;
  218. p.indexOf("on") != 0 && (p = "on" + p);
  219. baidu.lang.isFunction(this[p]) && this[p].apply(this, arguments);
  220. if (typeof t[p] == "object") {
  221. for (i in t[p]) {
  222. t[p][i].apply(this, arguments);
  223. }
  224. }
  225. return event.returnValue;
  226. };
  227. /**
  228. * 为类型构造器建立继承关系
  229. * @name baidu.lang.inherits
  230. * @function
  231. * @grammar baidu.lang.inherits(subClass, superClass[, className])
  232. * @param {Function} subClass 子类构造器
  233. * @param {Function} superClass 父类构造器
  234. * @param {string} className 类名标识
  235. * @remark 使subClass继承superClass的prototype,
  236. * 因此subClass的实例能够使用superClass的prototype中定义的所有属性和方法。<br>
  237. * 这个函数实际上是建立了subClass和superClass的原型链集成,并对subClass进行了constructor修正。<br>
  238. * <strong>注意:如果要继承构造函数,需要在subClass里面call一下,具体见下面的demo例子</strong>
  239. * @shortcut inherits
  240. * @meta standard
  241. * @see baidu.lang.Class
  242. */
  243. baidu.lang.inherits = function (subClass, superClass, className) {
  244. var key, proto,
  245. selfProps = subClass.prototype,
  246. clazz = new Function();
  247. clazz.prototype = superClass.prototype;
  248. proto = subClass.prototype = new clazz();
  249. for (key in selfProps) {
  250. proto[key] = selfProps[key];
  251. }
  252. subClass.prototype.constructor = subClass;
  253. subClass.superClass = superClass.prototype;
  254. if ("string" == typeof className) {
  255. proto._className = className;
  256. }
  257. };
  258. /**
  259. * @ignore
  260. * @namespace baidu.dom 操作dom的方法。
  261. */
  262. baidu.dom = baidu.dom || {};
  263. /**
  264. * 从文档中获取指定的DOM元素
  265. *
  266. * @param {string|HTMLElement} id 元素的id或DOM元素
  267. * @meta standard
  268. * @return {HTMLElement} DOM元素,如果不存在,返回null,如果参数不合法,直接返回参数
  269. */
  270. baidu._g = baidu.dom._g = function (id) {
  271. if (baidu.lang.isString(id)) {
  272. return document.getElementById(id);
  273. }
  274. return id;
  275. };
  276. /**
  277. * 从文档中获取指定的DOM元素
  278. * @name baidu.dom.g
  279. * @function
  280. * @grammar baidu.dom.g(id)
  281. * @param {string|HTMLElement} id 元素的id或DOM元素
  282. * @meta standard
  283. *
  284. * @returns {HTMLElement|null} 获取的元素,查找不到时返回null,如果参数不合法,直接返回参数
  285. */
  286. baidu.g = baidu.dom.g = function (id) {
  287. if ('string' == typeof id || id instanceof String) {
  288. return document.getElementById(id);
  289. } else if (id && id.nodeName && (id.nodeType == 1 || id.nodeType == 9)) {
  290. return id;
  291. }
  292. return null;
  293. };
  294. /**
  295. * 在目标元素的指定位置插入HTML代码
  296. * @name baidu.dom.insertHTML
  297. * @function
  298. * @grammar baidu.dom.insertHTML(element, position, html)
  299. * @param {HTMLElement|string} element 目标元素或目标元素的id
  300. * @param {string} position 插入html的位置信息,取值为beforeBegin,afterBegin,beforeEnd,afterEnd
  301. * @param {string} html 要插入的html
  302. * @remark
  303. *
  304. * 对于position参数,大小写不敏感<br>
  305. * 参数的意思:beforeBegin&lt;span&gt;afterBegin this is span! beforeEnd&lt;/span&gt; afterEnd <br />
  306. * 此外,如果使用本函数插入带有script标签的HTML字符串,script标签对应的脚本将不会被执行。
  307. *
  308. * @shortcut insertHTML
  309. * @meta standard
  310. *
  311. * @returns {HTMLElement} 目标元素
  312. */
  313. baidu.insertHTML = baidu.dom.insertHTML = function (element, position, html) {
  314. element = baidu.dom.g(element);
  315. var range,begin;
  316. if (element.insertAdjacentHTML) {
  317. element.insertAdjacentHTML(position, html);
  318. } else {
  319. // 这里不做"undefined" != typeof(HTMLElement) && !window.opera判断,其它浏览器将出错?!
  320. // 但是其实做了判断,其它浏览器下等于这个函数就不能执行了
  321. range = element.ownerDocument.createRange();
  322. // FF下range的位置设置错误可能导致创建出来的fragment在插入dom树之后html结构乱掉
  323. // 改用range.insertNode来插入html, by wenyuxiang @ 2010-12-14.
  324. position = position.toUpperCase();
  325. if (position == 'AFTERBEGIN' || position == 'BEFOREEND') {
  326. range.selectNodeContents(element);
  327. range.collapse(position == 'AFTERBEGIN');
  328. } else {
  329. begin = position == 'BEFOREBEGIN';
  330. range[begin ? 'setStartBefore' : 'setEndAfter'](element);
  331. range.collapse(begin);
  332. }
  333. range.insertNode(range.createContextualFragment(html));
  334. }
  335. return element;
  336. };
  337. /**
  338. * 为目标元素添加className
  339. * @name baidu.dom.addClass
  340. * @function
  341. * @grammar baidu.dom.addClass(element, className)
  342. * @param {HTMLElement|string} element 目标元素或目标元素的id
  343. * @param {string} className 要添加的className,允许同时添加多个class,中间使用空白符分隔
  344. * @remark
  345. * 使用者应保证提供的className合法性,不应包含不合法字符,className合法字符参考:http://www.w3.org/TR/CSS2/syndata.html。
  346. * @shortcut addClass
  347. * @meta standard
  348. *
  349. * @returns {HTMLElement} 目标元素
  350. */
  351. baidu.ac = baidu.dom.addClass = function (element, className) {
  352. element = baidu.dom.g(element);
  353. var classArray = className.split(/\s+/),
  354. result = element.className,
  355. classMatch = " " + result + " ",
  356. i = 0,
  357. l = classArray.length;
  358. for (; i < l; i++){
  359. if ( classMatch.indexOf( " " + classArray[i] + " " ) < 0 ) {
  360. result += (result ? ' ' : '') + classArray[i];
  361. }
  362. }
  363. element.className = result;
  364. return element;
  365. };
  366. /**
  367. * @ignore
  368. * @namespace baidu.event 屏蔽浏览器差异性的事件封装。
  369. * @property target 事件的触发元素
  370. * @property pageX 鼠标事件的鼠标x坐标
  371. * @property pageY 鼠标事件的鼠标y坐标
  372. * @property keyCode 键盘事件的键值
  373. */
  374. baidu.event = baidu.event || {};
  375. /**
  376. * 事件监听器的存储表
  377. * @private
  378. * @meta standard
  379. */
  380. baidu.event._listeners = baidu.event._listeners || [];
  381. /**
  382. * 为目标元素添加事件监听器
  383. * @name baidu.event.on
  384. * @function
  385. * @grammar baidu.event.on(element, type, listener)
  386. * @param {HTMLElement|string|window} element 目标元素或目标元素id
  387. * @param {string} type 事件类型
  388. * @param {Function} listener 需要添加的监听器
  389. * @remark
  390. * 1. 不支持跨浏览器的鼠标滚轮事件监听器添加<br>
  391. * 2. 改方法不为监听器灌入事件对象,以防止跨iframe事件挂载的事件对象获取失败
  392. * @shortcut on
  393. * @meta standard
  394. * @see baidu.event.un
  395. *
  396. * @returns {HTMLElement|window} 目标元素
  397. */
  398. baidu.on = baidu.event.on = function (element, type, listener) {
  399. type = type.replace(/^on/i, '');
  400. element = baidu._g(element);
  401. var realListener = function (ev) {
  402. // 1. 这里不支持EventArgument, 原因是跨frame的事件挂载
  403. // 2. element是为了修正this
  404. listener.call(element, ev);
  405. },
  406. lis = baidu.event._listeners,
  407. filter = baidu.event._eventFilter,
  408. afterFilter,
  409. realType = type;
  410. type = type.toLowerCase();
  411. // filter过滤
  412. if(filter && filter[type]){
  413. afterFilter = filter[type](element, type, realListener);
  414. realType = afterFilter.type;
  415. realListener = afterFilter.listener;
  416. }
  417. // 事件监听器挂载
  418. if (element.addEventListener) {
  419. element.addEventListener(realType, realListener, false);
  420. } else if (element.attachEvent) {
  421. element.attachEvent('on' + realType, realListener);
  422. }
  423. // 将监听器存储到数组中
  424. lis[lis.length] = [element, type, listener, realListener, realType];
  425. return element;
  426. };
  427. /**
  428. * 为目标元素移除事件监听器
  429. * @name baidu.event.un
  430. * @function
  431. * @grammar baidu.event.un(element, type, listener)
  432. * @param {HTMLElement|string|window} element 目标元素或目标元素id
  433. * @param {string} type 事件类型
  434. * @param {Function} listener 需要移除的监听器
  435. * @shortcut un
  436. * @meta standard
  437. *
  438. * @returns {HTMLElement|window} 目标元素
  439. */
  440. baidu.un = baidu.event.un = function (element, type, listener) {
  441. element = baidu._g(element);
  442. type = type.replace(/^on/i, '').toLowerCase();
  443. var lis = baidu.event._listeners,
  444. len = lis.length,
  445. isRemoveAll = !listener,
  446. item,
  447. realType, realListener;
  448. //如果将listener的结构改成json
  449. //可以节省掉这个循环,优化性能
  450. //但是由于un的使用频率并不高,同时在listener不多的时候
  451. //遍历数组的性能消耗不会对代码产生影响
  452. //暂不考虑此优化
  453. while (len--) {
  454. item = lis[len];
  455. // listener存在时,移除element的所有以listener监听的type类型事件
  456. // listener不存在时,移除element的所有type类型事件
  457. if (item[1] === type
  458. && item[0] === element
  459. && (isRemoveAll || item[2] === listener)) {
  460. realType = item[4];
  461. realListener = item[3];
  462. if (element.removeEventListener) {
  463. element.removeEventListener(realType, realListener, false);
  464. } else if (element.detachEvent) {
  465. element.detachEvent('on' + realType, realListener);
  466. }
  467. lis.splice(len, 1);
  468. }
  469. }
  470. return element;
  471. };
  472. /**
  473. * 阻止事件的默认行为
  474. * @name baidu.event.preventDefault
  475. * @function
  476. * @grammar baidu.event.preventDefault(event)
  477. * @param {Event} event 事件对象
  478. * @meta standard
  479. */
  480. baidu.preventDefault = baidu.event.preventDefault = function (event) {
  481. if (event.preventDefault) {
  482. event.preventDefault();
  483. } else {
  484. event.returnValue = false;
  485. }
  486. };
  487. })();
  488. /**
  489. * @exports DistanceTool as BMapLib.DistanceTool
  490. */
  491. var DistanceTool = function(map, opts){
  492. if (!map) {
  493. return;
  494. }
  495. /**
  496. * OperationMask,透明覆盖层,在地图上进行鼠标绘制操作时使用
  497. */
  498. var OperationMask = this.OperationMask = {
  499. /**
  500. * map对象
  501. * @type {Map}
  502. */
  503. _map : null,
  504. /**
  505. * HTML字符串
  506. * @type {String}
  507. */
  508. _html : "<div style='background:transparent url(http://api.map.baidu.com/images/blank.gif);position:absolute;left:0;top:0;width:100%;height:100%;z-index:1000' unselectable='on'></div>",
  509. /**
  510. * html元素
  511. * @type {HTMLElement}
  512. */
  513. _maskElement : null,
  514. /**
  515. * 鼠标指针
  516. * @type {String}
  517. */
  518. _cursor: 'default',
  519. /**
  520. * 操作层是否在使用中
  521. * @type {Boolean}
  522. */
  523. _inUse: false,
  524. /**
  525. * 透明覆盖层的显示
  526. *
  527. * @param {Map} map map对象
  528. * @return 无返回值
  529. */
  530. show : function(map) {
  531. if (!this._map) {
  532. this._map = map;
  533. }
  534. this._inUse = true;
  535. if (!this._maskElement) {
  536. this._createMask(map);
  537. }
  538. this._maskElement.style.display = 'block';
  539. },
  540. /**
  541. * 创建覆盖层
  542. *
  543. * @param {Map} map map对象
  544. * @return 无返回值
  545. */
  546. _createMask : function(map) {
  547. this._map = map;
  548. if (!this._map) {
  549. return;
  550. }
  551. baidu.insertHTML(this._map.getContainer(), "beforeEnd", this._html);
  552. var elem = this._maskElement = this._map.getContainer().lastChild;
  553. var stopAndPrevent = function(e) {
  554. stopBubble(e);
  555. return baidu.preventDefault(e);
  556. }
  557. baidu.on(elem, 'mouseup', function(e) {
  558. if (e.button == 2) {
  559. stopAndPrevent(e);
  560. }
  561. });
  562. baidu.on(elem, 'contextmenu', stopAndPrevent);
  563. elem.style.display = 'none';
  564. },
  565. /**
  566. * 获取当前绘制点的地理坐标
  567. *
  568. * @param {Event} e e对象
  569. * @param {Boolean} n 是否向上查到相对于地图container元素的坐标位置
  570. * @return Point对象的位置信息
  571. */
  572. getDrawPoint : function(e, n) {
  573. e = global.event || e;
  574. var x = e.layerX || e.offsetX || 0;
  575. var y = e.layerY || e.offsetY || 0;
  576. var t = e.target || e.srcElement;
  577. if (t != OperationMask.getDom(this._map) && n == true) {
  578. while (t && t != this._map.getContainer()) {
  579. if (!(t.clientWidth == 0 &&
  580. t.clientHeight == 0 &&
  581. t.offsetParent &&
  582. t.offsetParent.nodeName.toLowerCase() == 'td')) {
  583. x += t.offsetLeft;
  584. y += t.offsetTop;
  585. }
  586. t = t.offsetParent;
  587. }
  588. }
  589. if (t != OperationMask.getDom(this._map) &&
  590. t != this._map.getContainer()) {
  591. return;
  592. }
  593. if (typeof x === 'undefined' ||
  594. typeof y === 'undefined') {
  595. return;
  596. }
  597. if (isNaN(x) || isNaN(y)) {
  598. return;
  599. }
  600. return this._map.pixelToPoint(new BMap.Pixel(x, y));
  601. },
  602. /**
  603. * 透明覆盖层的隐藏
  604. *
  605. * @return 无返回值
  606. */
  607. hide : function() {
  608. if (!this._map) {
  609. return;
  610. }
  611. this._inUse = false;
  612. if (this._maskElement) {
  613. this._maskElement.style.display = 'none';
  614. }
  615. },
  616. /**
  617. * 获取HTML容器
  618. *
  619. * @param {Map} map map对象
  620. * @return HTML容器元素
  621. */
  622. getDom : function(map) {
  623. if (!this._maskElement) {
  624. this._createMask(map);
  625. }
  626. return this._maskElement;
  627. },
  628. /**
  629. * 设置鼠标样式
  630. *
  631. * @type {String} cursor 鼠标样式
  632. * @return 无返回值
  633. */
  634. _setCursor : function(cursor) {
  635. this._cursor = cursor || 'default';
  636. if (this._maskElement) {
  637. this._maskElement.style.cursor = this._cursor;
  638. }
  639. }
  640. };
  641. /**
  642. * map对象
  643. * @private
  644. * @type {Map}
  645. */
  646. this._map = map;
  647. opts = opts || {};
  648. /**
  649. * _opts是默认参数赋值。
  650. * 下面通过用户输入的opts,对默认参数赋值
  651. * @private
  652. * @type {Json}
  653. */
  654. this._opts = baidu.extend(
  655. baidu.extend(this._opts || {}, {
  656. /**
  657. * 测距提示
  658. * @private
  659. * @type {String}
  660. */
  661. tips : "测距",
  662. /**
  663. * 测距过程中,提示框文字
  664. * @private
  665. * @type {String}
  666. */
  667. followText : "单击确定地点,双击结束",
  668. /**
  669. * 测距结果所用的单位制,可接受的属性为"metric"表示米制和"us"表示美国传统单位
  670. * @private
  671. * @type {String}
  672. */
  673. unit : "metric",
  674. /**
  675. * 折线颜色
  676. * @private
  677. * @type {String}
  678. */
  679. lineColor : "#ff6319",
  680. /**
  681. * 折线线宽
  682. * @private
  683. * @type {Number}
  684. */
  685. lineStroke : 2,
  686. /**
  687. * 折线透明度
  688. * @private
  689. * @type {Number}
  690. */
  691. opacity : 0.8,
  692. /**
  693. * 折线样式
  694. * @private
  695. * @type {String}
  696. */
  697. lineStyle : "solid",
  698. /**
  699. * 跟随鼠标样式
  700. * @private
  701. * @type {String}
  702. */
  703. cursor : "http://api.map.baidu.com/images/ruler.cur",
  704. /**
  705. * 转折点的ICON样式
  706. * @private
  707. * @type {BMap.Icon}
  708. */
  709. secIcon : null,
  710. /**
  711. * 转折点的ICON样式
  712. * @private
  713. * @type {BMap.Icon}
  714. */
  715. closeIcon : null
  716. })
  717. , opts);
  718. /**
  719. * 跟随的title覆盖物
  720. * @private
  721. * @type {BMap.Label}
  722. */
  723. this._followTitle = null;
  724. /**
  725. * 折线包含所有点的数组
  726. * @private
  727. * @type {Array}
  728. */
  729. this._points = [];
  730. /**
  731. * 折线所包含的所有path数组
  732. * @private
  733. * @type {Array}
  734. */
  735. this._paths = [];
  736. /**
  737. * 折线结点图片数组
  738. * @private
  739. * @type {Array}
  740. */
  741. this._dots = [];
  742. /**
  743. * 折线测距包含所有线段的距离
  744. * @private
  745. * @type {Array}
  746. */
  747. this._segDistance = [];
  748. /**
  749. * 覆盖物的数组
  750. * @private
  751. * @type {Array}
  752. */
  753. this._overlays = [];
  754. /**
  755. * 是否在调用map.clearOverlays清除画线需要建立的相关overlay元素
  756. * @private
  757. * @type {Boolean}
  758. */
  759. this._enableMassClear = true,
  760. /**
  761. * 单位制,存储语言包中定义的单位名称
  762. * @private
  763. * @type {Json}
  764. */
  765. this._units = {
  766. // metric 表示米制
  767. metric : {
  768. /**
  769. * 米制的名称
  770. * @type {String}
  771. */
  772. name : "metric",
  773. /**
  774. * 和米制的换算关系
  775. * @type {Number}
  776. */
  777. conv : 1,
  778. /**
  779. * 米制单位下两个单位制之间的换算关系
  780. * @type {Number}
  781. */
  782. incon : 1000,
  783. /**
  784. * 米制单位下较小单位
  785. * @type {String}
  786. */
  787. u1 : "米",
  788. /**
  789. * 米制单位下较大单位
  790. * @type {String}
  791. */
  792. u2 : "公里"
  793. },
  794. // us 表示美国传统单位,各参数意义同上metric
  795. us : {
  796. name : "us",
  797. conv : 3.2808,
  798. incon : 5279.856,
  799. u1 : "英尺",
  800. u2 : "英里"
  801. }
  802. };
  803. /**
  804. * 是否已经开启了测距状态
  805. * @private
  806. * @type {Boolean}
  807. */
  808. this._isOpen = false;
  809. /**
  810. * 未点击任何一点时,鼠标移动时的跟随提示文字
  811. * @private
  812. * @type {String}
  813. */
  814. this._startFollowText = "单击确定起点";
  815. /**
  816. * 地图移动的计时器
  817. * @private
  818. * @type {Object}
  819. */
  820. this._movingTimerId = null;
  821. /**
  822. * 测距需要添加的CSS样式
  823. * @private
  824. * @type {Json}
  825. */
  826. this._styles = {
  827. "BMapLib_diso" : "height:17px;width:5px;position:absolute;background:url(http://api.map.baidu.com/images/dis_box_01.gif) no-repeat left top"
  828. ,"BMapLib_disi" : "color:#7a7a7a;position:absolute;left:5px;padding:0 4px 1px 0;line-height:17px;background:url(http://api.map.baidu.com/images/dis_box_01.gif) no-repeat right top"
  829. ,"BMapLib_disBoxDis" : "color:#ff6319;font-weight:bold"
  830. };
  831. if (this._opts.lineStroke <= 0) {
  832. this._opts.lineStroke = 2;
  833. }
  834. if (this._opts.opacity > 1) {
  835. this._opts.opacity = 1;
  836. } else if (this._opts.opacity < 0) {
  837. this._opts.opacity = 0;
  838. }
  839. if (this._opts.lineStyle != "solid" &&
  840. this._opts.lineStyle != "dashed") {
  841. this._opts.lineStyle = "solid";
  842. }
  843. if (!this._units[this._opts.unit]) {
  844. this._opts.unit = "metric";
  845. }
  846. this.text = "测距";
  847. }
  848. // 通过baidu.lang下的inherits方法,让DistanceTool继承baidu.lang.Class
  849. baidu.lang.inherits(DistanceTool, baidu.lang.Class, "DistanceTool");
  850. /**
  851. * 地图区域的移动事件绑定
  852. * @return 无返回值
  853. */
  854. DistanceTool.prototype._bind = function(){
  855. // 设置鼠标样式
  856. this._setCursor(this._opts.cursor);
  857. var me = this;
  858. var OperationMask = this.OperationMask;
  859. // 在装载地图的页面元素上,绑定鼠标移动事件
  860. baidu.on(this._map.getContainer(), "mousemove", function(e){
  861. if (!me._isOpen) {
  862. return;
  863. }
  864. if (!me._followTitle) {
  865. return;
  866. }
  867. e = global.event || e;
  868. var t = e.target || e.srcElement;
  869. // 如果触发该事件的页面元素不是遮盖效果层,则返回,无操作
  870. if (t != OperationMask.getDom(me._map)) {
  871. me._followTitle.hide();
  872. return;
  873. }
  874. if (!me._mapMoving) {
  875. me._followTitle.show();
  876. }
  877. // 设置鼠标移动过程中,跟随的文字提示框的位置
  878. var pt = OperationMask.getDrawPoint(e, true);
  879. me._followTitle.setPosition(pt);
  880. });
  881. // 创建鼠标跟随的文字提示框
  882. if (this._startFollowText) {
  883. var t = this._followTitle = new BMap.Label(this._startFollowText, {offset : new BMap.Size(14, 16)});
  884. this._followTitle.setStyles({color : "#333", borderColor : "#ff0103"});
  885. }
  886. };
  887. /**
  888. * 开启地图的测距状态
  889. * @return {Boolean},开启测距状态成功,返回true;否则返回false。
  890. *
  891. * @example <b>参考示例:</b><br />
  892. * myDistanceToolObject.open();
  893. */
  894. DistanceTool.prototype.open = function(){
  895. var OperationMask = this.OperationMask;
  896. // 判断测距状态是否已经开启
  897. if (this._isOpen == true){
  898. return true;
  899. }
  900. // 已有其他地图上的鼠标操作工具开启
  901. if (DistanceTool._toolInUse) {
  902. return;
  903. } else {
  904. this._isOpen = true;
  905. DistanceTool._toolInUse = true;
  906. }
  907. // 判断是否是否在移动过程中
  908. if (this._mapMoving){
  909. delete this._mapMoving;
  910. }
  911. var me = this;
  912. // 增加鼠标在地图区域移动的事件
  913. // 通过binded参数,避免多次绑定
  914. if (!this._binded) {
  915. this._binded = true;
  916. // 绑定控件项事件
  917. this._bind();
  918. // 地图的移动过程中,需要隐藏相关的提示框
  919. this._map.addEventListener("moving", function(){
  920. me._hideCurrent();
  921. });
  922. }
  923. // 将文字提示框作为BMap.Label元素,提交给Map Api进行管理
  924. if (this._followTitle) {
  925. this._map.addOverlay(this._followTitle);
  926. this._followTitle.hide();
  927. }
  928. /**
  929. * 测距过程中,点击地图时,触发的操作
  930. * @ignore
  931. * @param {Object} e event对象
  932. */
  933. var distClick = function(e) {
  934. var map = me._map;
  935. if (!me._isOpen) {
  936. return;
  937. }
  938. // 通过event对象,计算得出点击位置的物理坐标,poi为一个BMap.Point对象
  939. e = global.event || e;
  940. var poi = OperationMask.getDrawPoint(e, true);
  941. // 验证计算得出的该点的位置合理性
  942. if (!me._isPointValid(poi)) {
  943. return;
  944. }
  945. // 记录当前点的屏幕位置
  946. me._bind.initX = e.pageX || e.clientX || 0;
  947. me._bind.initY = e.pageY || e.clientY || 0;
  948. // 这个if循环内的计算是,判断当前这个点,与存储内的最后一个点的距离,
  949. // 如果距离过小,比如小于5,可以认为是用户的误点,可以忽略掉
  950. if (me._points.length > 0){
  951. var lstPx = map.pointToPixel(me._points[me._points.length - 1]);
  952. var thisPx = map.pointToPixel(poi);
  953. var dis = Math.sqrt(Math.pow(lstPx.x - thisPx.x, 2) + Math.pow(lstPx.y - thisPx.y, 2));
  954. if (dis < 5) {
  955. return;
  956. }
  957. }
  958. me._bind.x = e.layerX || e.offsetX || 0;
  959. me._bind.y = e.layerY || e.offsetY || 0;
  960. me._points.push(poi);
  961. // 添加测距结点
  962. me._addSecPoint(poi);
  963. // 调整跟踪鼠标的标签
  964. if (me._paths.length == 0) {
  965. me._formatTitle(1, me._opts.followText, me._getTotalDistance());
  966. }
  967. // 修改确定线的颜色
  968. if (me._paths.length > 0) {
  969. me._paths[me._paths.length - 1].show();
  970. me._paths[me._paths.length - 1].setStrokeOpacity(me._opts.opacity);
  971. }
  972. var path = new BMap.Polyline([poi, poi], {enableMassClear : me._enableMassClear});
  973. me._map.addOverlay(path);
  974. me._paths.push(path);
  975. me._overlays.push(path);
  976. // 测距模式下线样式固定
  977. path.setStrokeWeight(me._opts.lineStroke);
  978. path.setStrokeColor(me._opts.lineColor);
  979. path.setStrokeOpacity(me._opts.opacity / 2);
  980. path.setStrokeStyle(me._opts.lineStyle);
  981. // 如果地图正在移动则隐藏掉
  982. if (me._mapMoving){
  983. path.hide();
  984. }
  985. if (me._points.length > 1) {
  986. var siblingPath = me._paths[me._points.length - 2];
  987. siblingPath.setPositionAt(1, poi);
  988. }
  989. // 生成节点旁边的距离显示框
  990. var disText = "";
  991. if (me._points.length > 1) {
  992. // 非起点的节点,显示当前的距离
  993. var segDis = me._setSegDistance(me._points[me._points.length - 2], me._points[me._points.length - 1]);
  994. var meters = me._getTotalDistance();
  995. disText = me._formatDisStr(meters);
  996. } else {
  997. disText = "起点";
  998. }
  999. var disLabel = new BMap.Label(disText, {offset : new BMap.Size(10, -5), enableMassClear : me._enableMassClear});
  1000. disLabel.setStyles({color : "#333", borderColor : "#ff0103"});
  1001. me._map.addOverlay(disLabel);
  1002. me._formatSegLabel(disLabel, disText);
  1003. me._overlays.push(disLabel);
  1004. poi.disLabel = disLabel;
  1005. disLabel.setPosition(poi);
  1006. /**
  1007. * 测距过程中,每次点击底图添加节点时,派发事件的接口
  1008. * @name DistanceTool#onaddpoint
  1009. * @event
  1010. * @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
  1011. * <br />{"<b>point</b> : {BMap.Point} 最新添加上的节点BMap.Point对象,
  1012. * <br />"<b>pixel</b>:{BMap.pixel} 最新添加上的节点BMap.Pixel对象,
  1013. * <br />"<b>index</b>:{Number} 最新添加的节点的索引,
  1014. * <br />"<b>distance</b>:{Number} 截止最新添加的节点的总距离}
  1015. *
  1016. * @example <b>参考示例:</b><br />
  1017. * myDistanceToolObject.addEventListener("addpoint", function(e) { alert(e.distance); });
  1018. */
  1019. // 生成名为onaddpoint的baidu.lang.Event对象
  1020. // 并给该event对象添加上point、pixel、index和distance等属性字段
  1021. // 然后在此刻,将绑定在onaddpoint上事件,全部赋予event参数,然后派发出去
  1022. var event = new baidu.lang.Event("onaddpoint");
  1023. event.point = poi;
  1024. event.pixel = me._map.pointToPixel(poi);
  1025. event.index = me._points.length - 1;
  1026. event.distance = me._getTotalDistance().toFixed(0);
  1027. me.dispatchEvent(event);
  1028. };
  1029. /**
  1030. * 测距过程中,鼠标在地图上移动时,触发的操作
  1031. * @ignore
  1032. * @param {Object} e event对象
  1033. */
  1034. var distMove = function(e) {
  1035. if (!me._isOpen){
  1036. return;
  1037. }
  1038. // 通过判断数组me._paths的长度,判断当前是否已经有测量节点
  1039. // 也就是,如果没有节点,则还没有开始测量
  1040. if (me._paths.length > 0) {
  1041. // 通过event参数,计算当前点的位置
  1042. e = global.event || e;
  1043. var curX = e.pageX || e.clientX || 0;
  1044. var curY = e.pageY || e.clientY || 0;
  1045. if (typeof me._bind.initX == "undefined") {
  1046. me._bind.x = e.layerX || e.offsetX || 0;
  1047. me._bind.y = e.layerY || e.offsetY || 0;
  1048. me._bind.initX = curX;
  1049. me._bind.initY = curY;
  1050. }
  1051. var x = me._bind.x + curX - me._bind.initX;
  1052. var y = me._bind.y + curY - me._bind.initY;
  1053. // 修改最后一条折线的终点位置,使之随着鼠标移动画线
  1054. var path = me._paths[me._paths.length - 1];
  1055. var poi = me._map.pixelToPoint(new BMap.Pixel(x, y));
  1056. path.setPositionAt(1, poi);
  1057. if (!me._mapMoving) {
  1058. path.show();
  1059. }
  1060. var dx = 0;
  1061. var dy = 0;
  1062. // 计算当前鼠标位置,是否靠近边界、或者已经出了边界
  1063. // 如果在边界位置,则需要向对应的方向移动地图,来进行测量
  1064. // 每次移动的距离,设定为8
  1065. if (x < 10) {
  1066. dx = 8;
  1067. } else if (x > me._map.getSize().width - 10){
  1068. dx = -8;
  1069. }
  1070. if (y < 10) {
  1071. dy = 8;
  1072. } else if (y > me._map.getSize().height - 10){
  1073. dy = -8;
  1074. }
  1075. // 如果dx和dy都等于0,表明不需要移动地图
  1076. if (dx != 0 || dy != 0){
  1077. // 此时需要向一个方向,平移地图
  1078. if (!distMove._movingTimerId){
  1079. me._mapMoving = true;
  1080. me._map.panBy(dx, dy, {noAnimation : true});
  1081. me._movingTimerId = distMove._movingTimerId = setInterval(function(){
  1082. me._map.panBy(dx, dy, {noAnimation : true});
  1083. }, 30);
  1084. // 地图移动过程中,隐藏线段和标签
  1085. path.hide();
  1086. me._followTitle && me._followTitle.hide();
  1087. }
  1088. } else {
  1089. if (distMove._movingTimerId) {
  1090. // 此时用户不在需要移动地图来测量,可以清除计时器
  1091. clearInterval(distMove._movingTimerId);
  1092. delete distMove._movingTimerId;
  1093. delete me._movingTimerId;
  1094. // 显示跟随提示框,并修改线路位置
  1095. var lstP = me._paths[me._paths.length - 1];
  1096. var poiN = me._map.pixelToPoint(new BMap.Pixel(x, y));
  1097. if (!lstP) {
  1098. return;
  1099. }
  1100. lstP.setPositionAt(1, poiN);
  1101. lstP.show();
  1102. if (me._followTitle) {
  1103. me._followTitle.setPosition(poiN);
  1104. me._followTitle.show();
  1105. }
  1106. me._bind.i = 0;
  1107. me._bind.j = 0;
  1108. delete me._mapMoving;
  1109. }
  1110. }
  1111. // 实时更新文字提示框中的距离
  1112. if (me._followTitle) {
  1113. var td = me._getTotalDistance();
  1114. var dis = me._map.getDistance(me._points[me._points.length - 1], poi);
  1115. me._updateInstDis(me._followTitle, td + dis);
  1116. }
  1117. } else {
  1118. // 此时用户还没有开始测量,只是鼠标随便在地图上移动
  1119. if (me._followTitle) {
  1120. me._followTitle.show();
  1121. e = global.event || e;
  1122. var t = e.target || e.srcElement;
  1123. if (t != OperationMask.getDom()) {
  1124. me._followTitle.hide();
  1125. }
  1126. }
  1127. }
  1128. };
  1129. /**
  1130. * 测距要结束时,双击地图,触发的操作
  1131. * @ignore
  1132. * @param {Object} e event对象
  1133. */
  1134. var distDblclick = function(e) {
  1135. if (!me._isOpen) {
  1136. return;
  1137. }
  1138. // 结束时,删除绑定的事件
  1139. baidu.un(OperationMask.getDom(me._map), "click", distClick);
  1140. baidu.un(document, "mousemove", distMove);
  1141. baidu.un(OperationMask.getDom(me._map), "dblclick", distDblclick);
  1142. baidu.un(document, "keydown", distKeyDown);
  1143. baidu.un(OperationMask.getDom(me._map), "mouseup", distMouseUp);
  1144. // 调用close()关闭测距状态
  1145. setTimeout(function(){
  1146. me.close();
  1147. }, 50);
  1148. };
  1149. /**
  1150. * 测距时的键盘操作
  1151. * @ignore
  1152. * @param {Object} e event对象
  1153. */
  1154. var distKeyDown = function(e){
  1155. e = global.event || e;
  1156. if (e.keyCode == 27){
  1157. // [ESC]退出本次测距
  1158. me._clearCurData();
  1159. setTimeout(function(){
  1160. me.close();
  1161. }, 50);
  1162. }
  1163. };
  1164. /**
  1165. * 测距过程中,鼠标弹起时,触发的操作
  1166. * @ignore
  1167. * @param {Object} e event对象
  1168. */
  1169. var distMouseUp = function(e) {
  1170. e = global.event || e;
  1171. var ieVersion = 0;
  1172. if (/msie (\d+\.\d)/i.test(navigator.userAgent)) {
  1173. ieVersion = document.documentMode || + RegExp['\x241'];
  1174. }
  1175. if (ieVersion &&
  1176. e.button != 1 ||
  1177. e.button == 2){
  1178. me.close();
  1179. }
  1180. };
  1181. // 初始化存储数据
  1182. me._initData();
  1183. // 调整title的内容
  1184. this._formatTitle();
  1185. // 创建透明覆盖层,并设置鼠标样式
  1186. OperationMask.show(this._map);
  1187. this._setCursor(this._opts.cursor);
  1188. // 绑定全部事件
  1189. baidu.on(OperationMask.getDom(this._map), "click", distClick);
  1190. baidu.on(document, "mousemove", distMove);
  1191. baidu.on(OperationMask.getDom(this._map), "dblclick", distDblclick);
  1192. baidu.on(document, "keydown", distKeyDown);
  1193. baidu.on(OperationMask.getDom(this._map), "mouseup", distMouseUp);
  1194. // 将绑定的事件、和对应的绑定对象,记录在数组中
  1195. this.bindFunc = [
  1196. {elem : OperationMask.getDom(this._map), type : "click", func : distClick},
  1197. {elem : OperationMask.getDom(this._map), type : "dblclick", func : distDblclick},
  1198. {elem : document, type : "mousemove", func : distMove},
  1199. {elem : document, type : "keydown", func : distKeyDown},
  1200. {elem : OperationMask.getDom(this._map), type : "mouseup", func : distMouseUp}];
  1201. return true;
  1202. };
  1203. /**
  1204. * 画线结束时,派发drawend事件
  1205. * @return 无返回值
  1206. */
  1207. DistanceTool.prototype._dispatchLastEvent = function() {
  1208. /**
  1209. * 测距时,每次双击底图结束当前测距折线时,派发事件的接口
  1210. * @name DistanceTool#ondrawend
  1211. * @event
  1212. * @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
  1213. * <br />{"<b>points</b> : {BMap.Point} 所有测量时,打下的节点BMap.Point对象,
  1214. * <br />"<b>overlays</b>:{Array} 所有测量时,生成的线段BMap.Overlay对象,
  1215. * <br />"<b>distance</b>:{Number} 测量解释时的最终距离}
  1216. *
  1217. * @example <b>参考示例:</b><br />
  1218. * myDistanceToolObject.addEventListener("drawend", function(e) { alert(e.distance); });
  1219. */
  1220. // 生成名为ondrawend的baidu.lang.Event对象
  1221. // 并给该event对象添加上points、overlays和distance等属性字段
  1222. // 然后在此刻,将绑定在ondrawend上事件,全部赋予event参数,然后派发出去
  1223. var event = new baidu.lang.Event("ondrawend");
  1224. event.points =
  1225. this._points ?
  1226. this._points.slice(0) :
  1227. [];
  1228. event.overlays =
  1229. this._paths ?
  1230. this._paths.slice(0, this._paths.length - 1) :
  1231. [];
  1232. event.distance = this._getTotalDistance().toFixed(0);
  1233. this.dispatchEvent(event);
  1234. };
  1235. /**
  1236. * 关闭测距状态
  1237. * @return 无返回值
  1238. *
  1239. * @example <b>参考示例:</b><br />
  1240. * myDistanceToolObject.close();
  1241. */
  1242. DistanceTool.prototype.close = function(){
  1243. var OperationMask = this.OperationMask;
  1244. if (this._isOpen == false){
  1245. return;
  1246. }
  1247. this._isOpen = false;
  1248. DistanceTool._toolInUse = false;
  1249. if (this._mapMoving){
  1250. delete this._mapMoving;
  1251. }
  1252. var me = this;
  1253. me._dispatchLastEvent();
  1254. if (me._points.length < 2){
  1255. // 不是有效绘制,清除所有内容
  1256. me._clearCurData();
  1257. } else {
  1258. me._paths[me._paths.length - 1].remove();
  1259. me._paths[me._paths.length - 1] = null;
  1260. me._paths.length = me._paths.length - 1;
  1261. // 移除最近一次标记
  1262. var pt = me._points[me._points.length - 1];
  1263. if (pt.disLabel){
  1264. pt.disLabel.remove();
  1265. }
  1266. me._processLastOp();
  1267. }
  1268. OperationMask.hide();
  1269. // 删除绑定的事件
  1270. for (var i = 0, l = this.bindFunc.length; i < l; i ++){
  1271. baidu.un(this.bindFunc[i].elem, this.bindFunc[i].type, this.bindFunc[i].func);
  1272. }
  1273. // 停止地图移动
  1274. if (me._movingTimerId){
  1275. clearInterval(me._movingTimerId);
  1276. me._movingTimerId = null;
  1277. }
  1278. if (this._followTitle){
  1279. this._followTitle.hide();
  1280. }
  1281. };
  1282. /**
  1283. * 清除本次测距的暂存数据
  1284. * @return 无返回值
  1285. */
  1286. DistanceTool.prototype._clearCurData = function(){
  1287. var OperationMask = this.OperationMask;
  1288. for (var i = 0, l = this._points.length; i < l; i ++){
  1289. if (this._points[i].disLabel){
  1290. this._points[i].disLabel.remove();
  1291. }
  1292. }
  1293. for (var i = 0, l = this._paths.length; i < l; i ++){
  1294. this._paths[i].remove();
  1295. }
  1296. for (var i = 0, l = this._dots.length; i < l; i ++){
  1297. this._dots[i].remove();
  1298. }
  1299. this._initData();
  1300. };
  1301. /**
  1302. * 初始化存储数组
  1303. * @return 无返回值
  1304. */
  1305. DistanceTool.prototype._initData = function(){
  1306. // 初始化point数组
  1307. this._points.length = 0;
  1308. // 初始化path数组
  1309. this._paths.length = 0;
  1310. // 初始化分段距离数组
  1311. this._segDistance.length = 0;
  1312. // 初始化结点图像数组
  1313. this._dots.length = 0;
  1314. };
  1315. /**
  1316. * 计算两点之间距离并存放在分段距离数组中
  1317. * @param {Point}
  1318. * @param {Point}
  1319. * @return {Number} 两个地理点之间的距离
  1320. */
  1321. DistanceTool.prototype._setSegDistance = function(pt0, pt1){
  1322. if (!pt0 || !pt1){
  1323. return;
  1324. }
  1325. var dis = this._map.getDistance(pt0, pt1);
  1326. this._segDistance.push(dis);
  1327. return dis;
  1328. };
  1329. /**
  1330. * 获得总距离
  1331. * @return {Number} 总距离
  1332. */
  1333. DistanceTool.prototype._getTotalDistance = function(){
  1334. var totalDis = 0;
  1335. for (var i = 0, l = this._segDistance.length; i < l; i ++){
  1336. totalDis += this._segDistance[i];
  1337. }
  1338. return totalDis;
  1339. };
  1340. /**
  1341. * 将米制单位的数值换算成为目标单位下的数值
  1342. * @type {Number} 需要转换的数值
  1343. * @type {String} 字符串描述的目标单位,
  1344. * "metric" 表示米制单位,
  1345. * "us" 表示美国传统单位制
  1346. * @return {Number} 转换后的数值
  1347. */
  1348. DistanceTool.prototype._convertUnit = function(num, unit){
  1349. unit = unit || "metric";
  1350. if (this._units[unit]){
  1351. return num * this._units[unit].conv;
  1352. }
  1353. return num;
  1354. };
  1355. /**
  1356. * 添加测距结点
  1357. * @param {BMap.Point} 节点
  1358. * @return 无返回值
  1359. */
  1360. DistanceTool.prototype._addSecPoint = function(pt){
  1361. var ico =
  1362. this._opts.secIcon ?
  1363. this._opts.secIcon :
  1364. new BMap.Icon("http://api.map.baidu.com/images/mapctrls.png", new BMap.Size(11, 11), {imageOffset: new BMap.Size(-26, -313)});
  1365. var secPt = new BMap.Marker(pt, {
  1366. icon : ico,
  1367. clickable : false,
  1368. baseZIndex : 3500000,
  1369. zIndexFixed : true,
  1370. enableMassClear : this._enableMassClear
  1371. });
  1372. this._map.addOverlay(secPt);
  1373. this._dots.push(secPt);
  1374. };
  1375. /**
  1376. * 格式化距离字符串
  1377. * @param {Number} 距离
  1378. * @return {String} 格式化的字符串
  1379. */
  1380. DistanceTool.prototype._formatDisStr = function(distance){
  1381. var u = this._opts.unit;
  1382. var unit = this._units[u].u1;
  1383. var dis = this._convertUnit(distance, u);
  1384. if (dis > this._units[u].incon){
  1385. dis = dis / this._units[u].incon;
  1386. unit = this._units[u].u2;
  1387. dis = dis.toFixed(1);
  1388. } else {
  1389. dis = dis.toFixed(0);
  1390. }
  1391. return dis + unit;
  1392. };
  1393. /**
  1394. * 设置鼠标样式
  1395. * @param {String} cursor 鼠标样式
  1396. * @return 没有返回值
  1397. */
  1398. DistanceTool.prototype._setCursor = function(cursor){
  1399. var OperationMask = this.OperationMask;
  1400. // 由于webkit内核浏览器下,cursor设置后默认不会居中,所以需要对偏移值进行设置
  1401. var csr =
  1402. /webkit/.test(navigator.userAgent.toLowerCase()) ?
  1403. "url(" + this._opts.cursor + ") 3 6, crosshair" :
  1404. "url(" + this._opts.cursor + "), crosshair"
  1405. OperationMask._setCursor(csr);
  1406. };
  1407. /**
  1408. * 获取鼠标样式
  1409. * @return {String} 跟随的鼠标样式
  1410. */
  1411. DistanceTool.prototype._getCursor = function(){
  1412. return this._opts.cursor;
  1413. };
  1414. /**
  1415. * 调整分段距离样式
  1416. * @param {BMap.Label} label 提示框的Label
  1417. * @param {String} 需要填入的文字
  1418. * @return 没有返回值
  1419. */
  1420. DistanceTool.prototype._formatSegLabel = function(label, text){
  1421. label.setStyle({"border" : "none", "padding" : "0"});
  1422. label.setContent("<span style='" + this._styles.BMapLib_diso + "'><span style='" + this._styles.BMapLib_disi + "'>" + text + "</span></span>");
  1423. };
  1424. /**
  1425. * 处理最后一次操作,当用户双击或测距被强行退出时调用
  1426. * @return 没有返回值
  1427. */
  1428. DistanceTool.prototype._processLastOp = function() {
  1429. var me = this;
  1430. // 删除上次移动临时数据
  1431. delete me._bind.x;
  1432. delete me._bind.y;
  1433. delete me._bind.initX;
  1434. delete me._bind.initY;
  1435. // 验证路径
  1436. if (me._paths.length > me._points.length - 1){
  1437. var l = me._paths.length - 1;
  1438. me._paths[l].remove();
  1439. me._paths[l] = null;
  1440. me._paths.length = l;
  1441. }
  1442. // 保存本次测距对象
  1443. var disObj = {};
  1444. disObj.points = me._points.slice(0);
  1445. disObj.paths = me._paths.slice(0);
  1446. disObj.dots = me._dots.slice(0);
  1447. disObj.segDis = me._segDistance.slice(0);
  1448. // 判断总距离和按钮位置
  1449. var lstPx = me._map.pointToPixel(disObj.points[disObj.points.length - 1]);
  1450. var prePx = me._map.pointToPixel(disObj.points[disObj.points.length - 2]);
  1451. var btnOffset = [0, 0];
  1452. var disOffset = [0, 0];
  1453. if (lstPx.y - prePx.y >= 0){
  1454. // 距离位于下端
  1455. disOffset = [-5, 11];
  1456. } else {
  1457. // 距离位于上端
  1458. disOffset = [-5, -35];
  1459. }
  1460. if (lstPx.x - prePx.x >= 0){
  1461. // 按钮位于右侧
  1462. btnOffset = [14, 0];
  1463. } else {
  1464. // 按钮位于左侧
  1465. btnOffset = [-14, 0];
  1466. }
  1467. // 显示总距离
  1468. var pt = disObj.points[disObj.points.length - 1];
  1469. pt.disLabel = new BMap.Label("", {offset: new BMap.Size(-15, -40), enableMassClear: me._enableMassClear});
  1470. pt.disLabel.setStyles({color: "#333", borderColor: "#ff0103"});
  1471. me._map.addOverlay(pt.disLabel);
  1472. pt.disLabel.setOffset(new BMap.Size(disOffset[0], disOffset[1]));
  1473. pt.disLabel.setPosition(pt);
  1474. me._formatTitle(2, "", "", pt.disLabel);
  1475. // 添加关闭按钮
  1476. var bico =
  1477. this._opts.closeIcon ?
  1478. this._opts.closeIcon :
  1479. new BMap.Icon("http://api.map.baidu.com/images/mapctrls.gif", new BMap.Size(12, 12), {imageOffset: new BMap.Size(0, -14)});
  1480. disObj.closeBtn = new BMap.Marker(disObj.points[disObj.points.length - 1],
  1481. {icon : bico,
  1482. offset : new BMap.Size(btnOffset[0], btnOffset[1]),
  1483. baseZIndex : 3600000,
  1484. enableMassClear : me._enableMassClear}
  1485. );
  1486. me._map.addOverlay(disObj.closeBtn);
  1487. disObj.closeBtn.setTitle("清除本次测距");
  1488. // 点击关闭按钮,绑定关闭按钮事件
  1489. disObj.closeBtn.addEventListener("click", function(e){
  1490. // 关闭本次测距,清除相关存储和变量
  1491. for (var i = 0, l = disObj.points.length; i < l; i ++){
  1492. disObj.points[i].disLabel.remove();
  1493. disObj.points[i].disLabel = null;
  1494. }
  1495. for (var i = 0, l = disObj.paths.length; i < l; i ++){
  1496. disObj.paths[i].remove();
  1497. disObj.paths[i] = null;
  1498. }
  1499. for (var i = 0, l = disObj.dots.length; i < l; i ++){
  1500. disObj.dots[i].remove();
  1501. disObj.dots[i] = null;
  1502. }
  1503. disObj.closeBtn.remove();
  1504. disObj.closeBtn = null;
  1505. stopBubble(e);
  1506. /**
  1507. * @ignore
  1508. * 测距结束后,点击线段上最后一个节点旁的关闭按钮时,派发事件的接口
  1509. * @name DistanceTool#onremovepolyline
  1510. * @event
  1511. * @param {Event Object} e 回调函数会返回event参数
  1512. *
  1513. * @example <b>参考示例:</b><br />
  1514. * myDistanceToolObject.addEventListener("removepolyline", function(e) { alert(e.type); });
  1515. */
  1516. // 生成名为onremovepolyline的baidu.lang.Event对象
  1517. // 然后在此刻,将绑定在onremovepolyline上事件,全部赋予event参数,然后派发出去
  1518. var event = new baidu.lang.Event("onremovepolyline");
  1519. me.dispatchEvent(event);
  1520. });
  1521. me._initData();
  1522. };
  1523. /**
  1524. * 生成测距过程中的文字提示框
  1525. * @param {String} type
  1526. * @param {String} text
  1527. * @param {String} distance
  1528. * @param {Label} label
  1529. * @return 无返回值
  1530. */
  1531. DistanceTool.prototype._formatTitle = function(type, text, distance, label){
  1532. var title = label || this._followTitle;
  1533. if (!title){
  1534. return;
  1535. }
  1536. title.setStyle({"lineHeight" : "16px", "zIndex" : "85", "padding" : "3px 5px"});
  1537. var t = this._startFollowText || "";
  1538. var htmls = [];
  1539. if (type == 1){
  1540. // 测距过程中的提示
  1541. title.setOffset(0, 25);
  1542. var u = this._opts.unit;
  1543. var unit = this._units[u].u1;
  1544. var dis = this._convertUnit(distance, u);
  1545. if (dis > this._units[u].incon){
  1546. dis = dis / this._units[u].incon;
  1547. unit = this._units[u].u2;
  1548. dis = dis.toFixed(1);
  1549. } else {
  1550. dis = dis.toFixed(0);
  1551. }
  1552. htmls.push("<span>总长:<span style='" + this._styles.BMapLib_disBoxDis+"'>" + dis + "</span>" + unit + "</span><br />");
  1553. htmls.push("<span style='color:#7a7a7a'>" + text + "</span>");
  1554. } else if (type == 2) {
  1555. // 结束时的总距离展示
  1556. var u = this._opts.unit;
  1557. var unit = this._units[u].u1;
  1558. var dis = this._convertUnit(this._getTotalDistance(), u);
  1559. if (dis > this._units[u].incon){
  1560. dis = dis / this._units[u].incon;
  1561. unit = this._units[u].u2;
  1562. dis = dis.toFixed(1);
  1563. } else{
  1564. dis = dis.toFixed(0);
  1565. }
  1566. htmls.push("总长:<span style='" + this._styles.BMapLib_disBoxDis + "'>" + dis + "</span>" + unit);
  1567. } else {
  1568. title.setOffset(0, 25);
  1569. htmls.push(t);
  1570. }
  1571. title.setContent(htmls.join(""));
  1572. };
  1573. /**
  1574. * 更新label的距离
  1575. * @param HTMLElement label的DOM元素
  1576. * @param Number 距离
  1577. */
  1578. DistanceTool.prototype._updateInstDis = function(label, dis){
  1579. // 换算距离
  1580. var u = this._opts.unit;
  1581. var unit = this._units[u].u1;
  1582. if (dis > this._units[u].incon){
  1583. dis = dis / this._units[u].incon;
  1584. unit = this._units[u].u2;
  1585. dis = dis.toFixed(1);
  1586. } else {
  1587. dis = dis.toFixed(0);
  1588. }
  1589. // 修改Label的内容
  1590. if (label) {
  1591. var htmls = [];
  1592. htmls.push("<span>总长:<span style='" + this._styles.BMapLib_disBoxDis + "'>" + dis + "</span>" + unit + "</span><br />");
  1593. htmls.push("<span style='color:#7a7a7a'>" + this._opts.followText + "</span>");
  1594. label.setContent(htmls.join(""));
  1595. }
  1596. };
  1597. /**
  1598. * 隐藏相关的线段和提示框文字
  1599. * @return 无返回值
  1600. */
  1601. DistanceTool.prototype._hideCurrent = function(){
  1602. if (!this._isOpen){
  1603. return;
  1604. }
  1605. if (this._paths.length > 0){
  1606. var p = this._paths[this._paths.length - 1];
  1607. p.hide();
  1608. }
  1609. this._followTitle && this._followTitle.hide();
  1610. };
  1611. /**
  1612. * 验证传入点的位置合理性
  1613. * @param {BMap.Point} pt 需要被验证的point点
  1614. * @return 无返回值
  1615. */
  1616. DistanceTool.prototype._isPointValid = function(pt){
  1617. if (!pt){
  1618. return false;
  1619. }
  1620. var mapBounds = this._map.getBounds();
  1621. var sw = mapBounds.getSouthWest(),
  1622. ne = mapBounds.getNorthEast();
  1623. if (pt.lng < sw.lng ||
  1624. pt.lng > ne.lng ||
  1625. pt.lat < sw.lat ||
  1626. pt.lat > ne.lat) {
  1627. return false;
  1628. }
  1629. return true;
  1630. };
  1631. /**
  1632. * 停止事件冒泡传播,
  1633. * 闭包,对外不暴露
  1634. *
  1635. * @type {Event} e e对象
  1636. */
  1637. function stopBubble(e){
  1638. var e = global.event || e;
  1639. e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true;
  1640. };
  1641. return DistanceTool;
  1642. });