mxn.core.js 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755
  1. (function(){
  2. /**
  3. * @exports mxn.util.$m as $m
  4. */
  5. var $m = mxn.util.$m;
  6. /**
  7. * Initialise our provider. This function should only be called
  8. * from within mapstraction code, not exposed as part of the API.
  9. * @private
  10. */
  11. var init = function() {
  12. this.invoker.go('init', [ this.currentElement, this.api ]);
  13. this.applyOptions();
  14. };
  15. /**
  16. * Mapstraction instantiates a map with some API choice into the HTML element given
  17. * @name mxn.Mapstraction
  18. * @constructor
  19. * @param {String} element The HTML element to replace with a map
  20. * @param {String} api The API to use, one of 'yahoo', 'microsoft', 'openstreetmap', 'multimap', 'map24', 'openlayers', 'mapquest'. If omitted, first loaded provider implementation is used.
  21. * @param {Bool} debug optional parameter to turn on debug support - this uses alert panels for unsupported actions
  22. * @exports Mapstraction as mxn.Mapstraction
  23. */
  24. var Mapstraction = mxn.Mapstraction = function(element, api, debug) {
  25. if (!api){
  26. api = mxn.util.getAvailableProviders()[0];
  27. }
  28. this.api = api;
  29. this.maps = {};
  30. this.currentElement = $m(element);
  31. this.eventListeners = [];
  32. this.markers = [];
  33. this.layers = [];
  34. this.polylines = [];
  35. this.images = [];
  36. this.controls = [];
  37. this.loaded = {};
  38. this.onload = {};
  39. this.element = element;
  40. // option defaults
  41. this.options = {
  42. enableScrollWheelZoom: false,
  43. enableDragging: true
  44. };
  45. this.addControlsArgs = {};
  46. // set up our invoker for calling API methods
  47. this.invoker = new mxn.Invoker(this, 'Mapstraction', function(){ return this.api; });
  48. // Adding our events
  49. mxn.addEvents(this, [
  50. /**
  51. * Map has loaded
  52. * @name mxn.Mapstraction#load
  53. * @event
  54. */
  55. 'load',
  56. /**
  57. * Map is clicked {location: LatLonPoint}
  58. * @name mxn.Mapstraction#click
  59. * @event
  60. */
  61. 'click',
  62. /**
  63. * Map is panned
  64. * @name mxn.Mapstraction#endPan
  65. * @event
  66. */
  67. 'endPan',
  68. /**
  69. * Zoom is changed
  70. * @name mxn.Mapstraction#changeZoom
  71. * @event
  72. */
  73. 'changeZoom',
  74. /**
  75. * Marker is removed {marker: Marker}
  76. * @name mxn.Mapstraction#markerAdded
  77. * @event
  78. */
  79. 'markerAdded',
  80. /**
  81. * Marker is removed {marker: Marker}
  82. * @name mxn.Mapstraction#markerRemoved
  83. * @event
  84. */
  85. 'markerRemoved',
  86. /**
  87. * Polyline is added {polyline: Polyline}
  88. * @name mxn.Mapstraction#polylineAdded
  89. * @event
  90. */
  91. 'polylineAdded',
  92. /**
  93. * Polyline is removed {polyline: Polyline}
  94. * @name mxn.Mapstraction#polylineRemoved
  95. * @event
  96. */
  97. 'polylineRemoved'
  98. ]);
  99. // finally initialize our proper API map
  100. init.apply(this);
  101. };
  102. // Map type constants
  103. Mapstraction.ROAD = 1;
  104. Mapstraction.SATELLITE = 2;
  105. Mapstraction.HYBRID = 3;
  106. // methods that have no implementation in mapstraction core
  107. mxn.addProxyMethods(Mapstraction, [
  108. /**
  109. * Adds a large map panning control and zoom buttons to the map
  110. * @name mxn.Mapstraction#addLargeControls
  111. * @function
  112. */
  113. 'addLargeControls',
  114. /**
  115. * Adds a map type control to the map (streets, aerial imagery etc)
  116. * @name mxn.Mapstraction#addMapTypeControls
  117. * @function
  118. */
  119. 'addMapTypeControls',
  120. /**
  121. * Adds a GeoRSS or KML overlay to the map
  122. * some flavors of GeoRSS and KML are not supported by some of the Map providers
  123. * @name mxn.Mapstraction#addOverlay
  124. * @function
  125. * @param {String} url GeoRSS or KML feed URL
  126. * @param {Boolean} autoCenterAndZoom Set true to auto center and zoom after the feed is loaded
  127. */
  128. 'addOverlay',
  129. /**
  130. * Adds a small map panning control and zoom buttons to the map
  131. * @name mxn.Mapstraction#addSmallControls
  132. * @function
  133. */
  134. 'addSmallControls',
  135. /**
  136. * Applies the current option settings
  137. * @name mxn.Mapstraction#applyOptions
  138. * @function
  139. */
  140. 'applyOptions',
  141. /**
  142. * Gets the BoundingBox of the map
  143. * @name mxn.Mapstraction#getBounds
  144. * @function
  145. * @returns {BoundingBox} The bounding box for the current map state
  146. */
  147. 'getBounds',
  148. /**
  149. * Gets the central point of the map
  150. * @name mxn.Mapstraction#getCenter
  151. * @function
  152. * @returns {LatLonPoint} The center point of the map
  153. */
  154. 'getCenter',
  155. /**
  156. * Gets the imagery type for the map.
  157. * The type can be one of:
  158. * mxn.Mapstraction.ROAD
  159. * mxn.Mapstraction.SATELLITE
  160. * mxn.Mapstraction.HYBRID
  161. * @name mxn.Mapstraction#getMapType
  162. * @function
  163. * @returns {Number}
  164. */
  165. 'getMapType',
  166. /**
  167. * Returns a ratio to turn distance into pixels based on current projection
  168. * @name mxn.Mapstraction#getPixelRatio
  169. * @function
  170. * @returns {Float} ratio
  171. */
  172. 'getPixelRatio',
  173. /**
  174. * Returns the zoom level of the map
  175. * @name mxn.Mapstraction#getZoom
  176. * @function
  177. * @returns {Integer} The zoom level of the map
  178. */
  179. 'getZoom',
  180. /**
  181. * Returns the best zoom level for bounds given
  182. * @name mxn.Mapstraction#getZoomLevelForBoundingBox
  183. * @function
  184. * @param {BoundingBox} bbox The bounds to fit
  185. * @returns {Integer} The closest zoom level that contains the bounding box
  186. */
  187. 'getZoomLevelForBoundingBox',
  188. /**
  189. * Displays the coordinates of the cursor in the HTML element
  190. * @name mxn.Mapstraction#mousePosition
  191. * @function
  192. * @param {String} element ID of the HTML element to display the coordinates in
  193. */
  194. 'mousePosition',
  195. /**
  196. * Resize the current map to the specified width and height
  197. * (since it is actually on a child div of the mapElement passed
  198. * as argument to the Mapstraction constructor, the resizing of this
  199. * mapElement may have no effect on the size of the actual map)
  200. * @name mxn.Mapstraction#resizeTo
  201. * @function
  202. * @param {Integer} width The width the map should be.
  203. * @param {Integer} height The width the map should be.
  204. */
  205. 'resizeTo',
  206. /**
  207. * Sets the map to the appropriate location and zoom for a given BoundingBox
  208. * @name mxn.Mapstraction#setBounds
  209. * @function
  210. * @param {BoundingBox} bounds The bounding box you want the map to show
  211. */
  212. 'setBounds',
  213. /**
  214. * setCenter sets the central point of the map
  215. * @name mxn.Mapstraction#setCenter
  216. * @function
  217. * @param {LatLonPoint} point The point at which to center the map
  218. * @param {Object} options Optional parameters
  219. * @param {Boolean} options.pan Whether the map should move to the locations using a pan or just jump straight there
  220. */
  221. 'setCenter',
  222. /**
  223. * Centers the map to some place and zoom level
  224. * @name mxn.Mapstraction#setCenterAndZoom
  225. * @function
  226. * @param {LatLonPoint} point Where the center of the map should be
  227. * @param {Integer} zoom The zoom level where 0 is all the way out.
  228. */
  229. 'setCenterAndZoom',
  230. /**
  231. * Sets the imagery type for the map
  232. * The type can be one of:
  233. * mxn.Mapstraction.ROAD
  234. * mxn.Mapstraction.SATELLITE
  235. * mxn.Mapstraction.HYBRID
  236. * @name mxn.Mapstraction#setMapType
  237. * @function
  238. * @param {Number} type
  239. */
  240. 'setMapType',
  241. /**
  242. * Sets the zoom level for the map
  243. * MS doesn't seem to do zoom=0, and Gg's sat goes closer than it's maps, and MS's sat goes closer than Y!'s
  244. * TODO: Mapstraction.prototype.getZoomLevels or something.
  245. * @name mxn.Mapstraction#setZoom
  246. * @function
  247. * @param {Number} zoom The (native to the map) level zoom the map to.
  248. */
  249. 'setZoom',
  250. /**
  251. * Turns a Tile Layer on or off
  252. * @name mxn.Mapstraction#toggleTileLayer
  253. * @function
  254. * @param {tile_url} url of the tile layer that was created.
  255. */
  256. 'toggleTileLayer'
  257. ]);
  258. /**
  259. * Sets the current options to those specified in oOpts and applies them
  260. * @param {Object} oOpts Hash of options to set
  261. */
  262. Mapstraction.prototype.setOptions = function(oOpts){
  263. mxn.util.merge(this.options, oOpts);
  264. this.applyOptions();
  265. };
  266. /**
  267. * Sets an option and applies it.
  268. * @param {String} sOptName Option name
  269. * @param vVal Option value
  270. */
  271. Mapstraction.prototype.setOption = function(sOptName, vVal){
  272. this.options[sOptName] = vVal;
  273. this.applyOptions();
  274. };
  275. /**
  276. * Enable scroll wheel zooming
  277. * @deprecated Use setOption instead.
  278. */
  279. Mapstraction.prototype.enableScrollWheelZoom = function() {
  280. this.setOption('enableScrollWheelZoom', true);
  281. };
  282. /**
  283. * Enable/disable dragging of the map
  284. * @param {Boolean} on
  285. * @deprecated Use setOption instead.
  286. */
  287. Mapstraction.prototype.dragging = function(on) {
  288. this.setOption('enableDragging', on);
  289. };
  290. /**
  291. * Change the current api on the fly
  292. * @param {String} api The API to swap to
  293. * @param element
  294. */
  295. Mapstraction.prototype.swap = function(element,api) {
  296. if (this.api === api) {
  297. return;
  298. }
  299. var center = this.getCenter();
  300. var zoom = this.getZoom();
  301. this.currentElement.style.visibility = 'hidden';
  302. this.currentElement.style.display = 'none';
  303. this.currentElement = $m(element);
  304. this.currentElement.style.visibility = 'visible';
  305. this.currentElement.style.display = 'block';
  306. this.api = api;
  307. if (this.maps[this.api] === undefined) {
  308. init.apply(this);
  309. this.setCenterAndZoom(center,zoom);
  310. for (var i = 0; i < this.markers.length; i++) {
  311. this.addMarker(this.markers[i], true);
  312. }
  313. for (var j = 0; j < this.polylines.length; j++) {
  314. this.addPolyline( this.polylines[j], true);
  315. }
  316. }
  317. else {
  318. //sync the view
  319. this.setCenterAndZoom(center,zoom);
  320. //TODO synchronize the markers and polylines too
  321. // (any overlays created after api instantiation are not sync'd)
  322. }
  323. this.addControls(this.addControlsArgs);
  324. };
  325. /**
  326. * Returns the loaded state of a Map Provider
  327. * @param {String} api Optional API to query for. If not specified, returns state of the originally created API
  328. */
  329. Mapstraction.prototype.isLoaded = function(api){
  330. if (api === null) {
  331. api = this.api;
  332. }
  333. return this.loaded[api];
  334. };
  335. /**
  336. * Set the debugging on or off - shows alert panels for functions that don't exist in Mapstraction
  337. * @param {Boolean} debug true to turn on debugging, false to turn it off
  338. */
  339. Mapstraction.prototype.setDebug = function(debug){
  340. if(debug !== null) {
  341. this.debug = debug;
  342. }
  343. return this.debug;
  344. };
  345. /////////////////////////
  346. //
  347. // Event Handling
  348. //
  349. // FIXME need to consolidate some of these handlers...
  350. //
  351. ///////////////////////////
  352. // Click handler attached to native API
  353. Mapstraction.prototype.clickHandler = function(lat, lon, me) {
  354. this.callEventListeners('click', {
  355. location: new LatLonPoint(lat, lon)
  356. });
  357. };
  358. // Move and zoom handler attached to native API
  359. Mapstraction.prototype.moveendHandler = function(me) {
  360. this.callEventListeners('moveend', {});
  361. };
  362. /**
  363. * Add a listener for an event.
  364. * @param {String} type Event type to attach listener to
  365. * @param {Function} func Callback function
  366. * @param {Object} caller Callback object
  367. */
  368. Mapstraction.prototype.addEventListener = function() {
  369. var listener = {};
  370. listener.event_type = arguments[0];
  371. listener.callback_function = arguments[1];
  372. // added the calling object so we can retain scope of callback function
  373. if(arguments.length == 3) {
  374. listener.back_compat_mode = false;
  375. listener.callback_object = arguments[2];
  376. }
  377. else {
  378. listener.back_compat_mode = true;
  379. listener.callback_object = null;
  380. }
  381. this.eventListeners.push(listener);
  382. };
  383. /**
  384. * Call listeners for a particular event.
  385. * @param {String} sEventType Call listeners of this event type
  386. * @param {Object} oEventArgs Event args object to pass back to the callback
  387. */
  388. Mapstraction.prototype.callEventListeners = function(sEventType, oEventArgs) {
  389. oEventArgs.source = this;
  390. for(var i = 0; i < this.eventListeners.length; i++) {
  391. var evLi = this.eventListeners[i];
  392. if(evLi.event_type == sEventType) {
  393. // only two cases for this, click and move
  394. if(evLi.back_compat_mode) {
  395. if(evLi.event_type == 'click') {
  396. evLi.callback_function(oEventArgs.location);
  397. }
  398. else {
  399. evLi.callback_function();
  400. }
  401. }
  402. else {
  403. var scope = evLi.callback_object || this;
  404. evLi.callback_function.call(scope, oEventArgs);
  405. }
  406. }
  407. }
  408. };
  409. ////////////////////
  410. //
  411. // map manipulation
  412. //
  413. /////////////////////
  414. /**
  415. * addControls adds controls to the map. You specify which controls to add in
  416. * the associative array that is the only argument.
  417. * addControls can be called multiple time, with different args, to dynamically change controls.
  418. *
  419. * args = {
  420. * pan: true,
  421. * zoom: 'large' || 'small',
  422. * overview: true,
  423. * scale: true,
  424. * map_type: true,
  425. * }
  426. * @param {array} args Which controls to switch on
  427. */
  428. Mapstraction.prototype.addControls = function( args ) {
  429. this.addControlsArgs = args;
  430. this.invoker.go('addControls', arguments);
  431. };
  432. /**
  433. * Adds a marker pin to the map
  434. * @param {Marker} marker The marker to add
  435. * @param {Boolean} old If true, doesn't add this marker to the markers array. Used by the "swap" method
  436. */
  437. Mapstraction.prototype.addMarker = function(marker, old) {
  438. marker.mapstraction = this;
  439. marker.api = this.api;
  440. marker.location.api = this.api;
  441. marker.map = this.maps[this.api];
  442. var propMarker = this.invoker.go('addMarker', arguments);
  443. marker.setChild(propMarker);
  444. if (!old) {
  445. this.markers.push(marker);
  446. }
  447. this.markerAdded.fire({'marker': marker});
  448. };
  449. /**
  450. * addMarkerWithData will addData to the marker, then add it to the map
  451. * @param {Marker} marker The marker to add
  452. * @param {Object} data A data has to add
  453. */
  454. Mapstraction.prototype.addMarkerWithData = function(marker, data) {
  455. marker.addData(data);
  456. this.addMarker(marker);
  457. };
  458. /**
  459. * addPolylineWithData will addData to the polyline, then add it to the map
  460. * @param {Polyline} polyline The polyline to add
  461. * @param {Object} data A data has to add
  462. */
  463. Mapstraction.prototype.addPolylineWithData = function(polyline, data) {
  464. polyline.addData(data);
  465. this.addPolyline(polyline);
  466. };
  467. /**
  468. * removeMarker removes a Marker from the map
  469. * @param {Marker} marker The marker to remove
  470. */
  471. Mapstraction.prototype.removeMarker = function(marker) {
  472. var current_marker;
  473. for(var i = 0; i < this.markers.length; i++){
  474. current_marker = this.markers[i];
  475. if(marker == current_marker) {
  476. this.invoker.go('removeMarker', arguments);
  477. marker.onmap = false;
  478. this.markers.splice(i, 1);
  479. this.markerRemoved.fire({'marker': marker});
  480. break;
  481. }
  482. }
  483. };
  484. /**
  485. * removeAllMarkers removes all the Markers on a map
  486. */
  487. Mapstraction.prototype.removeAllMarkers = function() {
  488. var current_marker;
  489. while(this.markers.length > 0) {
  490. current_marker = this.markers.pop();
  491. this.invoker.go('removeMarker', [current_marker]);
  492. }
  493. };
  494. /**
  495. * Declutter the markers on the map, group together overlapping markers.
  496. * @param {Object} opts Declutter options
  497. */
  498. Mapstraction.prototype.declutterMarkers = function(opts) {
  499. if(this.loaded[this.api] === false) {
  500. var me = this;
  501. this.onload[this.api].push( function() {
  502. me.declutterMarkers(opts);
  503. } );
  504. return;
  505. }
  506. var map = this.maps[this.api];
  507. switch(this.api)
  508. {
  509. // case 'yahoo':
  510. //
  511. // break;
  512. // case 'openstreetmap':
  513. //
  514. // break;
  515. // case 'microsoft':
  516. //
  517. // break;
  518. // case 'openlayers':
  519. //
  520. // break;
  521. case 'multimap':
  522. /*
  523. * Multimap supports quite a lot of decluttering options such as whether
  524. * to use an accurate of fast declutter algorithm and what icon to use to
  525. * represent a cluster. Using all this would mean abstracting all the enums
  526. * etc so we're only implementing the group name function at the moment.
  527. */
  528. map.declutterGroup(opts.groupName);
  529. break;
  530. // case 'mapquest':
  531. //
  532. // break;
  533. // case 'map24':
  534. //
  535. // break;
  536. case ' dummy':
  537. break;
  538. default:
  539. if(this.debug) {
  540. alert(this.api + ' not supported by Mapstraction.declutterMarkers');
  541. }
  542. }
  543. };
  544. /**
  545. * Add a polyline to the map
  546. * @param {Polyline} polyline The Polyline to add to the map
  547. * @param {Boolean} old If true replaces an existing Polyline
  548. */
  549. Mapstraction.prototype.addPolyline = function(polyline, old) {
  550. polyline.api = this.api;
  551. polyline.map = this.maps[this.api];
  552. var propPoly = this.invoker.go('addPolyline', arguments);
  553. polyline.setChild(propPoly);
  554. if(!old) {
  555. this.polylines.push(polyline);
  556. }
  557. this.polylineAdded.fire({'polyline': polyline});
  558. };
  559. // Private remove implementation
  560. var removePolylineImpl = function(polyline) {
  561. this.invoker.go('removePolyline', arguments);
  562. polyline.onmap = false;
  563. this.polylineRemoved.fire({'polyline': polyline});
  564. };
  565. /**
  566. * Remove the polyline from the map
  567. * @param {Polyline} polyline The Polyline to remove from the map
  568. */
  569. Mapstraction.prototype.removePolyline = function(polyline) {
  570. var current_polyline;
  571. for(var i = 0; i < this.polylines.length; i++){
  572. current_polyline = this.polylines[i];
  573. if(polyline == current_polyline) {
  574. this.polylines.splice(i, 1);
  575. removePolylineImpl.call(this, polyline);
  576. break;
  577. }
  578. }
  579. };
  580. /**
  581. * Removes all polylines from the map
  582. */
  583. Mapstraction.prototype.removeAllPolylines = function() {
  584. var current_polyline;
  585. while(this.polylines.length > 0) {
  586. current_polyline = this.polylines.pop();
  587. removePolylineImpl.call(this, current_polyline);
  588. }
  589. };
  590. /**
  591. * autoCenterAndZoom sets the center and zoom of the map to the smallest bounding box
  592. * containing all markers
  593. */
  594. Mapstraction.prototype.autoCenterAndZoom = function() {
  595. var lat_max = -90;
  596. var lat_min = 90;
  597. var lon_max = -180;
  598. var lon_min = 180;
  599. var lat, lon;
  600. var checkMinMax = function(){
  601. if (lat > lat_max) {
  602. lat_max = lat;
  603. }
  604. if (lat < lat_min) {
  605. lat_min = lat;
  606. }
  607. if (lon > lon_max) {
  608. lon_max = lon;
  609. }
  610. if (lon < lon_min) {
  611. lon_min = lon;
  612. }
  613. };
  614. for (var i = 0; i < this.markers.length; i++) {
  615. lat = this.markers[i].location.lat;
  616. lon = this.markers[i].location.lon;
  617. checkMinMax();
  618. }
  619. for(i = 0; i < this.polylines.length; i++) {
  620. for (var j = 0; j < this.polylines[i].points.length; j++) {
  621. lat = this.polylines[i].points[j].lat;
  622. lon = this.polylines[i].points[j].lon;
  623. checkMinMax();
  624. }
  625. }
  626. this.setBounds( new BoundingBox(lat_min, lon_min, lat_max, lon_max) );
  627. };
  628. /**
  629. * centerAndZoomOnPoints sets the center and zoom of the map from an array of points
  630. *
  631. * This is useful if you don't want to have to add markers to the map
  632. */
  633. Mapstraction.prototype.centerAndZoomOnPoints = function(points) {
  634. var bounds = new BoundingBox(points[0].lat,points[0].lon,points[0].lat,points[0].lon);
  635. for (var i=1, len = points.length ; i<len; i++) {
  636. bounds.extend(points[i]);
  637. }
  638. this.setBounds(bounds);
  639. };
  640. /**
  641. * Sets the center and zoom of the map to the smallest bounding box
  642. * containing all visible markers and polylines
  643. * will only include markers and polylines with an attribute of "visible"
  644. */
  645. Mapstraction.prototype.visibleCenterAndZoom = function() {
  646. var lat_max = -90;
  647. var lat_min = 90;
  648. var lon_max = -180;
  649. var lon_min = 180;
  650. var lat, lon;
  651. var checkMinMax = function(){
  652. if (lat > lat_max) {
  653. lat_max = lat;
  654. }
  655. if (lat < lat_min) {
  656. lat_min = lat;
  657. }
  658. if (lon > lon_max) {
  659. lon_max = lon;
  660. }
  661. if (lon < lon_min) {
  662. lon_min = lon;
  663. }
  664. };
  665. for (var i=0; i<this.markers.length; i++) {
  666. if (this.markers[i].getAttribute("visible")) {
  667. lat = this.markers[i].location.lat;
  668. lon = this.markers[i].location.lon;
  669. checkMinMax();
  670. }
  671. }
  672. for (i=0; i<this.polylines.length; i++){
  673. if (this.polylines[i].getAttribute("visible")) {
  674. for (j=0; j<this.polylines[i].points.length; j++) {
  675. lat = this.polylines[i].points[j].lat;
  676. lon = this.polylines[i].points[j].lon;
  677. checkMinMax();
  678. }
  679. }
  680. }
  681. this.setBounds(new BoundingBox(lat_min, lon_min, lat_max, lon_max));
  682. };
  683. /**
  684. * Automatically sets center and zoom level to show all polylines
  685. * Takes into account radious of polyline
  686. * @param {Int} radius
  687. */
  688. Mapstraction.prototype.polylineCenterAndZoom = function(radius) {
  689. var lat_max = -90;
  690. var lat_min = 90;
  691. var lon_max = -180;
  692. var lon_min = 180;
  693. for (var i=0; i < mapstraction.polylines.length; i++)
  694. {
  695. for (var j=0; j<mapstraction.polylines[i].points.length; j++)
  696. {
  697. lat = mapstraction.polylines[i].points[j].lat;
  698. lon = mapstraction.polylines[i].points[j].lon;
  699. latConv = lonConv = radius;
  700. if (radius > 0)
  701. {
  702. latConv = (radius / mapstraction.polylines[i].points[j].latConv());
  703. lonConv = (radius / mapstraction.polylines[i].points[j].lonConv());
  704. }
  705. if ((lat + latConv) > lat_max) {
  706. lat_max = (lat + latConv);
  707. }
  708. if ((lat - latConv) < lat_min) {
  709. lat_min = (lat - latConv);
  710. }
  711. if ((lon + lonConv) > lon_max) {
  712. lon_max = (lon + lonConv);
  713. }
  714. if ((lon - lonConv) < lon_min) {
  715. lon_min = (lon - lonConv);
  716. }
  717. }
  718. }
  719. this.setBounds(new BoundingBox(lat_min, lon_min, lat_max, lon_max));
  720. };
  721. /**
  722. * addImageOverlay layers an georeferenced image over the map
  723. * @param {id} unique DOM identifier
  724. * @param {src} url of image
  725. * @param {opacity} opacity 0-100
  726. * @param {west} west boundary
  727. * @param {south} south boundary
  728. * @param {east} east boundary
  729. * @param {north} north boundary
  730. */
  731. Mapstraction.prototype.addImageOverlay = function(id, src, opacity, west, south, east, north) {
  732. var b = document.createElement("img");
  733. b.style.display = 'block';
  734. b.setAttribute('id',id);
  735. b.setAttribute('src',src);
  736. b.style.position = 'absolute';
  737. b.style.zIndex = 1;
  738. b.setAttribute('west',west);
  739. b.setAttribute('south',south);
  740. b.setAttribute('east',east);
  741. b.setAttribute('north',north);
  742. var oContext = {
  743. imgElm: b
  744. };
  745. this.invoker.go('addImageOverlay', arguments, { context: oContext });
  746. };
  747. Mapstraction.prototype.setImageOpacity = function(id, opacity) {
  748. if (opacity < 0) {
  749. opacity = 0;
  750. }
  751. if (opacity >= 100) {
  752. opacity = 100;
  753. }
  754. var c = opacity / 100;
  755. var d = document.getElementById(id);
  756. if(typeof(d.style.filter)=='string'){
  757. d.style.filter='alpha(opacity:'+opacity+')';
  758. }
  759. if(typeof(d.style.KHTMLOpacity)=='string'){
  760. d.style.KHTMLOpacity=c;
  761. }
  762. if(typeof(d.style.MozOpacity)=='string'){
  763. d.style.MozOpacity=c;
  764. }
  765. if(typeof(d.style.opacity)=='string'){
  766. d.style.opacity=c;
  767. }
  768. };
  769. Mapstraction.prototype.setImagePosition = function(id) {
  770. var imgElement = document.getElementById(id);
  771. var oContext = {
  772. latLng: {
  773. top: imgElement.getAttribute('north'),
  774. left: imgElement.getAttribute('west'),
  775. bottom: imgElement.getAttribute('south'),
  776. right: imgElement.getAttribute('east')
  777. },
  778. pixels: { top: 0, right: 0, bottom: 0, left: 0 }
  779. };
  780. this.invoker.go('setImagePosition', arguments, { context: oContext });
  781. imgElement.style.top = oContext.pixels.top.toString() + 'px';
  782. imgElement.style.left = oContext.pixels.left.toString() + 'px';
  783. imgElement.style.width = (oContext.pixels.right - oContext.pixels.left).toString() + 'px';
  784. imgElement.style.height = (oContext.pixels.bottom - oContext.pixels.top).toString() + 'px';
  785. };
  786. Mapstraction.prototype.addJSON = function(json) {
  787. var features;
  788. if (typeof(json) == "string") {
  789. features = eval('(' + json + ')');
  790. } else {
  791. features = json;
  792. }
  793. features = features.features;
  794. var map = this.maps[this.api];
  795. var html = "";
  796. var item;
  797. var polyline;
  798. var marker;
  799. var markers = [];
  800. if(features.type == "FeatureCollection") {
  801. this.addJSON(features.features);
  802. }
  803. for (var i = 0; i < features.length; i++) {
  804. item = features[i];
  805. switch(item.geometry.type) {
  806. case "Point":
  807. html = "<strong>" + item.title + "</strong><p>" + item.description + "</p>";
  808. marker = new Marker(new LatLonPoint(item.geometry.coordinates[1],item.geometry.coordinates[0]));
  809. markers.push(marker);
  810. this.addMarkerWithData(marker,{
  811. infoBubble : html,
  812. label : item.title,
  813. date : "new Date(\""+item.date+"\")",
  814. iconShadow : item.icon_shadow,
  815. marker : item.id,
  816. iconShadowSize : item.icon_shadow_size,
  817. icon : "http://boston.openguides.org/markers/AQUA.png",
  818. iconSize : item.icon_size,
  819. category : item.source_id,
  820. draggable : false,
  821. hover : false
  822. });
  823. break;
  824. case "Polygon":
  825. var points = [];
  826. polyline = new Polyline(points);
  827. mapstraction.addPolylineWithData(polyline,{
  828. fillColor : item.poly_color,
  829. date : "new Date(\""+item.date+"\")",
  830. category : item.source_id,
  831. width : item.line_width,
  832. opacity : item.line_opacity,
  833. color : item.line_color,
  834. polygon : true
  835. });
  836. markers.push(polyline);
  837. break;
  838. default:
  839. // console.log("Geometry: " + features.items[i].geometry.type);
  840. }
  841. }
  842. return markers;
  843. };
  844. /**
  845. * Adds a Tile Layer to the map
  846. *
  847. * Requires providing a parameterized tile url. Use {Z}, {X}, and {Y} to specify where the parameters
  848. * should go in the URL.
  849. *
  850. * For example, the OpenStreetMap tiles are:
  851. * m.addTileLayer("http://tile.openstreetmap.org/{Z}/{X}/{Y}.png", 1.0, "OSM", 1, 19, true);
  852. *
  853. * @param {tile_url} template url of the tiles.
  854. * @param {opacity} opacity of the tile layer - 0 is transparent, 1 is opaque. (default=0.6)
  855. * @param {copyright_text} copyright text to use for the tile layer. (default=Mapstraction)
  856. * @param {min_zoom} Minimum (furtherest out) zoom level that tiles are available (default=1)
  857. * @param {max_zoom} Maximum (closest) zoom level that the tiles are available (default=18)
  858. * @param {map_type} Should the tile layer be a selectable map type in the layers palette (default=false)
  859. */
  860. Mapstraction.prototype.addTileLayer = function(tile_url, opacity, copyright_text, min_zoom, max_zoom, map_type) {
  861. if(!tile_url) {
  862. return;
  863. }
  864. this.tileLayers = this.tileLayers || [];
  865. opacity = opacity || 0.6;
  866. copyright_text = copyright_text || "Mapstraction";
  867. min_zoom = min_zoom || 1;
  868. max_zoom = max_zoom || 18;
  869. map_type = map_type || false;
  870. return this.invoker.go('addTileLayer', [ tile_url, opacity, copyright_text, min_zoom, max_zoom, map_type] );
  871. };
  872. /**
  873. * addFilter adds a marker filter
  874. * @param {field} name of attribute to filter on
  875. * @param {operator} presently only "ge" or "le"
  876. * @param {value} the value to compare against
  877. */
  878. Mapstraction.prototype.addFilter = function(field, operator, value) {
  879. if (!this.filters) {
  880. this.filters = [];
  881. }
  882. this.filters.push( [field, operator, value] );
  883. };
  884. /**
  885. * Remove the specified filter
  886. * @param {Object} field
  887. * @param {Object} operator
  888. * @param {Object} value
  889. */
  890. Mapstraction.prototype.removeFilter = function(field, operator, value) {
  891. if (!this.filters) {
  892. return;
  893. }
  894. var del;
  895. for (var f=0; f<this.filters.length; f++) {
  896. if (this.filters[f][0] == field &&
  897. (! operator || (this.filters[f][1] == operator && this.filters[f][2] == value))) {
  898. this.filters.splice(f,1);
  899. f--; //array size decreased
  900. }
  901. }
  902. };
  903. /**
  904. * Delete the current filter if present; otherwise add it
  905. * @param {Object} field
  906. * @param {Object} operator
  907. * @param {Object} value
  908. */
  909. Mapstraction.prototype.toggleFilter = function(field, operator, value) {
  910. if (!this.filters) {
  911. this.filters = [];
  912. }
  913. var found = false;
  914. for (var f = 0; f < this.filters.length; f++) {
  915. if (this.filters[f][0] == field && this.filters[f][1] == operator && this.filters[f][2] == value) {
  916. this.filters.splice(f,1);
  917. f--; //array size decreased
  918. found = true;
  919. }
  920. }
  921. if (! found) {
  922. this.addFilter(field, operator, value);
  923. }
  924. };
  925. /**
  926. * removeAllFilters
  927. */
  928. Mapstraction.prototype.removeAllFilters = function() {
  929. this.filters = [];
  930. };
  931. /**
  932. * doFilter executes all filters added since last call
  933. * Now supports a callback function for when a marker is shown or hidden
  934. * @param {Function} showCallback
  935. * @param {Function} hideCallback
  936. * @returns {Int} count of visible markers
  937. */
  938. Mapstraction.prototype.doFilter = function(showCallback, hideCallback) {
  939. var map = this.maps[this.api];
  940. var visibleCount = 0;
  941. var f;
  942. if (this.filters) {
  943. switch (this.api) {
  944. case 'multimap':
  945. /* TODO polylines aren't filtered in multimap */
  946. var mmfilters = [];
  947. for (f=0; f<this.filters.length; f++) {
  948. mmfilters.push( new MMSearchFilter( this.filters[f][0], this.filters[f][1], this.filters[f][2] ));
  949. }
  950. map.setMarkerFilters( mmfilters );
  951. map.redrawMap();
  952. break;
  953. case ' dummy':
  954. break;
  955. default:
  956. var vis;
  957. for (var m=0; m<this.markers.length; m++) {
  958. vis = true;
  959. for (f = 0; f < this.filters.length; f++) {
  960. if (! this.applyFilter(this.markers[m], this.filters[f])) {
  961. vis = false;
  962. }
  963. }
  964. if (vis) {
  965. visibleCount ++;
  966. if (showCallback){
  967. showCallback(this.markers[m]);
  968. }
  969. else {
  970. this.markers[m].show();
  971. }
  972. }
  973. else {
  974. if (hideCallback){
  975. hideCallback(this.markers[m]);
  976. }
  977. else {
  978. this.markers[m].hide();
  979. }
  980. }
  981. this.markers[m].setAttribute("visible", vis);
  982. }
  983. break;
  984. }
  985. }
  986. return visibleCount;
  987. };
  988. Mapstraction.prototype.applyFilter = function(o, f) {
  989. var vis = true;
  990. switch (f[1]) {
  991. case 'ge':
  992. if (o.getAttribute( f[0] ) < f[2]) {
  993. vis = false;
  994. }
  995. break;
  996. case 'le':
  997. if (o.getAttribute( f[0] ) > f[2]) {
  998. vis = false;
  999. }
  1000. break;
  1001. case 'eq':
  1002. if (o.getAttribute( f[0] ) == f[2]) {
  1003. vis = false;
  1004. }
  1005. break;
  1006. }
  1007. return vis;
  1008. };
  1009. /**
  1010. * getAttributeExtremes returns the minimum/maximum of "field" from all markers
  1011. * @param {field} name of "field" to query
  1012. * @returns {array} of minimum/maximum
  1013. */
  1014. Mapstraction.prototype.getAttributeExtremes = function(field) {
  1015. var min;
  1016. var max;
  1017. for (var m=0; m<this.markers.length; m++) {
  1018. if (! min || min > this.markers[m].getAttribute(field)) {
  1019. min = this.markers[m].getAttribute(field);
  1020. }
  1021. if (! max || max < this.markers[m].getAttribute(field)) {
  1022. max = this.markers[m].getAttribute(field);
  1023. }
  1024. }
  1025. for (var p=0; m<this.polylines.length; m++) {
  1026. if (! min || min > this.polylines[p].getAttribute(field)) {
  1027. min = this.polylines[p].getAttribute(field);
  1028. }
  1029. if (! max || max < this.polylines[p].getAttribute(field)) {
  1030. max = this.polylines[p].getAttribute(field);
  1031. }
  1032. }
  1033. return [min, max];
  1034. };
  1035. /**
  1036. * getMap returns the native map object that mapstraction is talking to
  1037. * @returns the native map object mapstraction is using
  1038. */
  1039. Mapstraction.prototype.getMap = function() {
  1040. // FIXME in an ideal world this shouldn't exist right?
  1041. return this.maps[this.api];
  1042. };
  1043. //////////////////////////////
  1044. //
  1045. // LatLonPoint
  1046. //
  1047. /////////////////////////////
  1048. /**
  1049. * LatLonPoint is a point containing a latitude and longitude with helper methods
  1050. * @name mxn.LatLonPoint
  1051. * @constructor
  1052. * @param {double} lat is the latitude
  1053. * @param {double} lon is the longitude
  1054. * @exports LatLonPoint as mxn.LatLonPoint
  1055. */
  1056. var LatLonPoint = mxn.LatLonPoint = function(lat, lon) {
  1057. // TODO error if undefined?
  1058. // if (lat == undefined) alert('undefined lat');
  1059. // if (lon == undefined) alert('undefined lon');
  1060. this.lat = lat;
  1061. this.lon = lon;
  1062. this.lng = lon; // lets be lon/lng agnostic
  1063. this.invoker = new mxn.Invoker(this, 'LatLonPoint');
  1064. };
  1065. mxn.addProxyMethods(LatLonPoint, [
  1066. 'fromProprietary', 'toProprietary'
  1067. ], true);
  1068. /**
  1069. * toString returns a string represntation of a point
  1070. * @returns a string like '51.23, -0.123'
  1071. * @type String
  1072. */
  1073. LatLonPoint.prototype.toString = function() {
  1074. return this.lat + ', ' + this.lon;
  1075. };
  1076. /**
  1077. * distance returns the distance in kilometers between two points
  1078. * @param {LatLonPoint} otherPoint The other point to measure the distance from to this one
  1079. * @returns the distance between the points in kilometers
  1080. * @type double
  1081. */
  1082. LatLonPoint.prototype.distance = function(otherPoint) {
  1083. // Uses Haversine formula from http://www.movable-type.co.uk
  1084. var rads = Math.PI / 180;
  1085. var diffLat = (this.lat-otherPoint.lat) * rads;
  1086. var diffLon = (this.lon-otherPoint.lon) * rads;
  1087. var a = Math.sin(diffLat / 2) * Math.sin(diffLat / 2) +
  1088. Math.cos(this.lat*rads) * Math.cos(otherPoint.lat*rads) *
  1089. Math.sin(diffLon/2) * Math.sin(diffLon/2);
  1090. return 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)) * 6371; // Earth's mean radius in km
  1091. };
  1092. /**
  1093. * equals tests if this point is the same as some other one
  1094. * @param {LatLonPoint} otherPoint The other point to test with
  1095. * @returns true or false
  1096. * @type boolean
  1097. */
  1098. LatLonPoint.prototype.equals = function(otherPoint) {
  1099. return this.lat == otherPoint.lat && this.lon == otherPoint.lon;
  1100. };
  1101. /**
  1102. * Returns latitude conversion based on current projection
  1103. * @returns {Float} conversion
  1104. */
  1105. LatLonPoint.prototype.latConv = function() {
  1106. return this.distance(new LatLonPoint(this.lat + 0.1, this.lon))*10;
  1107. };
  1108. /**
  1109. * Returns longitude conversion based on current projection
  1110. * @returns {Float} conversion
  1111. */
  1112. LatLonPoint.prototype.lonConv = function() {
  1113. return this.distance(new LatLonPoint(this.lat, this.lon + 0.1))*10;
  1114. };
  1115. //////////////////////////
  1116. //
  1117. // BoundingBox
  1118. //
  1119. //////////////////////////
  1120. /**
  1121. * BoundingBox creates a new bounding box object
  1122. * @name mxn.BoundingBox
  1123. * @constructor
  1124. * @param {double} swlat the latitude of the south-west point
  1125. * @param {double} swlon the longitude of the south-west point
  1126. * @param {double} nelat the latitude of the north-east point
  1127. * @param {double} nelon the longitude of the north-east point
  1128. * @exports BoundingBox as mxn.BoundingBox
  1129. */
  1130. var BoundingBox = mxn.BoundingBox = function(swlat, swlon, nelat, nelon) {
  1131. //FIXME throw error if box bigger than world
  1132. //alert('new bbox ' + swlat + ',' + swlon + ',' + nelat + ',' + nelon);
  1133. this.sw = new LatLonPoint(swlat, swlon);
  1134. this.ne = new LatLonPoint(nelat, nelon);
  1135. };
  1136. /**
  1137. * getSouthWest returns a LatLonPoint of the south-west point of the bounding box
  1138. * @returns the south-west point of the bounding box
  1139. * @type LatLonPoint
  1140. */
  1141. BoundingBox.prototype.getSouthWest = function() {
  1142. return this.sw;
  1143. };
  1144. /**
  1145. * getNorthEast returns a LatLonPoint of the north-east point of the bounding box
  1146. * @returns the north-east point of the bounding box
  1147. * @type LatLonPoint
  1148. */
  1149. BoundingBox.prototype.getNorthEast = function() {
  1150. return this.ne;
  1151. };
  1152. /**
  1153. * isEmpty finds if this bounding box has zero area
  1154. * @returns whether the north-east and south-west points of the bounding box are the same point
  1155. * @type boolean
  1156. */
  1157. BoundingBox.prototype.isEmpty = function() {
  1158. return this.ne == this.sw; // is this right? FIXME
  1159. };
  1160. /**
  1161. * contains finds whether a given point is within a bounding box
  1162. * @param {LatLonPoint} point the point to test with
  1163. * @returns whether point is within this bounding box
  1164. * @type boolean
  1165. */
  1166. BoundingBox.prototype.contains = function(point){
  1167. return point.lat >= this.sw.lat && point.lat <= this.ne.lat && point.lon >= this.sw.lon && point.lon <= this.ne.lon;
  1168. };
  1169. /**
  1170. * toSpan returns a LatLonPoint with the lat and lon as the height and width of the bounding box
  1171. * @returns a LatLonPoint containing the height and width of this bounding box
  1172. * @type LatLonPoint
  1173. */
  1174. BoundingBox.prototype.toSpan = function() {
  1175. return new LatLonPoint( Math.abs(this.sw.lat - this.ne.lat), Math.abs(this.sw.lon - this.ne.lon) );
  1176. };
  1177. /**
  1178. * extend extends the bounding box to include the new point
  1179. */
  1180. BoundingBox.prototype.extend = function(point) {
  1181. if(this.sw.lat > point.lat) {
  1182. this.sw.lat = point.lat;
  1183. }
  1184. if(this.sw.lon > point.lon) {
  1185. this.sw.lon = point.lon;
  1186. }
  1187. if(this.ne.lat < point.lat) {
  1188. this.ne.lat = point.lat;
  1189. }
  1190. if(this.ne.lon < point.lon) {
  1191. this.ne.lon = point.lon;
  1192. }
  1193. return;
  1194. };
  1195. //////////////////////////////
  1196. //
  1197. // Marker
  1198. //
  1199. ///////////////////////////////
  1200. /**
  1201. * Marker create's a new marker pin
  1202. * @name mxn.Marker
  1203. * @constructor
  1204. * @param {LatLonPoint} point the point on the map where the marker should go
  1205. * @exports Marker as mxn.Marker
  1206. */
  1207. var Marker = mxn.Marker = function(point) {
  1208. this.api = null;
  1209. this.location = point;
  1210. this.onmap = false;
  1211. this.proprietary_marker = false;
  1212. this.attributes = [];
  1213. this.invoker = new mxn.Invoker(this, 'Marker', function(){return this.api;});
  1214. mxn.addEvents(this, [
  1215. 'openInfoBubble', // Info bubble opened
  1216. 'closeInfoBubble', // Info bubble closed
  1217. 'click' // Marker clicked
  1218. ]);
  1219. };
  1220. mxn.addProxyMethods(Marker, [
  1221. 'fromProprietary',
  1222. 'hide',
  1223. 'openBubble',
  1224. 'show',
  1225. 'toProprietary',
  1226. 'update'
  1227. ]);
  1228. Marker.prototype.setChild = function(some_proprietary_marker) {
  1229. this.proprietary_marker = some_proprietary_marker;
  1230. some_proprietary_marker.mapstraction_marker = this;
  1231. this.onmap = true;
  1232. };
  1233. Marker.prototype.setLabel = function(labelText) {
  1234. this.labelText = labelText;
  1235. };
  1236. /**
  1237. * addData conviniently set a hash of options on a marker
  1238. */
  1239. Marker.prototype.addData = function(options){
  1240. for(var sOptKey in options) {
  1241. if(options.hasOwnProperty(sOptKey)){
  1242. switch(sOptKey) {
  1243. case 'label':
  1244. this.setLabel(options.label);
  1245. break;
  1246. case 'infoBubble':
  1247. this.setInfoBubble(options.infoBubble);
  1248. break;
  1249. case 'icon':
  1250. if(options.iconSize && options.iconAnchor) {
  1251. this.setIcon(options.icon, options.iconSize, options.iconAnchor);
  1252. }
  1253. else if(options.iconSize) {
  1254. this.setIcon(options.icon, options.iconSize);
  1255. }
  1256. else {
  1257. this.setIcon(options.icon);
  1258. }
  1259. break;
  1260. case 'iconShadow':
  1261. if(options.iconShadowSize) {
  1262. this.setShadowIcon(options.iconShadow, [ options.iconShadowSize[0], options.iconShadowSize[1] ]);
  1263. }
  1264. else {
  1265. this.setIcon(options.iconShadow);
  1266. }
  1267. break;
  1268. case 'infoDiv':
  1269. this.setInfoDiv(options.infoDiv[0],options.infoDiv[1]);
  1270. break;
  1271. case 'draggable':
  1272. this.setDraggable(options.draggable);
  1273. break;
  1274. case 'hover':
  1275. this.setHover(options.hover);
  1276. this.setHoverIcon(options.hoverIcon);
  1277. break;
  1278. case 'hoverIcon':
  1279. this.setHoverIcon(options.hoverIcon);
  1280. break;
  1281. case 'openBubble':
  1282. this.openBubble();
  1283. break;
  1284. case 'groupName':
  1285. this.setGroupName(options.groupName);
  1286. break;
  1287. default:
  1288. // don't have a specific action for this bit of
  1289. // data so set a named attribute
  1290. this.setAttribute(sOptKey, options[sOptKey]);
  1291. break;
  1292. }
  1293. }
  1294. }
  1295. };
  1296. /**
  1297. * setInfoBubble sets the html/text content for a bubble popup for a marker
  1298. * @param {String} infoBubble the html/text you want displayed
  1299. */
  1300. Marker.prototype.setInfoBubble = function(infoBubble) {
  1301. this.infoBubble = infoBubble;
  1302. };
  1303. /**
  1304. * setInfoDiv sets the text and the id of the div element where to the information
  1305. * useful for putting information in a div outside of the map
  1306. * @param {String} infoDiv the html/text you want displayed
  1307. * @param {String} div the element id to use for displaying the text/html
  1308. */
  1309. Marker.prototype.setInfoDiv = function(infoDiv,div){
  1310. this.infoDiv = infoDiv;
  1311. this.div = div;
  1312. };
  1313. /**
  1314. * setIcon sets the icon for a marker
  1315. * @param {String} iconUrl The URL of the image you want to be the icon
  1316. */
  1317. Marker.prototype.setIcon = function(iconUrl, iconSize, iconAnchor) {
  1318. this.iconUrl = iconUrl;
  1319. if(iconSize) {
  1320. this.iconSize = iconSize;
  1321. }
  1322. if(iconAnchor) {
  1323. this.iconAnchor = iconAnchor;
  1324. }
  1325. };
  1326. /**
  1327. * setIconSize sets the size of the icon for a marker
  1328. * @param {String} iconSize The array size in pixels of the marker image
  1329. */
  1330. Marker.prototype.setIconSize = function(iconSize){
  1331. if(iconSize) {
  1332. this.iconSize = iconSize;
  1333. }
  1334. };
  1335. /**
  1336. * setIconAnchor sets the anchor point for a marker
  1337. * @param {String} iconAnchor The array offset of the anchor point
  1338. */
  1339. Marker.prototype.setIconAnchor = function(iconAnchor){
  1340. if(iconAnchor) {
  1341. this.iconAnchor = iconAnchor;
  1342. }
  1343. };
  1344. /**
  1345. * setShadowIcon sets the icon for a marker
  1346. * @param {String} iconUrl The URL of the image you want to be the icon
  1347. */
  1348. Marker.prototype.setShadowIcon = function(iconShadowUrl, iconShadowSize){
  1349. this.iconShadowUrl = iconShadowUrl;
  1350. if(iconShadowSize) {
  1351. this.iconShadowSize = iconShadowSize;
  1352. }
  1353. };
  1354. Marker.prototype.setHoverIcon = function(hoverIconUrl){
  1355. this.hoverIconUrl = hoverIconUrl;
  1356. };
  1357. /**
  1358. * setDraggable sets the draggable state of the marker
  1359. * @param {Bool} draggable set to true if marker should be draggable by the user
  1360. */
  1361. Marker.prototype.setDraggable = function(draggable) {
  1362. this.draggable = draggable;
  1363. };
  1364. /**
  1365. * setHover sets that the marker info is displayed on hover
  1366. * @param {Bool} hover set to true if marker should display info on hover
  1367. */
  1368. Marker.prototype.setHover = function(hover) {
  1369. this.hover = hover;
  1370. };
  1371. /**
  1372. * Markers are grouped up by this name. declutterGroup makes use of this.
  1373. */
  1374. Marker.prototype.setGroupName = function(sGrpName) {
  1375. this.groupName = sGrpName;
  1376. };
  1377. /**
  1378. * setAttribute: set an arbitrary key/value pair on a marker
  1379. * @arg(String) key
  1380. * @arg value
  1381. */
  1382. Marker.prototype.setAttribute = function(key,value) {
  1383. this.attributes[key] = value;
  1384. };
  1385. /**
  1386. * getAttribute: gets the value of "key"
  1387. * @arg(String) key
  1388. * @returns value
  1389. */
  1390. Marker.prototype.getAttribute = function(key) {
  1391. return this.attributes[key];
  1392. };
  1393. ///////////////
  1394. // Polyline ///
  1395. ///////////////
  1396. /**
  1397. * Instantiates a new Polyline.
  1398. * @name mxn.Polyline
  1399. * @constructor
  1400. * @param {Point[]} points Points that make up the Polyline.
  1401. * @exports Polyline as mxn.Polyline
  1402. */
  1403. var Polyline = mxn.Polyline = function(points) {
  1404. this.api = null;
  1405. this.points = points;
  1406. this.attributes = [];
  1407. this.onmap = false;
  1408. this.proprietary_polyline = false;
  1409. this.pllID = "mspll-"+new Date().getTime()+'-'+(Math.floor(Math.random()*Math.pow(2,16)));
  1410. this.invoker = new mxn.Invoker(this, 'Polyline', function(){return this.api;});
  1411. };
  1412. mxn.addProxyMethods(Polyline, [
  1413. 'fromProprietary',
  1414. 'hide',
  1415. 'show',
  1416. 'toProprietary',
  1417. 'update'
  1418. ]);
  1419. /**
  1420. * addData conviniently set a hash of options on a polyline
  1421. */
  1422. Polyline.prototype.addData = function(options){
  1423. for(var sOpt in options) {
  1424. if(options.hasOwnProperty(sOpt)){
  1425. switch(sOpt) {
  1426. case 'color':
  1427. this.setColor(options.color);
  1428. break;
  1429. case 'width':
  1430. this.setWidth(options.width);
  1431. break;
  1432. case 'opacity':
  1433. this.setOpacity(options.opacity);
  1434. break;
  1435. case 'closed':
  1436. this.setClosed(options.closed);
  1437. break;
  1438. case 'fillColor':
  1439. this.setFillColor(options.fillColor);
  1440. break;
  1441. default:
  1442. this.setAttribute(sOpt, options[sOpt]);
  1443. break;
  1444. }
  1445. }
  1446. }
  1447. };
  1448. Polyline.prototype.setChild = function(some_proprietary_polyline) {
  1449. this.proprietary_polyline = some_proprietary_polyline;
  1450. this.onmap = true;
  1451. };
  1452. /**
  1453. * in the form: #RRGGBB
  1454. * Note map24 insists on upper case, so we convert it.
  1455. */
  1456. Polyline.prototype.setColor = function(color){
  1457. this.color = (color.length==7 && color[0]=="#") ? color.toUpperCase() : color;
  1458. };
  1459. /**
  1460. * Stroke width of the polyline
  1461. * @param {Integer} width
  1462. */
  1463. Polyline.prototype.setWidth = function(width){
  1464. this.width = width;
  1465. };
  1466. /**
  1467. * A float between 0.0 and 1.0
  1468. * @param {Float} opacity
  1469. */
  1470. Polyline.prototype.setOpacity = function(opacity){
  1471. this.opacity = opacity;
  1472. };
  1473. /**
  1474. * Marks the polyline as a closed polygon
  1475. * @param {Boolean} bClosed
  1476. */
  1477. Polyline.prototype.setClosed = function(bClosed){
  1478. this.closed = bClosed;
  1479. };
  1480. /**
  1481. * Fill color for a closed polyline as HTML color value e.g. #RRGGBB
  1482. * @param {String} sFillColor HTML color value #RRGGBB
  1483. */
  1484. Polyline.prototype.setFillColor = function(sFillColor) {
  1485. this.fillColor = sFillColor;
  1486. };
  1487. /**
  1488. * setAttribute: set an arbitrary key/value pair on a polyline
  1489. * @arg(String) key
  1490. * @arg value
  1491. */
  1492. Polyline.prototype.setAttribute = function(key,value) {
  1493. this.attributes[key] = value;
  1494. };
  1495. /**
  1496. * getAttribute: gets the value of "key"
  1497. * @arg(String) key
  1498. * @returns value
  1499. */
  1500. Polyline.prototype.getAttribute = function(key) {
  1501. return this.attributes[key];
  1502. };
  1503. /**
  1504. * Simplifies a polyline, averaging and reducing the points
  1505. * @param {Integer} tolerance (1.0 is a good starting point)
  1506. */
  1507. Polyline.prototype.simplify = function(tolerance) {
  1508. var reduced = [];
  1509. // First point
  1510. reduced[0] = this.points[0];
  1511. var markerPoint = 0;
  1512. for (var i = 1; i < this.points.length-1; i++){
  1513. if (this.points[i].distance(this.points[markerPoint]) >= tolerance)
  1514. {
  1515. reduced[reduced.length] = this.points[i];
  1516. markerPoint = i;
  1517. }
  1518. }
  1519. // Last point
  1520. reduced[reduced.length] = this.points[this.points.length-1];
  1521. // Revert
  1522. this.points = reduced;
  1523. };
  1524. ///////////////
  1525. // Radius //
  1526. ///////////////
  1527. /**
  1528. * Creates a new radius object for drawing circles around a point, does a lot of initial calculation to increase load time
  1529. * @returns a new Radius
  1530. * @type Radius
  1531. * @constructor
  1532. * @classDescription Radius
  1533. * @param {Object} Center LatLonPoint of the radius
  1534. * @param {quality} Number of points that comprise the approximated circle (20 is a good starting point)
  1535. */
  1536. var Radius = mxn.Radius = function(center, quality) {
  1537. this.center = center;
  1538. var latConv = center.latConv();
  1539. var lonConv = center.lonConv();
  1540. // Create Radian conversion constant
  1541. var rad = Math.PI / 180;
  1542. this.calcs = [];
  1543. for(var i = 0; i < 360; i += quality){
  1544. this.calcs.push([Math.cos(i * rad) / latConv, Math.sin(i * rad) / lonConv]);
  1545. }
  1546. };
  1547. /**
  1548. * Returns polyline of a circle around the point based on new radius
  1549. * @param {Radius} radius
  1550. * @param {Colour} colour
  1551. * @returns {Polyline} Polyline
  1552. */
  1553. Radius.prototype.getPolyline = function(radius, colour) {
  1554. var points = [];
  1555. for(var i = 0; i < this.calcs.length; i++){
  1556. var point = new LatLonPoint(
  1557. this.center.lat + (radius * this.calcs[i][0]),
  1558. this.center.lon + (radius * this.calcs[i][1])
  1559. );
  1560. points.push(point);
  1561. }
  1562. // Add first point
  1563. points.push(points[0]);
  1564. var line = new Polyline(points);
  1565. line.setColor(colour);
  1566. return line;
  1567. };
  1568. })();