index.js 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  3. typeof define === 'function' && define.amd ? define(factory) :
  4. (global.BMapLib = global.BMapLib || {}, global.BMapLib.MarkerClusterer = factory());
  5. }(this, (function () { 'use strict';
  6. var __commonjs_global = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : this;
  7. function __commonjs(fn, module) { return module = { exports: {} }, fn(module, module.exports, __commonjs_global), module.exports; }
  8. var index$1 = __commonjs(function (module, exports, global) {
  9. (function (root, factory) {
  10. if (typeof exports === 'object') {
  11. module.exports = factory();
  12. } else if (typeof define === 'function' && define.amd) {
  13. define(factory);
  14. } else {
  15. root.BMapLib = root.BMapLib || {};
  16. root.BMapLib.TextIconOverlay = root.BMapLib.TextIconOverlay || factory();
  17. }
  18. })(__commonjs_global, function() {
  19. var T,
  20. baidu = T = baidu || {version: "1.3.8"};
  21. var context = {};
  22. //提出guid,防止在与老版本Tangram混用时
  23. //在下一行错误的修改context[undefined]
  24. baidu.guid = "$BAIDU$";
  25. //Tangram可能被放在闭包中
  26. //一些页面级别唯一的属性,需要挂载在context[baidu.guid]上
  27. context[baidu.guid] = context[baidu.guid] || {};
  28. /**
  29. * @ignore
  30. * @namespace baidu.dom 操作dom的方法。
  31. */
  32. baidu.dom = baidu.dom || {};
  33. /**
  34. * 从文档中获取指定的DOM元素
  35. * @name baidu.dom.g
  36. * @function
  37. * @grammar baidu.dom.g(id)
  38. * @param {string|HTMLElement} id 元素的id或DOM元素
  39. * @shortcut g,T.G
  40. * @meta standard
  41. * @see baidu.dom.q
  42. *
  43. * @returns {HTMLElement|null} 获取的元素,查找不到时返回null,如果参数不合法,直接返回参数
  44. */
  45. baidu.dom.g = function (id) {
  46. if ('string' == typeof id || id instanceof String) {
  47. return document.getElementById(id);
  48. } else if (id && id.nodeName && (id.nodeType == 1 || id.nodeType == 9)) {
  49. return id;
  50. }
  51. return null;
  52. };
  53. // 声明快捷方法
  54. baidu.g = baidu.G = baidu.dom.g;
  55. /**
  56. * 获取目标元素所属的document对象
  57. * @name baidu.dom.getDocument
  58. * @function
  59. * @grammar baidu.dom.getDocument(element)
  60. * @param {HTMLElement|string} element 目标元素或目标元素的id
  61. * @meta standard
  62. * @see baidu.dom.getWindow
  63. *
  64. * @returns {HTMLDocument} 目标元素所属的document对象
  65. */
  66. baidu.dom.getDocument = function (element) {
  67. element = baidu.dom.g(element);
  68. return element.nodeType == 9 ? element : element.ownerDocument || element.document;
  69. };
  70. /**
  71. * @ignore
  72. * @namespace baidu.lang 对语言层面的封装,包括类型判断、模块扩展、继承基类以及对象自定义事件的支持。
  73. */
  74. baidu.lang = baidu.lang || {};
  75. /**
  76. * 判断目标参数是否string类型或String对象
  77. * @name baidu.lang.isString
  78. * @function
  79. * @grammar baidu.lang.isString(source)
  80. * @param {Any} source 目标参数
  81. * @shortcut isString
  82. * @meta standard
  83. * @see baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isArray,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate
  84. *
  85. * @returns {boolean} 类型判断结果
  86. */
  87. baidu.lang.isString = function (source) {
  88. return '[object String]' == Object.prototype.toString.call(source);
  89. };
  90. // 声明快捷方法
  91. baidu.isString = baidu.lang.isString;
  92. /**
  93. * 从文档中获取指定的DOM元素
  94. * **内部方法**
  95. *
  96. * @param {string|HTMLElement} id 元素的id或DOM元素
  97. * @meta standard
  98. * @return {HTMLElement} DOM元素,如果不存在,返回null,如果参数不合法,直接返回参数
  99. */
  100. baidu.dom._g = function (id) {
  101. if (baidu.lang.isString(id)) {
  102. return document.getElementById(id);
  103. }
  104. return id;
  105. };
  106. // 声明快捷方法
  107. baidu._g = baidu.dom._g;
  108. /**
  109. * @ignore
  110. * @namespace baidu.browser 判断浏览器类型和特性的属性。
  111. */
  112. baidu.browser = baidu.browser || {};
  113. if (/msie (\d+\.\d)/i.test(navigator.userAgent)) {
  114. //IE 8下,以documentMode为准
  115. //在百度模板中,可能会有$,防止冲突,将$1 写成 \x241
  116. /**
  117. * 判断是否为ie浏览器
  118. * @property ie ie版本号
  119. * @grammar baidu.browser.ie
  120. * @meta standard
  121. * @shortcut ie
  122. * @see baidu.browser.firefox,baidu.browser.safari,baidu.browser.opera,baidu.browser.chrome,baidu.browser.maxthon
  123. */
  124. baidu.browser.ie = baidu.ie = document.documentMode || + RegExp['\x241'];
  125. }
  126. /**
  127. * 获取目标元素的computed style值。如果元素的样式值不能被浏览器计算,则会返回空字符串(IE)
  128. *
  129. * @author berg
  130. * @name baidu.dom.getComputedStyle
  131. * @function
  132. * @grammar baidu.dom.getComputedStyle(element, key)
  133. * @param {HTMLElement|string} element 目标元素或目标元素的id
  134. * @param {string} key 要获取的样式名
  135. *
  136. * @see baidu.dom.getStyle
  137. *
  138. * @returns {string} 目标元素的computed style值
  139. */
  140. baidu.dom.getComputedStyle = function(element, key){
  141. element = baidu.dom._g(element);
  142. var doc = baidu.dom.getDocument(element),
  143. styles;
  144. if (doc.defaultView && doc.defaultView.getComputedStyle) {
  145. styles = doc.defaultView.getComputedStyle(element, null);
  146. if (styles) {
  147. return styles[key] || styles.getPropertyValue(key);
  148. }
  149. }
  150. return '';
  151. };
  152. /**
  153. * 提供给setStyle与getStyle使用
  154. */
  155. baidu.dom._styleFixer = baidu.dom._styleFixer || {};
  156. /**
  157. * 提供给setStyle与getStyle使用
  158. */
  159. baidu.dom._styleFilter = baidu.dom._styleFilter || [];
  160. /**
  161. * 为获取和设置样式的过滤器
  162. * @private
  163. * @meta standard
  164. */
  165. baidu.dom._styleFilter.filter = function (key, value, method) {
  166. for (var i = 0, filters = baidu.dom._styleFilter, filter; filter = filters[i]; i++) {
  167. if (filter = filter[method]) {
  168. value = filter(key, value);
  169. }
  170. }
  171. return value;
  172. };
  173. /**
  174. * @ignore
  175. * @namespace baidu.string 操作字符串的方法。
  176. */
  177. baidu.string = baidu.string || {};
  178. /**
  179. * 将目标字符串进行驼峰化处理
  180. * @name baidu.string.toCamelCase
  181. * @function
  182. * @grammar baidu.string.toCamelCase(source)
  183. * @param {string} source 目标字符串
  184. * @remark
  185. * 支持单词以“-_”分隔
  186. * @meta standard
  187. *
  188. * @returns {string} 驼峰化处理后的字符串
  189. */
  190. baidu.string.toCamelCase = function (source) {
  191. //提前判断,提高getStyle等的效率 thanks xianwei
  192. if (source.indexOf('-') < 0 && source.indexOf('_') < 0) {
  193. return source;
  194. }
  195. return source.replace(/[-_][^-_]/g, function (match) {
  196. return match.charAt(1).toUpperCase();
  197. });
  198. };
  199. /**
  200. * 获取目标元素的样式值
  201. * @name baidu.dom.getStyle
  202. * @function
  203. * @grammar baidu.dom.getStyle(element, key)
  204. * @param {HTMLElement|string} element 目标元素或目标元素的id
  205. * @param {string} key 要获取的样式名
  206. * @remark
  207. *
  208. * 为了精简代码,本模块默认不对任何浏览器返回值进行归一化处理(如使用getStyle时,不同浏览器下可能返回rgb颜色或hex颜色),也不会修复浏览器的bug和差异性(如设置IE的float属性叫styleFloat,firefox则是cssFloat)。<br />
  209. * baidu.dom._styleFixer和baidu.dom._styleFilter可以为本模块提供支持。<br />
  210. * 其中_styleFilter能对颜色和px进行归一化处理,_styleFixer能对display,float,opacity,textOverflow的浏览器兼容性bug进行处理。
  211. * @shortcut getStyle
  212. * @meta standard
  213. * @see baidu.dom.setStyle,baidu.dom.setStyles, baidu.dom.getComputedStyle
  214. *
  215. * @returns {string} 目标元素的样式值
  216. */
  217. baidu.dom.getStyle = function (element, key) {
  218. var dom = baidu.dom;
  219. element = dom.g(element);
  220. key = baidu.string.toCamelCase(key);
  221. //computed style, then cascaded style, then explicitly set style.
  222. var value = element.style[key] ||
  223. (element.currentStyle ? element.currentStyle[key] : "") ||
  224. dom.getComputedStyle(element, key);
  225. // 在取不到值的时候,用fixer进行修正
  226. if (!value) {
  227. var fixer = dom._styleFixer[key];
  228. if(fixer){
  229. value = fixer.get ? fixer.get(element) : baidu.dom.getStyle(element, fixer);
  230. }
  231. }
  232. /* 检查结果过滤器 */
  233. if (fixer = dom._styleFilter) {
  234. value = fixer.filter(key, value, 'get');
  235. }
  236. return value;
  237. };
  238. // 声明快捷方法
  239. baidu.getStyle = baidu.dom.getStyle;
  240. if (/opera\/(\d+\.\d)/i.test(navigator.userAgent)) {
  241. /**
  242. * 判断是否为opera浏览器
  243. * @property opera opera版本号
  244. * @grammar baidu.browser.opera
  245. * @meta standard
  246. * @see baidu.browser.ie,baidu.browser.firefox,baidu.browser.safari,baidu.browser.chrome
  247. */
  248. baidu.browser.opera = + RegExp['\x241'];
  249. }
  250. /**
  251. * 判断是否为webkit内核
  252. * @property isWebkit
  253. * @grammar baidu.browser.isWebkit
  254. * @meta standard
  255. * @see baidu.browser.isGecko
  256. */
  257. baidu.browser.isWebkit = /webkit/i.test(navigator.userAgent);
  258. /**
  259. * 判断是否为gecko内核
  260. * @property isGecko
  261. * @grammar baidu.browser.isGecko
  262. * @meta standard
  263. * @see baidu.browser.isWebkit
  264. */
  265. baidu.browser.isGecko = /gecko/i.test(navigator.userAgent) && !/like gecko/i.test(navigator.userAgent);
  266. /**
  267. * 判断是否严格标准的渲染模式
  268. * @property isStrict
  269. * @grammar baidu.browser.isStrict
  270. * @meta standard
  271. */
  272. baidu.browser.isStrict = document.compatMode == "CSS1Compat";
  273. /**
  274. * 获取目标元素相对于整个文档左上角的位置
  275. * @name baidu.dom.getPosition
  276. * @function
  277. * @grammar baidu.dom.getPosition(element)
  278. * @param {HTMLElement|string} element 目标元素或目标元素的id
  279. * @meta standard
  280. *
  281. * @returns {Object} 目标元素的位置,键值为top和left的Object。
  282. */
  283. baidu.dom.getPosition = function (element) {
  284. element = baidu.dom.g(element);
  285. var doc = baidu.dom.getDocument(element),
  286. browser = baidu.browser,
  287. getStyle = baidu.dom.getStyle,
  288. // Gecko 1.9版本以下用getBoxObjectFor计算位置
  289. // 但是某些情况下是有bug的
  290. // 对于这些有bug的情况
  291. // 使用递归查找的方式
  292. BUGGY_GECKO_BOX_OBJECT = browser.isGecko > 0 &&
  293. doc.getBoxObjectFor &&
  294. getStyle(element, 'position') == 'absolute' &&
  295. (element.style.top === '' || element.style.left === ''),
  296. pos = {"left":0,"top":0},
  297. viewport = (browser.ie && !browser.isStrict) ? doc.body : doc.documentElement,
  298. parent,
  299. box;
  300. if(element == viewport){
  301. return pos;
  302. }
  303. if(element.getBoundingClientRect){ // IE and Gecko 1.9+
  304. //当HTML或者BODY有border width时, 原生的getBoundingClientRect返回值是不符合预期的
  305. //考虑到通常情况下 HTML和BODY的border只会设成0px,所以忽略该问题.
  306. box = element.getBoundingClientRect();
  307. pos.left = Math.floor(box.left) + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
  308. pos.top = Math.floor(box.top) + Math.max(doc.documentElement.scrollTop, doc.body.scrollTop);
  309. // IE会给HTML元素添加一个border,默认是medium(2px)
  310. // 但是在IE 6 7 的怪异模式下,可以被html { border: 0; } 这条css规则覆盖
  311. // 在IE7的标准模式下,border永远是2px,这个值通过clientLeft 和 clientTop取得
  312. // 但是。。。在IE 6 7的怪异模式,如果用户使用css覆盖了默认的medium
  313. // clientTop和clientLeft不会更新
  314. pos.left -= doc.documentElement.clientLeft;
  315. pos.top -= doc.documentElement.clientTop;
  316. var htmlDom = doc.body,
  317. // 在这里,不使用element.style.borderLeftWidth,只有computedStyle是可信的
  318. htmlBorderLeftWidth = parseInt(getStyle(htmlDom, 'borderLeftWidth')),
  319. htmlBorderTopWidth = parseInt(getStyle(htmlDom, 'borderTopWidth'));
  320. if(browser.ie && !browser.isStrict){
  321. pos.left -= isNaN(htmlBorderLeftWidth) ? 2 : htmlBorderLeftWidth;
  322. pos.top -= isNaN(htmlBorderTopWidth) ? 2 : htmlBorderTopWidth;
  323. }
  324. } else {
  325. // safari/opera/firefox
  326. parent = element;
  327. do {
  328. pos.left += parent.offsetLeft;
  329. pos.top += parent.offsetTop;
  330. // safari里面,如果遍历到了一个fixed的元素,后面的offset都不准了
  331. if (browser.isWebkit > 0 && getStyle(parent, 'position') == 'fixed') {
  332. pos.left += doc.body.scrollLeft;
  333. pos.top += doc.body.scrollTop;
  334. break;
  335. }
  336. parent = parent.offsetParent;
  337. } while (parent && parent != element);
  338. // 对body offsetTop的修正
  339. if(browser.opera > 0 || (browser.isWebkit > 0 && getStyle(element, 'position') == 'absolute')){
  340. pos.top -= doc.body.offsetTop;
  341. }
  342. // 计算除了body的scroll
  343. parent = element.offsetParent;
  344. while (parent && parent != doc.body) {
  345. pos.left -= parent.scrollLeft;
  346. // see https://bugs.opera.com/show_bug.cgi?id=249965
  347. if (!browser.opera || parent.tagName != 'TR') {
  348. pos.top -= parent.scrollTop;
  349. }
  350. parent = parent.offsetParent;
  351. }
  352. }
  353. return pos;
  354. };
  355. /**
  356. * @ignore
  357. * @namespace baidu.event 屏蔽浏览器差异性的事件封装。
  358. * @property target 事件的触发元素
  359. * @property pageX 鼠标事件的鼠标x坐标
  360. * @property pageY 鼠标事件的鼠标y坐标
  361. * @property keyCode 键盘事件的键值
  362. */
  363. baidu.event = baidu.event || {};
  364. /**
  365. * 事件监听器的存储表
  366. * @private
  367. * @meta standard
  368. */
  369. baidu.event._listeners = baidu.event._listeners || [];
  370. /**
  371. * 为目标元素添加事件监听器
  372. * @name baidu.event.on
  373. * @function
  374. * @grammar baidu.event.on(element, type, listener)
  375. * @param {HTMLElement|string|window} element 目标元素或目标元素id
  376. * @param {string} type 事件类型
  377. * @param {Function} listener 需要添加的监听器
  378. * @remark
  379. *
  380. 1. 不支持跨浏览器的鼠标滚轮事件监听器添加<br>
  381. 2. 改方法不为监听器灌入事件对象,以防止跨iframe事件挂载的事件对象获取失败
  382. * @shortcut on
  383. * @meta standard
  384. * @see baidu.event.un
  385. *
  386. * @returns {HTMLElement|window} 目标元素
  387. */
  388. baidu.event.on = function (element, type, listener) {
  389. type = type.replace(/^on/i, '');
  390. element = baidu.dom._g(element);
  391. var realListener = function (ev) {
  392. // 1. 这里不支持EventArgument, 原因是跨frame的事件挂载
  393. // 2. element是为了修正this
  394. listener.call(element, ev);
  395. },
  396. lis = baidu.event._listeners,
  397. filter = baidu.event._eventFilter,
  398. afterFilter,
  399. realType = type;
  400. type = type.toLowerCase();
  401. // filter过滤
  402. if(filter && filter[type]){
  403. afterFilter = filter[type](element, type, realListener);
  404. realType = afterFilter.type;
  405. realListener = afterFilter.listener;
  406. }
  407. // 事件监听器挂载
  408. if (element.addEventListener) {
  409. element.addEventListener(realType, realListener, false);
  410. } else if (element.attachEvent) {
  411. element.attachEvent('on' + realType, realListener);
  412. }
  413. // 将监听器存储到数组中
  414. lis[lis.length] = [element, type, listener, realListener, realType];
  415. return element;
  416. };
  417. // 声明快捷方法
  418. baidu.on = baidu.event.on;
  419. /**
  420. * 返回一个当前页面的唯一标识字符串。
  421. * @name baidu.lang.guid
  422. * @function
  423. * @grammar baidu.lang.guid()
  424. * @version 1.1.1
  425. * @meta standard
  426. *
  427. * @returns {String} 当前页面的唯一标识字符串
  428. */
  429. (function(){
  430. //不直接使用window,可以提高3倍左右性能
  431. var guid = context[baidu.guid];
  432. baidu.lang.guid = function() {
  433. return "TANGRAM__" + (guid._counter ++).toString(36);
  434. };
  435. guid._counter = guid._counter || 1;
  436. })();
  437. /**
  438. * 所有类的实例的容器
  439. * key为每个实例的guid
  440. * @meta standard
  441. */
  442. context[baidu.guid]._instances = context[baidu.guid]._instances || {};
  443. /**
  444. * 判断目标参数是否为function或Function实例
  445. * @name baidu.lang.isFunction
  446. * @function
  447. * @grammar baidu.lang.isFunction(source)
  448. * @param {Any} source 目标参数
  449. * @version 1.2
  450. * @see baidu.lang.isString,baidu.lang.isObject,baidu.lang.isNumber,baidu.lang.isArray,baidu.lang.isElement,baidu.lang.isBoolean,baidu.lang.isDate
  451. * @meta standard
  452. * @returns {boolean} 类型判断结果
  453. */
  454. baidu.lang.isFunction = function (source) {
  455. // chrome下,'function' == typeof /a/ 为true.
  456. return '[object Function]' == Object.prototype.toString.call(source);
  457. };
  458. /**
  459. *
  460. * @ignore
  461. * @class Tangram继承机制提供的一个基类,用户可以通过继承baidu.lang.Class来获取它的属性及方法。
  462. * @name baidu.lang.Class
  463. * @grammar baidu.lang.Class(guid)
  464. * @param {string} guid 对象的唯一标识
  465. * @meta standard
  466. * @remark baidu.lang.Class和它的子类的实例均包含一个全局唯一的标识guid。guid是在构造函数中生成的,因此,继承自baidu.lang.Class的类应该直接或者间接调用它的构造函数。<br>baidu.lang.Class的构造函数中产生guid的方式可以保证guid的唯一性,及每个实例都有一个全局唯一的guid。
  467. * @meta standard
  468. * @see baidu.lang.inherits,baidu.lang.Event
  469. */
  470. baidu.lang.Class = function(guid) {
  471. this.guid = guid || baidu.lang.guid();
  472. context[baidu.guid]._instances[this.guid] = this;
  473. };
  474. context[baidu.guid]._instances = context[baidu.guid]._instances || {};
  475. /**
  476. * 释放对象所持有的资源,主要是自定义事件。
  477. * @name dispose
  478. * @grammar obj.dispose()
  479. */
  480. baidu.lang.Class.prototype.dispose = function(){
  481. delete context[baidu.guid]._instances[this.guid];
  482. for(var property in this){
  483. if (!baidu.lang.isFunction(this[property])) {
  484. delete this[property];
  485. }
  486. }
  487. this.disposed = true;
  488. };
  489. /**
  490. * 重载了默认的toString方法,使得返回信息更加准确一些。
  491. * @return {string} 对象的String表示形式
  492. */
  493. baidu.lang.Class.prototype.toString = function(){
  494. return "[object " + (this._className || "Object" ) + "]";
  495. };
  496. /**
  497. * @ignore
  498. * @class 自定义的事件对象。
  499. * @name baidu.lang.Event
  500. * @grammar baidu.lang.Event(type[, target])
  501. * @param {string} type 事件类型名称。为了方便区分事件和一个普通的方法,事件类型名称必须以"on"(小写)开头。
  502. * @param {Object} [target]触发事件的对象
  503. * @meta standard
  504. * @remark 引入该模块,会自动为Class引入3个事件扩展方法:addEventListener、removeEventListener和dispatchEvent。
  505. * @meta standard
  506. * @see baidu.lang.Class
  507. */
  508. baidu.lang.Event = function (type, target) {
  509. this.type = type;
  510. this.returnValue = true;
  511. this.target = target || null;
  512. this.currentTarget = null;
  513. };
  514. /**
  515. * 注册对象的事件监听器。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
  516. * @grammar obj.addEventListener(type, handler[, key])
  517. * @param {string} type 自定义事件的名称
  518. * @param {Function} handler 自定义事件被触发时应该调用的回调函数
  519. * @param {string} [key] 为事件监听函数指定的名称,可在移除时使用。如果不提供,方法会默认为它生成一个全局唯一的key。
  520. * @remark 事件类型区分大小写。如果自定义事件名称不是以小写"on"开头,该方法会给它加上"on"再进行判断,即"click"和"onclick"会被认为是同一种事件。
  521. */
  522. baidu.lang.Class.prototype.addEventListener = function (type, handler, key) {
  523. if (!baidu.lang.isFunction(handler)) {
  524. return;
  525. }
  526. !this.__listeners && (this.__listeners = {});
  527. var t = this.__listeners, id;
  528. if (typeof key == "string" && key) {
  529. if (/[^\w\-]/.test(key)) {
  530. throw("nonstandard key:" + key);
  531. } else {
  532. handler.hashCode = key;
  533. id = key;
  534. }
  535. }
  536. type.indexOf("on") != 0 && (type = "on" + type);
  537. typeof t[type] != "object" && (t[type] = {});
  538. id = id || baidu.lang.guid();
  539. handler.hashCode = id;
  540. t[type][id] = handler;
  541. };
  542. /**
  543. * 移除对象的事件监听器。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
  544. * @grammar obj.removeEventListener(type, handler)
  545. * @param {string} type 事件类型
  546. * @param {Function|string} handler 要移除的事件监听函数或者监听函数的key
  547. * @remark 如果第二个参数handler没有被绑定到对应的自定义事件中,什么也不做。
  548. */
  549. baidu.lang.Class.prototype.removeEventListener = function (type, handler) {
  550. if (typeof handler != "undefined") {
  551. if ( (baidu.lang.isFunction(handler) && ! (handler = handler.hashCode))
  552. || (! baidu.lang.isString(handler))
  553. ){
  554. return;
  555. }
  556. }
  557. !this.__listeners && (this.__listeners = {});
  558. type.indexOf("on") != 0 && (type = "on" + type);
  559. var t = this.__listeners;
  560. if (!t[type]) {
  561. return;
  562. }
  563. if (typeof handler != "undefined") {
  564. t[type][handler] && delete t[type][handler];
  565. } else {
  566. for(var guid in t[type]){
  567. delete t[type][guid];
  568. }
  569. }
  570. };
  571. /**
  572. * 派发自定义事件,使得绑定到自定义事件上面的函数都会被执行。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
  573. * @grammar obj.dispatchEvent(event, options)
  574. * @param {baidu.lang.Event|String} event Event对象,或事件名称(1.1.1起支持)
  575. * @param {Object} options 扩展参数,所含属性键值会扩展到Event对象上(1.2起支持)
  576. * @remark 处理会调用通过addEventListenr绑定的自定义事件回调函数之外,还会调用直接绑定到对象上面的自定义事件。例如:<br>
  577. myobj.onMyEvent = function(){}<br>
  578. myobj.addEventListener("onMyEvent", function(){});
  579. */
  580. baidu.lang.Class.prototype.dispatchEvent = function (event, options) {
  581. if (baidu.lang.isString(event)) {
  582. event = new baidu.lang.Event(event);
  583. }
  584. !this.__listeners && (this.__listeners = {});
  585. // 20100603 添加本方法的第二个参数,将 options extend到event中去传递
  586. options = options || {};
  587. for (var i in options) {
  588. event[i] = options[i];
  589. }
  590. var i, t = this.__listeners, p = event.type;
  591. event.target = event.target || this;
  592. event.currentTarget = this;
  593. p.indexOf("on") != 0 && (p = "on" + p);
  594. baidu.lang.isFunction(this[p]) && this[p].apply(this, arguments);
  595. if (typeof t[p] == "object") {
  596. for (i in t[p]) {
  597. t[p][i].apply(this, arguments);
  598. }
  599. }
  600. return event.returnValue;
  601. };
  602. baidu.lang.inherits = function (subClass, superClass, className) {
  603. var key, proto,
  604. selfProps = subClass.prototype,
  605. clazz = new Function();
  606. clazz.prototype = superClass.prototype;
  607. proto = subClass.prototype = new clazz();
  608. for (key in selfProps) {
  609. proto[key] = selfProps[key];
  610. }
  611. subClass.prototype.constructor = subClass;
  612. subClass.superClass = superClass.prototype;
  613. // 类名标识,兼容Class的toString,基本没用
  614. if ("string" == typeof className) {
  615. proto._className = className;
  616. }
  617. };
  618. // 声明快捷方法
  619. baidu.inherits = baidu.lang.inherits;
  620. /**
  621. * 图片的路径
  622. * @private
  623. * @type {String}
  624. */
  625. var _IMAGE_PATH = 'http://api.map.baidu.com/library/TextIconOverlay/1.2/src/images/m';
  626. /**
  627. * 图片的后缀名
  628. * @private
  629. * @type {String}
  630. */
  631. var _IMAGE_EXTENSION = 'png';
  632. /**
  633. *@exports TextIconOverlay as BMapLib.TextIconOverlay
  634. */
  635. /**
  636. * TextIconOverlay
  637. * @class 此类表示地图上的一个覆盖物,该覆盖物由文字和图标组成,从Overlay继承。文字通常是数字(0-9)或字母(A-Z ),而文字与图标之间有一定的映射关系。
  638. *该覆盖物适用于以下类似的场景:需要在地图上添加一系列覆盖物,这些覆盖物之间用不同的图标和文字来区分,文字可能表示了该覆盖物的某一属性值,根据该文字和一定的映射关系,自动匹配相应颜色和大小的图标。
  639. *
  640. *@constructor
  641. *@param {Point} position 表示一个经纬度坐标位置。
  642. *@param {String} text 表示该覆盖物显示的文字信息。
  643. *@param {Json Object} options 可选参数,可选项包括:<br />
  644. *"<b>styles</b>":{Array<IconStyle>} 一组图标风格。单个图表风格包括以下几个属性:<br />
  645. * url {String} 图片的url地址。(必选)<br />
  646. * size {Size} 图片的大小。(必选)<br />
  647. * anchor {Size} 图标定位在地图上的位置相对于图标左上角的偏移值,默认偏移值为图标的中心位置。(可选)<br />
  648. * offset {Size} 图片相对于可视区域的偏移值,此功能的作用等同于CSS中的background-position属性。(可选)<br />
  649. * textSize {Number} 文字的大小。(可选,默认10)<br />
  650. * textColor {String} 文字的颜色。(可选,默认black)<br />
  651. */
  652. var TextIconOverlay = function(position, text, options){
  653. try {
  654. BMap;
  655. } catch (e) {
  656. throw Error('Baidu Map JS API is not ready yet!');
  657. }
  658. T.lang.inherits(TextIconOverlay, BMap.Overlay, "TextIconOverlay");
  659. this._position = position;
  660. this._text = text;
  661. this._options = options || {};
  662. this._styles = this._options['styles'] || [];
  663. (!this._styles.length) && this._setupDefaultStyles();
  664. };
  665. TextIconOverlay.prototype._setupDefaultStyles = function(){
  666. var sizes = [53, 56, 66, 78, 90];
  667. for(var i = 0, size; size = sizes[i]; i++){
  668. this._styles.push({
  669. url:_IMAGE_PATH + i + '.' + _IMAGE_EXTENSION,
  670. size: new BMap.Size(size, size)
  671. });
  672. }//for循环的简洁写法
  673. };
  674. /**
  675. *继承Overlay的intialize方法,自定义覆盖物时必须。
  676. *@param {Map} map BMap.Map的实例化对象。
  677. *@return {HTMLElement} 返回覆盖物对应的HTML元素。
  678. */
  679. TextIconOverlay.prototype.initialize = function(map){
  680. this._map = map;
  681. this._domElement = document.createElement('div');
  682. this._updateCss();
  683. this._updateText();
  684. this._updatePosition();
  685. this._bind();
  686. this._map.getPanes().markerMouseTarget.appendChild(this._domElement);
  687. return this._domElement;
  688. };
  689. /**
  690. *继承Overlay的draw方法,自定义覆盖物时必须。
  691. *@return 无返回值。
  692. */
  693. TextIconOverlay.prototype.draw = function(){
  694. this._map && this._updatePosition();
  695. };
  696. /**
  697. *获取该覆盖物上的文字。
  698. *@return {String} 该覆盖物上的文字。
  699. */
  700. TextIconOverlay.prototype.getText = function(){
  701. return this._text;
  702. };
  703. /**
  704. *设置该覆盖物上的文字。
  705. *@param {String} text 要设置的文字,通常是字母A-Z或数字0-9。
  706. *@return 无返回值。
  707. */
  708. TextIconOverlay.prototype.setText = function(text){
  709. if(text && (!this._text || (this._text.toString() != text.toString()))){
  710. this._text = text;
  711. this._updateText();
  712. this._updateCss();
  713. this._updatePosition();
  714. }
  715. };
  716. /**
  717. *获取该覆盖物的位置。
  718. *@return {Point} 该覆盖物的经纬度坐标。
  719. */
  720. TextIconOverlay.prototype.getPosition = function () {
  721. return this._position;
  722. };
  723. /**
  724. *设置该覆盖物的位置。
  725. *@param {Point} position 要设置的经纬度坐标。
  726. *@return 无返回值。
  727. */
  728. TextIconOverlay.prototype.setPosition = function (position) {
  729. if(position && (!this._position || !this._position.equals(position))){
  730. this._position = position;
  731. this._updatePosition();
  732. }
  733. };
  734. /**
  735. *由文字信息获取风格数组的对应索引值。
  736. *内部默认的对应函数为文字转换为数字除以10的结果,比如文字8返回索引0,文字25返回索引2.
  737. *如果需要自定义映射关系,请覆盖该函数。
  738. *@param {String} text 文字。
  739. *@param {Array<IconStyle>} styles 一组图标风格。
  740. *@return {Number} 对应的索引值。
  741. */
  742. TextIconOverlay.prototype.getStyleByText = function(text, styles){
  743. var count = parseInt(text);
  744. var index = parseInt(count / 10);
  745. index = Math.max(0, index);
  746. index = Math.min(index, styles.length - 1);
  747. return styles[index];
  748. };
  749. /**
  750. *更新相应的CSS。
  751. *@return 无返回值。
  752. */
  753. TextIconOverlay.prototype._updateCss = function(){
  754. if (!this._domElement) {
  755. return
  756. }
  757. var style = this.getStyleByText(this._text, this._styles);
  758. this._domElement.style.cssText = this._buildCssText(style);
  759. };
  760. /**
  761. *更新覆盖物的显示文字。
  762. *@return 无返回值。
  763. */
  764. TextIconOverlay.prototype._updateText = function(){
  765. if (this._domElement) {
  766. this._domElement.innerHTML = this._text;
  767. }
  768. };
  769. /**
  770. *调整覆盖物在地图上的位置更新覆盖物的显示文字。
  771. *@return 无返回值。
  772. */
  773. TextIconOverlay.prototype._updatePosition = function(){
  774. if (this._domElement && this._position) {
  775. var style = this._domElement.style;
  776. var pixelPosition= this._map.pointToOverlayPixel(this._position);
  777. pixelPosition.x -= Math.ceil(parseInt(style.width) / 2);
  778. pixelPosition.y -= Math.ceil(parseInt(style.height) / 2);
  779. style.left = pixelPosition.x + "px";
  780. style.top = pixelPosition.y + "px";
  781. }
  782. };
  783. /**
  784. * 为该覆盖物的HTML元素构建CSS
  785. * @param {IconStyle} 一个图标的风格。
  786. * @return {String} 构建完成的CSSTEXT。
  787. */
  788. TextIconOverlay.prototype._buildCssText = function(style) {
  789. //根据style来确定一些默认值
  790. var url = style['url'];
  791. var size = style['size'];
  792. var anchor = style['anchor'];
  793. var offset = style['offset'];
  794. var textColor = style['textColor'] || 'black';
  795. var textSize = style['textSize'] || 10;
  796. var csstext = [];
  797. if (T.browser["ie"] < 7) {
  798. csstext.push('filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(' +
  799. 'sizingMethod=scale,src="' + url + '");');
  800. } else {
  801. csstext.push('background-image:url(' + url + ');');
  802. var backgroundPosition = '0 0';
  803. (offset instanceof BMap.Size) && (backgroundPosition = offset.width + 'px' + ' ' + offset.height + 'px');
  804. csstext.push('background-position:' + backgroundPosition + ';');
  805. }
  806. if (size instanceof BMap.Size){
  807. if (anchor instanceof BMap.Size) {
  808. if (anchor.height > 0 && anchor.height < size.height) {
  809. csstext.push('height:' + (size.height - anchor.height) + 'px; padding-top:' + anchor.height + 'px;');
  810. }
  811. if(anchor.width > 0 && anchor.width < size.width){
  812. csstext.push('width:' + (size.width - anchor.width) + 'px; padding-left:' + anchor.width + 'px;');
  813. }
  814. } else {
  815. csstext.push('height:' + size.height + 'px; line-height:' + size.height + 'px;');
  816. csstext.push('width:' + size.width + 'px; text-align:center;');
  817. }
  818. }
  819. csstext.push('cursor:pointer; color:' + textColor + '; position:absolute; font-size:' +
  820. textSize + 'px; font-family:Arial,sans-serif; font-weight:bold');
  821. return csstext.join('');
  822. };
  823. /**
  824. * 当鼠标点击该覆盖物时会触发该事件
  825. * @name TextIconOverlay#click
  826. * @event
  827. * @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
  828. * <br />"<b>type</b> : {String} 事件类型
  829. * <br />"<b>target</b>:{BMapLib.TextIconOverlay} 事件目标
  830. *
  831. */
  832. /**
  833. * 当鼠标进入该覆盖物区域时会触发该事件
  834. * @name TextIconOverlay#mouseover
  835. * @event
  836. * @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
  837. * <br />"<b>type</b> : {String} 事件类型
  838. * <br />"<b>target</b>:{BMapLib.TextIconOverlay} 事件目标
  839. * <br />"<b>point</b> : {BMap.Point} 最新添加上的节点BMap.Point对象
  840. * <br />"<b>pixel</b>:{BMap.pixel} 最新添加上的节点BMap.Pixel对象
  841. *
  842. * @example <b>参考示例:</b><br />
  843. * myTextIconOverlay.addEventListener("mouseover", function(e) { alert(e.point); });
  844. */
  845. /**
  846. * 当鼠标离开该覆盖物区域时会触发该事件
  847. * @name TextIconOverlay#mouseout
  848. * @event
  849. * @param {Event Object} e 回调函数会返回event参数,包括以下返回值:
  850. * <br />"<b>type</b> : {String} 事件类型
  851. * <br />"<b>target</b>:{BMapLib.TextIconOverlay} 事件目标
  852. * <br />"<b>point</b> : {BMap.Point} 最新添加上的节点BMap.Point对象
  853. * <br />"<b>pixel</b>:{BMap.pixel} 最新添加上的节点BMap.Pixel对象
  854. *
  855. * @example <b>参考示例:</b><br />
  856. * myTextIconOverlay.addEventListener("mouseout", function(e) { alert(e.point); });
  857. */
  858. /**
  859. * 为该覆盖物绑定一系列事件
  860. * 当前支持click mouseover mouseout
  861. * @return 无返回值。
  862. */
  863. TextIconOverlay.prototype._bind = function(){
  864. if (!this._domElement){
  865. return;
  866. }
  867. var me = this;
  868. var map = this._map;
  869. var BaseEvent = T.lang.Event;
  870. function eventExtend(e, be){
  871. var elem = e.srcElement || e.target;
  872. var x = e.clientX || e.pageX;
  873. var y = e.clientY || e.pageY;
  874. if (e && be && x && y && elem){
  875. var offset = T.dom.getPosition(map.getContainer());
  876. be.pixel = new BMap.Pixel(x - offset.left, y - offset.top);
  877. be.point = map.pixelToPoint(be.pixel);
  878. }
  879. return be;
  880. }//给事件参数增加pixel和point两个值
  881. T.event.on(this._domElement,"mouseover", function(e){
  882. me.dispatchEvent(eventExtend(e, new BaseEvent("onmouseover")));
  883. });
  884. T.event.on(this._domElement,"mouseout", function(e){
  885. me.dispatchEvent(eventExtend(e, new BaseEvent("onmouseout")));
  886. });
  887. T.event.on(this._domElement,"click", function(e){
  888. me.dispatchEvent(eventExtend(e, new BaseEvent("onclick")));
  889. });
  890. };
  891. return TextIconOverlay;
  892. });
  893. });
  894. var TextIconOverlay = (index$1 && typeof index$1 === 'object' && 'default' in index$1 ? index$1['default'] : index$1);
  895. /**
  896. * 获取一个扩展的视图范围,把上下左右都扩大一样的像素值。
  897. * @param {Map} map BMap.Map的实例化对象
  898. * @param {BMap.Bounds} bounds BMap.Bounds的实例化对象
  899. * @param {Number} gridSize 要扩大的像素值
  900. *
  901. * @return {BMap.Bounds} 返回扩大后的视图范围。
  902. */
  903. var getExtendedBounds = function(map, bounds, gridSize){
  904. bounds = cutBoundsInRange(bounds);
  905. var pixelNE = map.pointToPixel(bounds.getNorthEast());
  906. var pixelSW = map.pointToPixel(bounds.getSouthWest());
  907. pixelNE.x += gridSize;
  908. pixelNE.y -= gridSize;
  909. pixelSW.x -= gridSize;
  910. pixelSW.y += gridSize;
  911. var newNE = map.pixelToPoint(pixelNE);
  912. var newSW = map.pixelToPoint(pixelSW);
  913. return new BMap.Bounds(newSW, newNE);
  914. };
  915. /**
  916. * 按照百度地图支持的世界范围对bounds进行边界处理
  917. * @param {BMap.Bounds} bounds BMap.Bounds的实例化对象
  918. *
  919. * @return {BMap.Bounds} 返回不越界的视图范围
  920. */
  921. var cutBoundsInRange = function (bounds) {
  922. var maxX = getRange(bounds.getNorthEast().lng, -180, 180);
  923. var minX = getRange(bounds.getSouthWest().lng, -180, 180);
  924. var maxY = getRange(bounds.getNorthEast().lat, -74, 74);
  925. var minY = getRange(bounds.getSouthWest().lat, -74, 74);
  926. return new BMap.Bounds(new BMap.Point(minX, minY), new BMap.Point(maxX, maxY));
  927. };
  928. /**
  929. * 对单个值进行边界处理。
  930. * @param {Number} i 要处理的数值
  931. * @param {Number} min 下边界值
  932. * @param {Number} max 上边界值
  933. *
  934. * @return {Number} 返回不越界的数值
  935. */
  936. var getRange = function (i, mix, max) {
  937. mix && (i = Math.max(i, mix));
  938. max && (i = Math.min(i, max));
  939. return i;
  940. };
  941. /**
  942. * 判断给定的对象是否为数组
  943. * @param {Object} source 要测试的对象
  944. *
  945. * @return {Boolean} 如果是数组返回true,否则返回false
  946. */
  947. var isArray = function (source) {
  948. return '[object Array]' === Object.prototype.toString.call(source);
  949. };
  950. /**
  951. * 返回item在source中的索引位置
  952. * @param {Object} item 要测试的对象
  953. * @param {Array} source 数组
  954. *
  955. * @return {Number} 如果在数组内,返回索引,否则返回-1
  956. */
  957. var indexOf = function(item, source){
  958. var index = -1;
  959. if(isArray(source)){
  960. if (source.indexOf) {
  961. index = source.indexOf(item);
  962. } else {
  963. for (var i = 0, m; m = source[i]; i++) {
  964. if (m === item) {
  965. index = i;
  966. break;
  967. }
  968. }
  969. }
  970. }
  971. return index;
  972. };
  973. /**
  974. *@exports MarkerClusterer as BMapLib.MarkerClusterer
  975. */
  976. /**
  977. * MarkerClusterer
  978. * @class 用来解决加载大量点要素到地图上产生覆盖现象的问题,并提高性能
  979. * @constructor
  980. * @param {Map} map 地图的一个实例。
  981. * @param {Json Object} options 可选参数,可选项包括:<br />
  982. * markers {Array<Marker>} 要聚合的标记数组<br />
  983. * girdSize {Number} 聚合计算时网格的像素大小,默认60<br />
  984. * maxZoom {Number} 最大的聚合级别,大于该级别就不进行相应的聚合<br />
  985. * minClusterSize {Number} 最小的聚合数量,小于该数量的不能成为一个聚合,默认为2<br />
  986. * isAverangeCenter {Boolean} 聚合点的落脚位置是否是所有聚合在内点的平均值,默认为否,落脚在聚合内的第一个点<br />
  987. * styles {Array<IconStyle>} 自定义聚合后的图标风格,请参考TextIconOverlay类<br />
  988. */
  989. var MarkerClusterer = function(map, options){
  990. try {
  991. BMap;
  992. } catch (e) {
  993. throw Error('Baidu Map JS API is not ready yet!');
  994. }
  995. if (!map){
  996. return;
  997. }
  998. this._map = map;
  999. this._markers = [];
  1000. this._clusters = [];
  1001. var opts = options || {};
  1002. this._gridSize = opts["gridSize"] || 60;
  1003. this._maxZoom = opts["maxZoom"] || 18;
  1004. this._minClusterSize = opts["minClusterSize"] || 2;
  1005. this._isAverageCenter = false;
  1006. if (opts['isAverageCenter'] != undefined) {
  1007. this._isAverageCenter = opts['isAverageCenter'];
  1008. }
  1009. this._styles = opts["styles"] || [];
  1010. var that = this;
  1011. this._map.addEventListener("zoomend",function(){
  1012. that._redraw();
  1013. });
  1014. this._map.addEventListener("moveend",function(){
  1015. that._redraw();
  1016. });
  1017. var mkrs = opts["markers"];
  1018. isArray(mkrs) && this.addMarkers(mkrs);
  1019. };
  1020. /**
  1021. * 添加要聚合的标记数组。
  1022. * @param {Array<Marker>} markers 要聚合的标记数组
  1023. *
  1024. * @return 无返回值。
  1025. */
  1026. MarkerClusterer.prototype.addMarkers = function(markers){
  1027. if (!markers.length) {
  1028. return
  1029. }
  1030. for(var i = 0, len = markers.length; i <len ; i++){
  1031. this._pushMarkerTo(markers[i]);
  1032. }
  1033. this._createClusters();
  1034. };
  1035. /**
  1036. * 把一个标记添加到要聚合的标记数组中
  1037. * @param {BMap.Marker} marker 要添加的标记
  1038. *
  1039. * @return 无返回值。
  1040. */
  1041. MarkerClusterer.prototype._pushMarkerTo = function(marker){
  1042. var index = indexOf(marker, this._markers);
  1043. if(index === -1){
  1044. marker.isInCluster = false;
  1045. this._markers.push(marker);//Marker拖放后enableDragging不做变化,忽略
  1046. }
  1047. };
  1048. /**
  1049. * 添加一个聚合的标记。
  1050. * @param {BMap.Marker} marker 要聚合的单个标记。
  1051. * @return 无返回值。
  1052. */
  1053. MarkerClusterer.prototype.addMarker = function(marker) {
  1054. this._pushMarkerTo(marker);
  1055. this._createClusters();
  1056. };
  1057. /**
  1058. * 根据所给定的标记,创建聚合点
  1059. * @return 无返回值
  1060. */
  1061. MarkerClusterer.prototype._createClusters = function(){
  1062. var mapBounds = this._map.getBounds();
  1063. if (!mapBounds.getCenter()) {
  1064. return
  1065. }
  1066. var extendedBounds = getExtendedBounds(this._map, mapBounds, this._gridSize);
  1067. for(var i = 0, marker; marker = this._markers[i]; i++){
  1068. if(!marker.isInCluster && extendedBounds.containsPoint(marker.getPosition()) ){
  1069. this._addToClosestCluster(marker);
  1070. }
  1071. }
  1072. };
  1073. /**
  1074. * 根据标记的位置,把它添加到最近的聚合中
  1075. * @param {BMap.Marker} marker 要进行聚合的单个标记
  1076. *
  1077. * @return 无返回值。
  1078. */
  1079. MarkerClusterer.prototype._addToClosestCluster = function (marker){
  1080. var distance = 4000000;
  1081. var clusterToAddTo = null;
  1082. var position = marker.getPosition();
  1083. for(var i = 0, cluster; cluster = this._clusters[i]; i++){
  1084. var center = cluster.getCenter();
  1085. if(center){
  1086. var d = this._map.getDistance(center, marker.getPosition());
  1087. if(d < distance){
  1088. distance = d;
  1089. clusterToAddTo = cluster;
  1090. }
  1091. }
  1092. }
  1093. if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)){
  1094. clusterToAddTo.addMarker(marker);
  1095. } else {
  1096. var cluster = new Cluster(this);
  1097. cluster.addMarker(marker);
  1098. this._clusters.push(cluster);
  1099. }
  1100. };
  1101. /**
  1102. * 清除上一次的聚合的结果
  1103. * @return 无返回值。
  1104. */
  1105. MarkerClusterer.prototype._clearLastClusters = function(){
  1106. for(var i = 0, cluster; cluster = this._clusters[i]; i++){
  1107. cluster.remove();
  1108. }
  1109. this._clusters = [];//置空Cluster数组
  1110. this._removeMarkersFromCluster();//把Marker的cluster标记设为false
  1111. };
  1112. /**
  1113. * 清除某个聚合中的所有标记
  1114. * @return 无返回值
  1115. */
  1116. MarkerClusterer.prototype._removeMarkersFromCluster = function(){
  1117. for(var i = 0, marker; marker = this._markers[i]; i++){
  1118. marker.isInCluster = false;
  1119. }
  1120. };
  1121. /**
  1122. * 把所有的标记从地图上清除
  1123. * @return 无返回值
  1124. */
  1125. MarkerClusterer.prototype._removeMarkersFromMap = function(){
  1126. for(var i = 0, marker; marker = this._markers[i]; i++){
  1127. marker.isInCluster = false;
  1128. var label = marker.getLabel();
  1129. this._map.removeOverlay(marker);
  1130. marker.setLabel(label);
  1131. }
  1132. };
  1133. /**
  1134. * 删除单个标记
  1135. * @param {BMap.Marker} marker 需要被删除的marker
  1136. *
  1137. * @return {Boolean} 删除成功返回true,否则返回false
  1138. */
  1139. MarkerClusterer.prototype._removeMarker = function(marker) {
  1140. var index = indexOf(marker, this._markers);
  1141. if (index === -1) {
  1142. return false;
  1143. }
  1144. this._map.removeOverlay(marker);
  1145. this._markers.splice(index, 1);
  1146. return true;
  1147. };
  1148. /**
  1149. * 删除单个标记
  1150. * @param {BMap.Marker} marker 需要被删除的marker
  1151. *
  1152. * @return {Boolean} 删除成功返回true,否则返回false
  1153. */
  1154. MarkerClusterer.prototype.removeMarker = function(marker) {
  1155. var success = this._removeMarker(marker);
  1156. if (success) {
  1157. this._clearLastClusters();
  1158. this._createClusters();
  1159. }
  1160. return success;
  1161. };
  1162. /**
  1163. * 删除一组标记
  1164. * @param {Array<BMap.Marker>} markers 需要被删除的marker数组
  1165. *
  1166. * @return {Boolean} 删除成功返回true,否则返回false
  1167. */
  1168. MarkerClusterer.prototype.removeMarkers = function(markers) {
  1169. var success = false;
  1170. for (var i = 0; i < markers.length; i++) {
  1171. var r = this._removeMarker(markers[i]);
  1172. success = success || r;
  1173. }
  1174. if (success) {
  1175. this._clearLastClusters();
  1176. this._createClusters();
  1177. }
  1178. return success;
  1179. };
  1180. /**
  1181. * 从地图上彻底清除所有的标记
  1182. * @return 无返回值
  1183. */
  1184. MarkerClusterer.prototype.clearMarkers = function() {
  1185. this._clearLastClusters();
  1186. this._removeMarkersFromMap();
  1187. this._markers = [];
  1188. };
  1189. /**
  1190. * 重新生成,比如改变了属性等
  1191. * @return 无返回值
  1192. */
  1193. MarkerClusterer.prototype._redraw = function () {
  1194. this._clearLastClusters();
  1195. this._createClusters();
  1196. };
  1197. /**
  1198. * 获取网格大小
  1199. * @return {Number} 网格大小
  1200. */
  1201. MarkerClusterer.prototype.getGridSize = function() {
  1202. return this._gridSize;
  1203. };
  1204. /**
  1205. * 设置网格大小
  1206. * @param {Number} size 网格大小
  1207. * @return 无返回值
  1208. */
  1209. MarkerClusterer.prototype.setGridSize = function(size) {
  1210. this._gridSize = size;
  1211. this._redraw();
  1212. };
  1213. /**
  1214. * 获取聚合的最大缩放级别。
  1215. * @return {Number} 聚合的最大缩放级别。
  1216. */
  1217. MarkerClusterer.prototype.getMaxZoom = function() {
  1218. return this._maxZoom;
  1219. };
  1220. /**
  1221. * 设置聚合的最大缩放级别
  1222. * @param {Number} maxZoom 聚合的最大缩放级别
  1223. * @return 无返回值
  1224. */
  1225. MarkerClusterer.prototype.setMaxZoom = function(maxZoom) {
  1226. this._maxZoom = maxZoom;
  1227. this._redraw();
  1228. };
  1229. /**
  1230. * 获取聚合的样式风格集合
  1231. * @return {Array<IconStyle>} 聚合的样式风格集合
  1232. */
  1233. MarkerClusterer.prototype.getStyles = function() {
  1234. return this._styles;
  1235. };
  1236. /**
  1237. * 设置聚合的样式风格集合
  1238. * @param {Array<IconStyle>} styles 样式风格数组
  1239. * @return 无返回值
  1240. */
  1241. MarkerClusterer.prototype.setStyles = function(styles) {
  1242. this._styles = styles;
  1243. this._redraw();
  1244. };
  1245. /**
  1246. * 获取单个聚合的最小数量。
  1247. * @return {Number} 单个聚合的最小数量。
  1248. */
  1249. MarkerClusterer.prototype.getMinClusterSize = function() {
  1250. return this._minClusterSize;
  1251. };
  1252. /**
  1253. * 设置单个聚合的最小数量。
  1254. * @param {Number} size 单个聚合的最小数量。
  1255. * @return 无返回值。
  1256. */
  1257. MarkerClusterer.prototype.setMinClusterSize = function(size) {
  1258. this._minClusterSize = size;
  1259. this._redraw();
  1260. };
  1261. /**
  1262. * 获取单个聚合的落脚点是否是聚合内所有标记的平均中心。
  1263. * @return {Boolean} true或false。
  1264. */
  1265. MarkerClusterer.prototype.isAverageCenter = function() {
  1266. return this._isAverageCenter;
  1267. };
  1268. /**
  1269. * 获取聚合的Map实例。
  1270. * @return {Map} Map的示例。
  1271. */
  1272. MarkerClusterer.prototype.getMap = function() {
  1273. return this._map;
  1274. };
  1275. /**
  1276. * 获取所有的标记数组。
  1277. * @return {Array<Marker>} 标记数组。
  1278. */
  1279. MarkerClusterer.prototype.getMarkers = function() {
  1280. return this._markers;
  1281. };
  1282. /**
  1283. * 获取聚合的总数量。
  1284. * @return {Number} 聚合的总数量。
  1285. */
  1286. MarkerClusterer.prototype.getClustersCount = function() {
  1287. var count = 0;
  1288. for(var i = 0, cluster; cluster = this._clusters[i]; i++){
  1289. cluster.isReal() && count++;
  1290. }
  1291. return count;
  1292. };
  1293. /**
  1294. * @ignore
  1295. * Cluster
  1296. * @class 表示一个聚合对象,该聚合,包含有N个标记,这N个标记组成的范围,并有予以显示在Map上的TextIconOverlay等。
  1297. * @constructor
  1298. * @param {MarkerClusterer} markerClusterer 一个标记聚合器示例。
  1299. */
  1300. function Cluster(markerClusterer){
  1301. this._markerClusterer = markerClusterer;
  1302. this._map = markerClusterer.getMap();
  1303. this._minClusterSize = markerClusterer.getMinClusterSize();
  1304. this._isAverageCenter = markerClusterer.isAverageCenter();
  1305. this._center = null;//落脚位置
  1306. this._markers = [];//这个Cluster中所包含的markers
  1307. this._gridBounds = null;//以中心点为准,向四边扩大gridSize个像素的范围,也即网格范围
  1308. this._isReal = false; //真的是个聚合
  1309. this._clusterMarker = new TextIconOverlay(this._center, this._markers.length, {"styles":this._markerClusterer.getStyles()});
  1310. //this._map.addOverlay(this._clusterMarker);
  1311. }
  1312. /**
  1313. * 向该聚合添加一个标记。
  1314. * @param {Marker} marker 要添加的标记。
  1315. * @return 无返回值。
  1316. */
  1317. Cluster.prototype.addMarker = function(marker){
  1318. if(this.isMarkerInCluster(marker)){
  1319. return false;
  1320. }//也可用marker.isInCluster判断,外面判断OK,这里基本不会命中
  1321. if (!this._center){
  1322. this._center = marker.getPosition();
  1323. this.updateGridBounds();//
  1324. } else {
  1325. if(this._isAverageCenter){
  1326. var l = this._markers.length + 1;
  1327. var lat = (this._center.lat * (l - 1) + marker.getPosition().lat) / l;
  1328. var lng = (this._center.lng * (l - 1) + marker.getPosition().lng) / l;
  1329. this._center = new BMap.Point(lng, lat);
  1330. this.updateGridBounds();
  1331. }//计算新的Center
  1332. }
  1333. marker.isInCluster = true;
  1334. this._markers.push(marker);
  1335. var len = this._markers.length;
  1336. if(len < this._minClusterSize ){
  1337. this._map.addOverlay(marker);
  1338. //this.updateClusterMarker();
  1339. return true;
  1340. } else if (len === this._minClusterSize) {
  1341. for (var i = 0; i < len; i++) {
  1342. var label = this._markers[i].getLabel();
  1343. this._markers[i].getMap() && this._map.removeOverlay(this._markers[i]);
  1344. this._markers[i].setLabel(label);
  1345. }
  1346. }
  1347. this._map.addOverlay(this._clusterMarker);
  1348. this._isReal = true;
  1349. this.updateClusterMarker();
  1350. return true;
  1351. };
  1352. /**
  1353. * 判断一个标记是否在该聚合中。
  1354. * @param {Marker} marker 要判断的标记。
  1355. * @return {Boolean} true或false。
  1356. */
  1357. Cluster.prototype.isMarkerInCluster= function(marker){
  1358. if (this._markers.indexOf) {
  1359. return this._markers.indexOf(marker) != -1;
  1360. } else {
  1361. for (var i = 0, m; m = this._markers[i]; i++) {
  1362. if (m === marker) {
  1363. return true;
  1364. }
  1365. }
  1366. }
  1367. return false;
  1368. };
  1369. /**
  1370. * 判断一个标记是否在该聚合网格范围中。
  1371. * @param {Marker} marker 要判断的标记。
  1372. * @return {Boolean} true或false。
  1373. */
  1374. Cluster.prototype.isMarkerInClusterBounds = function(marker) {
  1375. return this._gridBounds.containsPoint(marker.getPosition());
  1376. };
  1377. Cluster.prototype.isReal = function(marker) {
  1378. return this._isReal;
  1379. };
  1380. /**
  1381. * 更新该聚合的网格范围。
  1382. * @return 无返回值。
  1383. */
  1384. Cluster.prototype.updateGridBounds = function() {
  1385. var bounds = new BMap.Bounds(this._center, this._center);
  1386. this._gridBounds = getExtendedBounds(this._map, bounds, this._markerClusterer.getGridSize());
  1387. };
  1388. /**
  1389. * 更新该聚合的显示样式,也即TextIconOverlay。
  1390. * @return 无返回值。
  1391. */
  1392. Cluster.prototype.updateClusterMarker = function () {
  1393. if (this._map.getZoom() > this._markerClusterer.getMaxZoom()) {
  1394. this._clusterMarker && this._map.removeOverlay(this._clusterMarker);
  1395. for (var i = 0, marker; marker = this._markers[i]; i++) {
  1396. this._map.addOverlay(marker);
  1397. }
  1398. return;
  1399. }
  1400. if (this._markers.length < this._minClusterSize) {
  1401. this._clusterMarker.hide();
  1402. return;
  1403. }
  1404. this._clusterMarker.setPosition(this._center);
  1405. this._clusterMarker.setText(this._markers.length);
  1406. this._clusterMarker.addEventListener && !this._clusterMarker._hasClickEvent && this._clusterMarker.addEventListener("click", function(event){
  1407. this._clusterMarker._hasClickEvent = true;
  1408. this._markers && this._map.setViewport(this.getBounds());
  1409. }.bind(this));
  1410. };
  1411. /**
  1412. * 删除该聚合。
  1413. * @return 无返回值。
  1414. */
  1415. Cluster.prototype.remove = function(){
  1416. for (var i = 0, m; m = this._markers[i]; i++) {
  1417. var label = this._markers[i].getLabel();
  1418. this._markers[i].getMap() && this._map.removeOverlay(this._markers[i]);
  1419. this._markers[i].setLabel(label);
  1420. }//清除散的标记点
  1421. this._map.removeOverlay(this._clusterMarker);
  1422. this._markers.length = 0;
  1423. delete this._markers;
  1424. };
  1425. /**
  1426. * 获取该聚合所包含的所有标记的最小外接矩形的范围。
  1427. * @return {BMap.Bounds} 计算出的范围。
  1428. */
  1429. Cluster.prototype.getBounds = function() {
  1430. var bounds = new BMap.Bounds(this._center,this._center);
  1431. for (var i = 0, marker; marker = this._markers[i]; i++) {
  1432. bounds.extend(marker.getPosition());
  1433. }
  1434. return bounds;
  1435. };
  1436. /**
  1437. * 获取该聚合的落脚点。
  1438. * @return {BMap.Point} 该聚合的落脚点。
  1439. */
  1440. Cluster.prototype.getCenter = function() {
  1441. return this._center;
  1442. };
  1443. return MarkerClusterer;
  1444. })));