Map.js 88 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546
  1. /* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the Clear BSD license.
  3. * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/BaseTypes/Class.js
  7. * @requires OpenLayers/Util.js
  8. * @requires OpenLayers/Events.js
  9. * @requires OpenLayers/Tween.js
  10. * @requires OpenLayers/Console.js
  11. * @requires OpenLayers/Lang.js
  12. */
  13. /**
  14. * Class: OpenLayers.Map
  15. * Instances of OpenLayers.Map are interactive maps embedded in a web page.
  16. * Create a new map with the <OpenLayers.Map> constructor.
  17. *
  18. * On their own maps do not provide much functionality. To extend a map
  19. * it's necessary to add controls (<OpenLayers.Control>) and
  20. * layers (<OpenLayers.Layer>) to the map.
  21. */
  22. OpenLayers.Map = OpenLayers.Class({
  23. /**
  24. * Constant: Z_INDEX_BASE
  25. * {Object} Base z-indexes for different classes of thing
  26. */
  27. Z_INDEX_BASE: {
  28. BaseLayer: 100,
  29. Overlay: 325,
  30. Feature: 725,
  31. Popup: 750,
  32. Control: 1000
  33. },
  34. /**
  35. * Constant: EVENT_TYPES
  36. * {Array(String)} Supported application event types. Register a listener
  37. * for a particular event with the following syntax:
  38. * (code)
  39. * map.events.register(type, obj, listener);
  40. * (end)
  41. *
  42. * Listeners will be called with a reference to an event object. The
  43. * properties of this event depends on exactly what happened.
  44. *
  45. * All event objects have at least the following properties:
  46. * - *object* {Object} A reference to map.events.object.
  47. * - *element* {DOMElement} A reference to map.events.element.
  48. *
  49. * Browser events have the following additional properties:
  50. * - *xy* {<OpenLayers.Pixel>} The pixel location of the event (relative
  51. * to the the map viewport).
  52. * - other properties that come with browser events
  53. *
  54. * Supported map event types:
  55. * - *preaddlayer* triggered before a layer has been added. The event
  56. * object will include a *layer* property that references the layer
  57. * to be added. When a listener returns "false" the adding will be
  58. * aborted.
  59. * - *addlayer* triggered after a layer has been added. The event object
  60. * will include a *layer* property that references the added layer.
  61. * - *preremovelayer* triggered before a layer has been removed. The event
  62. * object will include a *layer* property that references the layer
  63. * to be removed. When a listener returns "false" the removal will be
  64. * aborted.
  65. * - *removelayer* triggered after a layer has been removed. The event
  66. * object will include a *layer* property that references the removed
  67. * layer.
  68. * - *changelayer* triggered after a layer name change, order change,
  69. * opacity change, params change, visibility change (due to resolution
  70. * thresholds) or attribution change (due to extent change). Listeners
  71. * will receive an event object with *layer* and *property* properties.
  72. * The *layer* property will be a reference to the changed layer. The
  73. * *property* property will be a key to the changed property (name,
  74. * order, opacity, params, visibility or attribution).
  75. * - *movestart* triggered after the start of a drag, pan, or zoom
  76. * - *move* triggered after each drag, pan, or zoom
  77. * - *moveend* triggered after a drag, pan, or zoom completes
  78. * - *zoomend* triggered after a zoom completes
  79. * - *mouseover* triggered after mouseover the map
  80. * - *mouseout* triggered after mouseout the map
  81. * - *mousemove* triggered after mousemove the map
  82. * - *changebaselayer* triggered after the base layer changes
  83. */
  84. EVENT_TYPES: [
  85. "preaddlayer", "addlayer","preremovelayer", "removelayer",
  86. "changelayer", "movestart",
  87. "move", "moveend", "zoomend", "popupopen", "popupclose",
  88. "addmarker", "removemarker", "clearmarkers", "mouseover",
  89. "mouseout", "mousemove", "dragstart", "drag", "dragend",
  90. "changebaselayer"],
  91. /**
  92. * Property: id
  93. * {String} Unique identifier for the map
  94. */
  95. id: null,
  96. /**
  97. * Property: fractionalZoom
  98. * {Boolean} For a base layer that supports it, allow the map resolution
  99. * to be set to a value between one of the values in the resolutions
  100. * array. Default is false.
  101. *
  102. * When fractionalZoom is set to true, it is possible to zoom to
  103. * an arbitrary extent. This requires a base layer from a source
  104. * that supports requests for arbitrary extents (i.e. not cached
  105. * tiles on a regular lattice). This means that fractionalZoom
  106. * will not work with commercial layers (Google, Yahoo, VE), layers
  107. * using TileCache, or any other pre-cached data sources.
  108. *
  109. * If you are using fractionalZoom, then you should also use
  110. * <getResolutionForZoom> instead of layer.resolutions[zoom] as the
  111. * former works for non-integer zoom levels.
  112. */
  113. fractionalZoom: false,
  114. /**
  115. * APIProperty: events
  116. * {<OpenLayers.Events>} An events object that handles all
  117. * events on the map
  118. */
  119. events: null,
  120. /**
  121. * APIProperty: allOverlays
  122. * {Boolean} Allow the map to function with "overlays" only. Defaults to
  123. * false. If true, the lowest layer in the draw order will act as
  124. * the base layer. In addition, if set to true, all layers will
  125. * have isBaseLayer set to false when they are added to the map.
  126. *
  127. * Note:
  128. * If you set map.allOverlays to true, then you *cannot* use
  129. * map.setBaseLayer or layer.setIsBaseLayer. With allOverlays true,
  130. * the lowest layer in the draw layer is the base layer. So, to change
  131. * the base layer, use <setLayerIndex> or <raiseLayer> to set the layer
  132. * index to 0.
  133. */
  134. allOverlays: false,
  135. /**
  136. * APIProperty: div
  137. * {DOMElement|String} The element that contains the map (or an id for
  138. * that element). If the <OpenLayers.Map> constructor is called
  139. * with two arguments, this should be provided as the first argument.
  140. * Alternatively, the map constructor can be called with the options
  141. * object as the only argument. In this case (one argument), a
  142. * div property may or may not be provided. If the div property
  143. * is not provided, the map can be rendered to a container later
  144. * using the <render> method.
  145. *
  146. * Note:
  147. * If you are calling <render> after map construction, do not use
  148. * <maxResolution> auto. Instead, divide your <maxExtent> by your
  149. * maximum expected dimension.
  150. */
  151. div: null,
  152. /**
  153. * Property: dragging
  154. * {Boolean} The map is currently being dragged.
  155. */
  156. dragging: false,
  157. /**
  158. * Property: size
  159. * {<OpenLayers.Size>} Size of the main div (this.div)
  160. */
  161. size: null,
  162. /**
  163. * Property: viewPortDiv
  164. * {HTMLDivElement} The element that represents the map viewport
  165. */
  166. viewPortDiv: null,
  167. /**
  168. * Property: layerContainerOrigin
  169. * {<OpenLayers.LonLat>} The lonlat at which the later container was
  170. * re-initialized (on-zoom)
  171. */
  172. layerContainerOrigin: null,
  173. /**
  174. * Property: layerContainerDiv
  175. * {HTMLDivElement} The element that contains the layers.
  176. */
  177. layerContainerDiv: null,
  178. /**
  179. * APIProperty: layers
  180. * {Array(<OpenLayers.Layer>)} Ordered list of layers in the map
  181. */
  182. layers: null,
  183. /**
  184. * Property: controls
  185. * {Array(<OpenLayers.Control>)} List of controls associated with the map.
  186. *
  187. * If not provided in the map options at construction, the map will
  188. * be given the following controls by default:
  189. * - <OpenLayers.Control.Navigation>
  190. * - <OpenLayers.Control.PanZoom>
  191. * - <OpenLayers.Control.ArgParser>
  192. * - <OpenLayers.Control.Attribution>
  193. */
  194. controls: null,
  195. /**
  196. * Property: popups
  197. * {Array(<OpenLayers.Popup>)} List of popups associated with the map
  198. */
  199. popups: null,
  200. /**
  201. * APIProperty: baseLayer
  202. * {<OpenLayers.Layer>} The currently selected base layer. This determines
  203. * min/max zoom level, projection, etc.
  204. */
  205. baseLayer: null,
  206. /**
  207. * Property: center
  208. * {<OpenLayers.LonLat>} The current center of the map
  209. */
  210. center: null,
  211. /**
  212. * Property: resolution
  213. * {Float} The resolution of the map.
  214. */
  215. resolution: null,
  216. /**
  217. * Property: zoom
  218. * {Integer} The current zoom level of the map
  219. */
  220. zoom: 0,
  221. /**
  222. * Property: panRatio
  223. * {Float} The ratio of the current extent within
  224. * which panning will tween.
  225. */
  226. panRatio: 1.5,
  227. /**
  228. * Property: viewRequestID
  229. * {String} Used to store a unique identifier that changes when the map
  230. * view changes. viewRequestID should be used when adding data
  231. * asynchronously to the map: viewRequestID is incremented when
  232. * you initiate your request (right now during changing of
  233. * baselayers and changing of zooms). It is stored here in the
  234. * map and also in the data that will be coming back
  235. * asynchronously. Before displaying this data on request
  236. * completion, we check that the viewRequestID of the data is
  237. * still the same as that of the map. Fix for #480
  238. */
  239. viewRequestID: 0,
  240. // Options
  241. /**
  242. * APIProperty: tileSize
  243. * {<OpenLayers.Size>} Set in the map options to override the default tile
  244. * size for this map.
  245. */
  246. tileSize: null,
  247. /**
  248. * APIProperty: projection
  249. * {String} Set in the map options to override the default projection
  250. * string this map - also set maxExtent, maxResolution, and
  251. * units if appropriate. Default is "EPSG:4326".
  252. */
  253. projection: "EPSG:4326",
  254. /**
  255. * APIProperty: units
  256. * {String} The map units. Defaults to 'degrees'. Possible values are
  257. * 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.
  258. */
  259. units: 'degrees',
  260. /**
  261. * APIProperty: resolutions
  262. * {Array(Float)} A list of map resolutions (map units per pixel) in
  263. * descending order. If this is not set in the layer constructor, it
  264. * will be set based on other resolution related properties
  265. * (maxExtent, maxResolution, maxScale, etc.).
  266. */
  267. resolutions: null,
  268. /**
  269. * APIProperty: maxResolution
  270. * {Float} Default max is 360 deg / 256 px, which corresponds to
  271. * zoom level 0 on gmaps. Specify a different value in the map
  272. * options if you are not using a geographic projection and
  273. * displaying the whole world.
  274. */
  275. maxResolution: 1.40625,
  276. /**
  277. * APIProperty: minResolution
  278. * {Float}
  279. */
  280. minResolution: null,
  281. /**
  282. * APIProperty: maxScale
  283. * {Float}
  284. */
  285. maxScale: null,
  286. /**
  287. * APIProperty: minScale
  288. * {Float}
  289. */
  290. minScale: null,
  291. /**
  292. * APIProperty: maxExtent
  293. * {<OpenLayers.Bounds>} The maximum extent for the map. Defaults to the
  294. * whole world in decimal degrees
  295. * (-180, -90, 180, 90). Specify a different
  296. * extent in the map options if you are not using a
  297. * geographic projection and displaying the whole
  298. * world.
  299. */
  300. maxExtent: null,
  301. /**
  302. * APIProperty: minExtent
  303. * {<OpenLayers.Bounds>}
  304. */
  305. minExtent: null,
  306. /**
  307. * APIProperty: restrictedExtent
  308. * {<OpenLayers.Bounds>} Limit map navigation to this extent where possible.
  309. * If a non-null restrictedExtent is set, panning will be restricted
  310. * to the given bounds. In addition, zooming to a resolution that
  311. * displays more than the restricted extent will center the map
  312. * on the restricted extent. If you wish to limit the zoom level
  313. * or resolution, use maxResolution.
  314. */
  315. restrictedExtent: null,
  316. /**
  317. * APIProperty: numZoomLevels
  318. * {Integer} Number of zoom levels for the map. Defaults to 16. Set a
  319. * different value in the map options if needed.
  320. */
  321. numZoomLevels: 16,
  322. /**
  323. * APIProperty: theme
  324. * {String} Relative path to a CSS file from which to load theme styles.
  325. * Specify null in the map options (e.g. {theme: null}) if you
  326. * want to get cascading style declarations - by putting links to
  327. * stylesheets or style declarations directly in your page.
  328. */
  329. theme: null,
  330. /**
  331. * APIProperty: displayProjection
  332. * {<OpenLayers.Projection>} Requires proj4js support.Projection used by
  333. * several controls to display data to user. If this property is set,
  334. * it will be set on any control which has a null displayProjection
  335. * property at the time the control is added to the map.
  336. */
  337. displayProjection: null,
  338. /**
  339. * APIProperty: fallThrough
  340. * {Boolean} Should OpenLayers allow events on the map to fall through to
  341. * other elements on the page, or should it swallow them? (#457)
  342. * Default is to fall through.
  343. */
  344. fallThrough: true,
  345. /**
  346. * Property: panTween
  347. * {OpenLayers.Tween} Animated panning tween object, see panTo()
  348. */
  349. panTween: null,
  350. /**
  351. * APIProperty: eventListeners
  352. * {Object} If set as an option at construction, the eventListeners
  353. * object will be registered with <OpenLayers.Events.on>. Object
  354. * structure must be a listeners object as shown in the example for
  355. * the events.on method.
  356. */
  357. eventListeners: null,
  358. /**
  359. * APIProperty: panMethod
  360. * {Function} The Easing function to be used for tweening. Default is
  361. * OpenLayers.Easing.Expo.easeOut. Setting this to 'null' turns off
  362. * animated panning.
  363. */
  364. panMethod: OpenLayers.Easing.Expo.easeOut,
  365. /**
  366. * Property: panDuration
  367. * {Integer} The number of steps to be passed to the
  368. * OpenLayers.Tween.start() method when the map is
  369. * panned.
  370. * Default is 50.
  371. */
  372. panDuration: 50,
  373. /**
  374. * Property: paddingForPopups
  375. * {<OpenLayers.Bounds>} Outside margin of the popup. Used to prevent
  376. * the popup from getting too close to the map border.
  377. */
  378. paddingForPopups : null,
  379. /**
  380. * Property: minPx
  381. * {<OpenLayers.Pixel>} Lower left of maxExtent in viewport pixel space.
  382. * Used to verify in moveByPx that the new location we're moving to
  383. * is valid. It is also used in the getLonLatFromViewPortPx function
  384. * of Layer.
  385. */
  386. minPx: null,
  387. /**
  388. * Property: maxPx
  389. * {<OpenLayers.Pixel>} Top right of maxExtent in viewport pixel space.
  390. * Used to verify in moveByPx that the new location we're moving to
  391. * is valid.
  392. */
  393. maxPx: null,
  394. /**
  395. * Constructor: OpenLayers.Map
  396. * Constructor for a new OpenLayers.Map instance. There are two possible
  397. * ways to call the map constructor. See the examples below.
  398. *
  399. * Parameters:
  400. * div - {DOMElement|String} The element or id of an element in your page
  401. * that will contain the map. May be omitted if the <div> option is
  402. * provided or if you intend to call the <render> method later.
  403. * options - {Object} Optional object with properties to tag onto the map.
  404. *
  405. * Examples:
  406. * (code)
  407. * // create a map with default options in an element with the id "map1"
  408. * var map = new OpenLayers.Map("map1");
  409. *
  410. * // create a map with non-default options in an element with id "map2"
  411. * var options = {
  412. * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
  413. * maxResolution: 156543,
  414. * units: 'm',
  415. * projection: "EPSG:41001"
  416. * };
  417. * var map = new OpenLayers.Map("map2", options);
  418. *
  419. * // map with non-default options - same as above but with a single argument
  420. * var map = new OpenLayers.Map({
  421. * div: "map_id",
  422. * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
  423. * maxResolution: 156543,
  424. * units: 'm',
  425. * projection: "EPSG:41001"
  426. * });
  427. *
  428. * // create a map without a reference to a container - call render later
  429. * var map = new OpenLayers.Map({
  430. * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
  431. * maxResolution: 156543,
  432. * units: 'm',
  433. * projection: "EPSG:41001"
  434. * });
  435. * (end)
  436. */
  437. initialize: function (div, options) {
  438. // If only one argument is provided, check if it is an object.
  439. if(arguments.length === 1 && typeof div === "object") {
  440. options = div;
  441. div = options && options.div;
  442. }
  443. // Simple-type defaults are set in class definition.
  444. // Now set complex-type defaults
  445. this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
  446. OpenLayers.Map.TILE_HEIGHT);
  447. this.maxExtent = new OpenLayers.Bounds(-180, -90, 180, 90);
  448. this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);
  449. this.theme = OpenLayers._getScriptLocation() +
  450. 'theme/default/style.css';
  451. // now override default options
  452. OpenLayers.Util.extend(this, options);
  453. // initialize layers array
  454. this.layers = [];
  455. this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
  456. this.div = OpenLayers.Util.getElement(div);
  457. if(!this.div) {
  458. this.div = document.createElement("div");
  459. this.div.style.height = "1px";
  460. this.div.style.width = "1px";
  461. }
  462. OpenLayers.Element.addClass(this.div, 'olMap');
  463. // the viewPortDiv is the outermost div we modify
  464. var id = this.id + "_OpenLayers_ViewPort";
  465. this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,
  466. "relative", null,
  467. "hidden");
  468. this.viewPortDiv.style.width = "100%";
  469. this.viewPortDiv.style.height = "100%";
  470. this.viewPortDiv.className = "olMapViewport";
  471. this.div.appendChild(this.viewPortDiv);
  472. // the eventsDiv is where we listen for all map events
  473. var eventsDiv = document.createElement("div");
  474. eventsDiv.id = this.id + "_events";
  475. eventsDiv.style.position = "absolute";
  476. eventsDiv.style.width = "100%";
  477. eventsDiv.style.height = "100%";
  478. eventsDiv.style.zIndex = this.Z_INDEX_BASE.Control - 1;
  479. this.viewPortDiv.appendChild(eventsDiv);
  480. this.eventsDiv = eventsDiv;
  481. this.events = new OpenLayers.Events(
  482. this, this.eventsDiv, this.EVENT_TYPES, this.fallThrough,
  483. {includeXY: true}
  484. );
  485. // the layerContainerDiv is the one that holds all the layers
  486. id = this.id + "_OpenLayers_Container";
  487. this.layerContainerDiv = OpenLayers.Util.createDiv(id);
  488. this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;
  489. this.eventsDiv.appendChild(this.layerContainerDiv);
  490. this.updateSize();
  491. if(this.eventListeners instanceof Object) {
  492. this.events.on(this.eventListeners);
  493. }
  494. // update the map size and location before the map moves
  495. this.events.register("movestart", this, this.updateSize);
  496. // Because Mozilla does not support the "resize" event for elements
  497. // other than "window", we need to put a hack here.
  498. if (OpenLayers.String.contains(navigator.appName, "Microsoft")) {
  499. // If IE, register the resize on the div
  500. this.events.register("resize", this, this.updateSize);
  501. } else {
  502. // Else updateSize on catching the window's resize
  503. // Note that this is ok, as updateSize() does nothing if the
  504. // map's size has not actually changed.
  505. this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize,
  506. this);
  507. OpenLayers.Event.observe(window, 'resize',
  508. this.updateSizeDestroy);
  509. }
  510. // only append link stylesheet if the theme property is set
  511. if(this.theme) {
  512. // check existing links for equivalent url
  513. var addNode = true;
  514. var nodes = document.getElementsByTagName('link');
  515. for(var i=0, len=nodes.length; i<len; ++i) {
  516. if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,
  517. this.theme)) {
  518. addNode = false;
  519. break;
  520. }
  521. }
  522. // only add a new node if one with an equivalent url hasn't already
  523. // been added
  524. if(addNode) {
  525. var cssNode = document.createElement('link');
  526. cssNode.setAttribute('rel', 'stylesheet');
  527. cssNode.setAttribute('type', 'text/css');
  528. cssNode.setAttribute('href', this.theme);
  529. document.getElementsByTagName('head')[0].appendChild(cssNode);
  530. }
  531. }
  532. if (this.controls == null) {
  533. if (OpenLayers.Control != null) { // running full or lite?
  534. this.controls = [ new OpenLayers.Control.Navigation(),
  535. new OpenLayers.Control.PanZoom(),
  536. new OpenLayers.Control.ArgParser(),
  537. new OpenLayers.Control.Attribution()
  538. ];
  539. } else {
  540. this.controls = [];
  541. }
  542. }
  543. for(var i=0, len=this.controls.length; i<len; i++) {
  544. this.addControlToMap(this.controls[i]);
  545. }
  546. this.popups = [];
  547. this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);
  548. // always call map.destroy()
  549. OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);
  550. // add any initial layers
  551. if (options && options.layers) {
  552. /**
  553. * If you have set options.center, the map center property will be
  554. * set at this point. However, since setCenter has not been caleld,
  555. * addLayers gets confused. So we delete the map center in this
  556. * case. Because the check below uses options.center, it will
  557. * be properly set below.
  558. */
  559. delete this.center;
  560. this.addLayers(options.layers);
  561. // set center (and optionally zoom)
  562. if (options.center) {
  563. // zoom can be undefined here
  564. this.setCenter(options.center, options.zoom);
  565. }
  566. }
  567. },
  568. /**
  569. * APIMethod: render
  570. * Render the map to a specified container.
  571. *
  572. * Parameters:
  573. * div - {String|DOMElement} The container that the map should be rendered
  574. * to. If different than the current container, the map viewport
  575. * will be moved from the current to the new container.
  576. */
  577. render: function(div) {
  578. this.div = OpenLayers.Util.getElement(div);
  579. OpenLayers.Element.addClass(this.div, 'olMap');
  580. this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
  581. this.div.appendChild(this.viewPortDiv);
  582. this.updateSize();
  583. },
  584. /**
  585. * Method: unloadDestroy
  586. * Function that is called to destroy the map on page unload. stored here
  587. * so that if map is manually destroyed, we can unregister this.
  588. */
  589. unloadDestroy: null,
  590. /**
  591. * Method: updateSizeDestroy
  592. * When the map is destroyed, we need to stop listening to updateSize
  593. * events: this method stores the function we need to unregister in
  594. * non-IE browsers.
  595. */
  596. updateSizeDestroy: null,
  597. /**
  598. * APIMethod: destroy
  599. * Destroy this map.
  600. * Note that if you are using an application which removes a container
  601. * of the map from the DOM, you need to ensure that you destroy the
  602. * map *before* this happens; otherwise, the page unload handler
  603. * will fail because the DOM elements that map.destroy() wants
  604. * to clean up will be gone. (See
  605. * http://trac.osgeo.org/openlayers/ticket/2277 for more information).
  606. * This will apply to GeoExt and also to other applications which
  607. * modify the DOM of the container of the OpenLayers Map.
  608. */
  609. destroy:function() {
  610. // if unloadDestroy is null, we've already been destroyed
  611. if (!this.unloadDestroy) {
  612. return false;
  613. }
  614. // make sure panning doesn't continue after destruction
  615. if(this.panTween) {
  616. this.panTween.stop();
  617. this.panTween = null;
  618. }
  619. // map has been destroyed. dont do it again!
  620. OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);
  621. this.unloadDestroy = null;
  622. if (this.updateSizeDestroy) {
  623. OpenLayers.Event.stopObserving(window, 'resize',
  624. this.updateSizeDestroy);
  625. } else {
  626. this.events.unregister("resize", this, this.updateSize);
  627. }
  628. this.paddingForPopups = null;
  629. if (this.controls != null) {
  630. for (var i = this.controls.length - 1; i>=0; --i) {
  631. this.controls[i].destroy();
  632. }
  633. this.controls = null;
  634. }
  635. if (this.layers != null) {
  636. for (var i = this.layers.length - 1; i>=0; --i) {
  637. //pass 'false' to destroy so that map wont try to set a new
  638. // baselayer after each baselayer is removed
  639. this.layers[i].destroy(false);
  640. }
  641. this.layers = null;
  642. }
  643. if (this.viewPortDiv) {
  644. this.div.removeChild(this.viewPortDiv);
  645. }
  646. this.viewPortDiv = null;
  647. if(this.eventListeners) {
  648. this.events.un(this.eventListeners);
  649. this.eventListeners = null;
  650. }
  651. this.events.destroy();
  652. this.events = null;
  653. },
  654. /**
  655. * APIMethod: setOptions
  656. * Change the map options
  657. *
  658. * Parameters:
  659. * options - {Object} Hashtable of options to tag to the map
  660. */
  661. setOptions: function(options) {
  662. var updatePxExtent = this.minPx &&
  663. options.restrictedExtent != this.restrictedExtent;
  664. OpenLayers.Util.extend(this, options);
  665. // force recalculation of minPx and maxPx
  666. updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {
  667. forceZoomChange: true
  668. });
  669. },
  670. /**
  671. * APIMethod: getTileSize
  672. * Get the tile size for the map
  673. *
  674. * Returns:
  675. * {<OpenLayers.Size>}
  676. */
  677. getTileSize: function() {
  678. return this.tileSize;
  679. },
  680. /**
  681. * APIMethod: getBy
  682. * Get a list of objects given a property and a match item.
  683. *
  684. * Parameters:
  685. * array - {String} A property on the map whose value is an array.
  686. * property - {String} A property on each item of the given array.
  687. * match - {String | Object} A string to match. Can also be a regular
  688. * expression literal or object. In addition, it can be any object
  689. * with a method named test. For reqular expressions or other, if
  690. * match.test(map[array][i][property]) evaluates to true, the item will
  691. * be included in the array returned. If no items are found, an empty
  692. * array is returned.
  693. *
  694. * Returns:
  695. * {Array} An array of items where the given property matches the given
  696. * criteria.
  697. */
  698. getBy: function(array, property, match) {
  699. var test = (typeof match.test == "function");
  700. var found = OpenLayers.Array.filter(this[array], function(item) {
  701. return item[property] == match || (test && match.test(item[property]));
  702. });
  703. return found;
  704. },
  705. /**
  706. * APIMethod: getLayersBy
  707. * Get a list of layers with properties matching the given criteria.
  708. *
  709. * Parameter:
  710. * property - {String} A layer property to be matched.
  711. * match - {String | Object} A string to match. Can also be a regular
  712. * expression literal or object. In addition, it can be any object
  713. * with a method named test. For reqular expressions or other, if
  714. * match.test(layer[property]) evaluates to true, the layer will be
  715. * included in the array returned. If no layers are found, an empty
  716. * array is returned.
  717. *
  718. * Returns:
  719. * {Array(<OpenLayers.Layer>)} A list of layers matching the given criteria.
  720. * An empty array is returned if no matches are found.
  721. */
  722. getLayersBy: function(property, match) {
  723. return this.getBy("layers", property, match);
  724. },
  725. /**
  726. * APIMethod: getLayersByName
  727. * Get a list of layers with names matching the given name.
  728. *
  729. * Parameter:
  730. * match - {String | Object} A layer name. The name can also be a regular
  731. * expression literal or object. In addition, it can be any object
  732. * with a method named test. For reqular expressions or other, if
  733. * name.test(layer.name) evaluates to true, the layer will be included
  734. * in the list of layers returned. If no layers are found, an empty
  735. * array is returned.
  736. *
  737. * Returns:
  738. * {Array(<OpenLayers.Layer>)} A list of layers matching the given name.
  739. * An empty array is returned if no matches are found.
  740. */
  741. getLayersByName: function(match) {
  742. return this.getLayersBy("name", match);
  743. },
  744. /**
  745. * APIMethod: getLayersByClass
  746. * Get a list of layers of a given class (CLASS_NAME).
  747. *
  748. * Parameter:
  749. * match - {String | Object} A layer class name. The match can also be a
  750. * regular expression literal or object. In addition, it can be any
  751. * object with a method named test. For reqular expressions or other,
  752. * if type.test(layer.CLASS_NAME) evaluates to true, the layer will
  753. * be included in the list of layers returned. If no layers are
  754. * found, an empty array is returned.
  755. *
  756. * Returns:
  757. * {Array(<OpenLayers.Layer>)} A list of layers matching the given class.
  758. * An empty array is returned if no matches are found.
  759. */
  760. getLayersByClass: function(match) {
  761. return this.getLayersBy("CLASS_NAME", match);
  762. },
  763. /**
  764. * APIMethod: getControlsBy
  765. * Get a list of controls with properties matching the given criteria.
  766. *
  767. * Parameter:
  768. * property - {String} A control property to be matched.
  769. * match - {String | Object} A string to match. Can also be a regular
  770. * expression literal or object. In addition, it can be any object
  771. * with a method named test. For reqular expressions or other, if
  772. * match.test(layer[property]) evaluates to true, the layer will be
  773. * included in the array returned. If no layers are found, an empty
  774. * array is returned.
  775. *
  776. * Returns:
  777. * {Array(<OpenLayers.Control>)} A list of controls matching the given
  778. * criteria. An empty array is returned if no matches are found.
  779. */
  780. getControlsBy: function(property, match) {
  781. return this.getBy("controls", property, match);
  782. },
  783. /**
  784. * APIMethod: getControlsByClass
  785. * Get a list of controls of a given class (CLASS_NAME).
  786. *
  787. * Parameter:
  788. * match - {String | Object} A control class name. The match can also be a
  789. * regular expression literal or object. In addition, it can be any
  790. * object with a method named test. For reqular expressions or other,
  791. * if type.test(control.CLASS_NAME) evaluates to true, the control will
  792. * be included in the list of controls returned. If no controls are
  793. * found, an empty array is returned.
  794. *
  795. * Returns:
  796. * {Array(<OpenLayers.Control>)} A list of controls matching the given class.
  797. * An empty array is returned if no matches are found.
  798. */
  799. getControlsByClass: function(match) {
  800. return this.getControlsBy("CLASS_NAME", match);
  801. },
  802. /********************************************************/
  803. /* */
  804. /* Layer Functions */
  805. /* */
  806. /* The following functions deal with adding and */
  807. /* removing Layers to and from the Map */
  808. /* */
  809. /********************************************************/
  810. /**
  811. * APIMethod: getLayer
  812. * Get a layer based on its id
  813. *
  814. * Parameter:
  815. * id - {String} A layer id
  816. *
  817. * Returns:
  818. * {<OpenLayers.Layer>} The Layer with the corresponding id from the map's
  819. * layer collection, or null if not found.
  820. */
  821. getLayer: function(id) {
  822. var foundLayer = null;
  823. for (var i=0, len=this.layers.length; i<len; i++) {
  824. var layer = this.layers[i];
  825. if (layer.id == id) {
  826. foundLayer = layer;
  827. break;
  828. }
  829. }
  830. return foundLayer;
  831. },
  832. /**
  833. * Method: setLayerZIndex
  834. *
  835. * Parameters:
  836. * layer - {<OpenLayers.Layer>}
  837. * zIdx - {int}
  838. */
  839. setLayerZIndex: function (layer, zIdx) {
  840. layer.setZIndex(
  841. this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay']
  842. + zIdx * 5 );
  843. },
  844. /**
  845. * Method: resetLayersZIndex
  846. * Reset each layer's z-index based on layer's array index
  847. */
  848. resetLayersZIndex: function() {
  849. for (var i=0, len=this.layers.length; i<len; i++) {
  850. var layer = this.layers[i];
  851. this.setLayerZIndex(layer, i);
  852. }
  853. },
  854. /**
  855. * APIMethod: addLayer
  856. *
  857. * Parameters:
  858. * layer - {<OpenLayers.Layer>}
  859. */
  860. addLayer: function (layer) {
  861. for(var i=0, len=this.layers.length; i <len; i++) {
  862. if (this.layers[i] == layer) {
  863. var msg = OpenLayers.i18n('layerAlreadyAdded',
  864. {'layerName':layer.name});
  865. OpenLayers.Console.warn(msg);
  866. return false;
  867. }
  868. }
  869. if (this.events.triggerEvent("preaddlayer", {layer: layer}) === false) {
  870. return;
  871. }
  872. if(this.allOverlays) {
  873. layer.isBaseLayer = false;
  874. }
  875. layer.div.className = "olLayerDiv";
  876. layer.div.style.overflow = "";
  877. this.setLayerZIndex(layer, this.layers.length);
  878. if (layer.isFixed) {
  879. this.viewPortDiv.appendChild(layer.div);
  880. } else {
  881. this.layerContainerDiv.appendChild(layer.div);
  882. }
  883. this.layers.push(layer);
  884. layer.setMap(this);
  885. if (layer.isBaseLayer || (this.allOverlays && !this.baseLayer)) {
  886. if (this.baseLayer == null) {
  887. // set the first baselaye we add as the baselayer
  888. this.setBaseLayer(layer);
  889. } else {
  890. layer.setVisibility(false);
  891. }
  892. } else {
  893. layer.redraw();
  894. }
  895. this.events.triggerEvent("addlayer", {layer: layer});
  896. layer.events.triggerEvent("added", {map: this, layer: layer});
  897. layer.afterAdd();
  898. },
  899. /**
  900. * APIMethod: addLayers
  901. *
  902. * Parameters:
  903. * layers - {Array(<OpenLayers.Layer>)}
  904. */
  905. addLayers: function (layers) {
  906. for (var i=0, len=layers.length; i<len; i++) {
  907. this.addLayer(layers[i]);
  908. }
  909. },
  910. /**
  911. * APIMethod: removeLayer
  912. * Removes a layer from the map by removing its visual element (the
  913. * layer.div property), then removing it from the map's internal list
  914. * of layers, setting the layer's map property to null.
  915. *
  916. * a "removelayer" event is triggered.
  917. *
  918. * very worthy of mention is that simply removing a layer from a map
  919. * will not cause the removal of any popups which may have been created
  920. * by the layer. this is due to the fact that it was decided at some
  921. * point that popups would not belong to layers. thus there is no way
  922. * for us to know here to which layer the popup belongs.
  923. *
  924. * A simple solution to this is simply to call destroy() on the layer.
  925. * the default OpenLayers.Layer class's destroy() function
  926. * automatically takes care to remove itself from whatever map it has
  927. * been attached to.
  928. *
  929. * The correct solution is for the layer itself to register an
  930. * event-handler on "removelayer" and when it is called, if it
  931. * recognizes itself as the layer being removed, then it cycles through
  932. * its own personal list of popups, removing them from the map.
  933. *
  934. * Parameters:
  935. * layer - {<OpenLayers.Layer>}
  936. * setNewBaseLayer - {Boolean} Default is true
  937. */
  938. removeLayer: function(layer, setNewBaseLayer) {
  939. if (this.events.triggerEvent("preremovelayer", {layer: layer}) === false) {
  940. return;
  941. }
  942. if (setNewBaseLayer == null) {
  943. setNewBaseLayer = true;
  944. }
  945. if (layer.isFixed) {
  946. this.viewPortDiv.removeChild(layer.div);
  947. } else {
  948. this.layerContainerDiv.removeChild(layer.div);
  949. }
  950. OpenLayers.Util.removeItem(this.layers, layer);
  951. layer.removeMap(this);
  952. layer.map = null;
  953. // if we removed the base layer, need to set a new one
  954. if(this.baseLayer == layer) {
  955. this.baseLayer = null;
  956. if(setNewBaseLayer) {
  957. for(var i=0, len=this.layers.length; i<len; i++) {
  958. var iLayer = this.layers[i];
  959. if (iLayer.isBaseLayer || this.allOverlays) {
  960. this.setBaseLayer(iLayer);
  961. break;
  962. }
  963. }
  964. }
  965. }
  966. this.resetLayersZIndex();
  967. this.events.triggerEvent("removelayer", {layer: layer});
  968. layer.events.triggerEvent("removed", {map: this, layer: layer});
  969. },
  970. /**
  971. * APIMethod: getNumLayers
  972. *
  973. * Returns:
  974. * {Int} The number of layers attached to the map.
  975. */
  976. getNumLayers: function () {
  977. return this.layers.length;
  978. },
  979. /**
  980. * APIMethod: getLayerIndex
  981. *
  982. * Parameters:
  983. * layer - {<OpenLayers.Layer>}
  984. *
  985. * Returns:
  986. * {Integer} The current (zero-based) index of the given layer in the map's
  987. * layer stack. Returns -1 if the layer isn't on the map.
  988. */
  989. getLayerIndex: function (layer) {
  990. return OpenLayers.Util.indexOf(this.layers, layer);
  991. },
  992. /**
  993. * APIMethod: setLayerIndex
  994. * Move the given layer to the specified (zero-based) index in the layer
  995. * list, changing its z-index in the map display. Use
  996. * map.getLayerIndex() to find out the current index of a layer. Note
  997. * that this cannot (or at least should not) be effectively used to
  998. * raise base layers above overlays.
  999. *
  1000. * Parameters:
  1001. * layer - {<OpenLayers.Layer>}
  1002. * idx - {int}
  1003. */
  1004. setLayerIndex: function (layer, idx) {
  1005. var base = this.getLayerIndex(layer);
  1006. if (idx < 0) {
  1007. idx = 0;
  1008. } else if (idx > this.layers.length) {
  1009. idx = this.layers.length;
  1010. }
  1011. if (base != idx) {
  1012. this.layers.splice(base, 1);
  1013. this.layers.splice(idx, 0, layer);
  1014. for (var i=0, len=this.layers.length; i<len; i++) {
  1015. this.setLayerZIndex(this.layers[i], i);
  1016. }
  1017. this.events.triggerEvent("changelayer", {
  1018. layer: layer, property: "order"
  1019. });
  1020. if(this.allOverlays) {
  1021. if(idx === 0) {
  1022. this.setBaseLayer(layer);
  1023. } else if(this.baseLayer !== this.layers[0]) {
  1024. this.setBaseLayer(this.layers[0]);
  1025. }
  1026. }
  1027. }
  1028. },
  1029. /**
  1030. * APIMethod: raiseLayer
  1031. * Change the index of the given layer by delta. If delta is positive,
  1032. * the layer is moved up the map's layer stack; if delta is negative,
  1033. * the layer is moved down. Again, note that this cannot (or at least
  1034. * should not) be effectively used to raise base layers above overlays.
  1035. *
  1036. * Paremeters:
  1037. * layer - {<OpenLayers.Layer>}
  1038. * delta - {int}
  1039. */
  1040. raiseLayer: function (layer, delta) {
  1041. var idx = this.getLayerIndex(layer) + delta;
  1042. this.setLayerIndex(layer, idx);
  1043. },
  1044. /**
  1045. * APIMethod: setBaseLayer
  1046. * Allows user to specify one of the currently-loaded layers as the Map's
  1047. * new base layer.
  1048. *
  1049. * Parameters:
  1050. * newBaseLayer - {<OpenLayers.Layer>}
  1051. */
  1052. setBaseLayer: function(newBaseLayer) {
  1053. if (newBaseLayer != this.baseLayer) {
  1054. // ensure newBaseLayer is already loaded
  1055. if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {
  1056. // preserve center and scale when changing base layers
  1057. var center = this.getCachedCenter();
  1058. var newResolution = OpenLayers.Util.getResolutionFromScale(
  1059. this.getScale(), newBaseLayer.units
  1060. );
  1061. // make the old base layer invisible
  1062. if (this.baseLayer != null && !this.allOverlays) {
  1063. this.baseLayer.setVisibility(false);
  1064. }
  1065. // set new baselayer
  1066. this.baseLayer = newBaseLayer;
  1067. // Increment viewRequestID since the baseLayer is
  1068. // changing. This is used by tiles to check if they should
  1069. // draw themselves.
  1070. this.viewRequestID++;
  1071. if(!this.allOverlays || this.baseLayer.visibility) {
  1072. this.baseLayer.setVisibility(true);
  1073. }
  1074. // recenter the map
  1075. if (center != null) {
  1076. // new zoom level derived from old scale
  1077. var newZoom = this.getZoomForResolution(
  1078. newResolution || this.resolution, true
  1079. );
  1080. // zoom and force zoom change
  1081. this.setCenter(center, newZoom, false, true);
  1082. }
  1083. this.events.triggerEvent("changebaselayer", {
  1084. layer: this.baseLayer
  1085. });
  1086. }
  1087. }
  1088. },
  1089. /********************************************************/
  1090. /* */
  1091. /* Control Functions */
  1092. /* */
  1093. /* The following functions deal with adding and */
  1094. /* removing Controls to and from the Map */
  1095. /* */
  1096. /********************************************************/
  1097. /**
  1098. * APIMethod: addControl
  1099. * Add the passed over control to the map. Optionally
  1100. * position the control at the given pixel.
  1101. *
  1102. * Parameters:
  1103. * control - {<OpenLayers.Control>}
  1104. * px - {<OpenLayers.Pixel>}
  1105. */
  1106. addControl: function (control, px) {
  1107. this.controls.push(control);
  1108. this.addControlToMap(control, px);
  1109. },
  1110. /**
  1111. * APIMethod: addControls
  1112. * Add all of the passed over controls to the map.
  1113. * You can pass over an optional second array
  1114. * with pixel-objects to position the controls.
  1115. * The indices of the two arrays should match and
  1116. * you can add null as pixel for those controls
  1117. * you want to be autopositioned.
  1118. *
  1119. * Parameters:
  1120. * controls - {Array(<OpenLayers.Control>)}
  1121. * pixels - {Array(<OpenLayers.Pixel>)}
  1122. */
  1123. addControls: function (controls, pixels) {
  1124. var pxs = (arguments.length === 1) ? [] : pixels;
  1125. for (var i=0, len=controls.length; i<len; i++) {
  1126. var ctrl = controls[i];
  1127. var px = (pxs[i]) ? pxs[i] : null;
  1128. this.addControl( ctrl, px );
  1129. }
  1130. },
  1131. /**
  1132. * Method: addControlToMap
  1133. *
  1134. * Parameters:
  1135. *
  1136. * control - {<OpenLayers.Control>}
  1137. * px - {<OpenLayers.Pixel>}
  1138. */
  1139. addControlToMap: function (control, px) {
  1140. // If a control doesn't have a div at this point, it belongs in the
  1141. // viewport.
  1142. control.outsideViewport = (control.div != null);
  1143. // If the map has a displayProjection, and the control doesn't, set
  1144. // the display projection.
  1145. if (this.displayProjection && !control.displayProjection) {
  1146. control.displayProjection = this.displayProjection;
  1147. }
  1148. control.setMap(this);
  1149. var div = control.draw(px);
  1150. if (div) {
  1151. if(!control.outsideViewport) {
  1152. div.style.zIndex = this.Z_INDEX_BASE['Control'] +
  1153. this.controls.length;
  1154. this.viewPortDiv.appendChild( div );
  1155. }
  1156. }
  1157. if(control.autoActivate) {
  1158. control.activate();
  1159. }
  1160. },
  1161. /**
  1162. * APIMethod: getControl
  1163. *
  1164. * Parameters:
  1165. * id - {String} ID of the control to return.
  1166. *
  1167. * Returns:
  1168. * {<OpenLayers.Control>} The control from the map's list of controls
  1169. * which has a matching 'id'. If none found,
  1170. * returns null.
  1171. */
  1172. getControl: function (id) {
  1173. var returnControl = null;
  1174. for(var i=0, len=this.controls.length; i<len; i++) {
  1175. var control = this.controls[i];
  1176. if (control.id == id) {
  1177. returnControl = control;
  1178. break;
  1179. }
  1180. }
  1181. return returnControl;
  1182. },
  1183. /**
  1184. * APIMethod: removeControl
  1185. * Remove a control from the map. Removes the control both from the map
  1186. * object's internal array of controls, as well as from the map's
  1187. * viewPort (assuming the control was not added outsideViewport)
  1188. *
  1189. * Parameters:
  1190. * control - {<OpenLayers.Control>} The control to remove.
  1191. */
  1192. removeControl: function (control) {
  1193. //make sure control is non-null and actually part of our map
  1194. if ( (control) && (control == this.getControl(control.id)) ) {
  1195. if (control.div && (control.div.parentNode == this.viewPortDiv)) {
  1196. this.viewPortDiv.removeChild(control.div);
  1197. }
  1198. OpenLayers.Util.removeItem(this.controls, control);
  1199. }
  1200. },
  1201. /********************************************************/
  1202. /* */
  1203. /* Popup Functions */
  1204. /* */
  1205. /* The following functions deal with adding and */
  1206. /* removing Popups to and from the Map */
  1207. /* */
  1208. /********************************************************/
  1209. /**
  1210. * APIMethod: addPopup
  1211. *
  1212. * Parameters:
  1213. * popup - {<OpenLayers.Popup>}
  1214. * exclusive - {Boolean} If true, closes all other popups first
  1215. */
  1216. addPopup: function(popup, exclusive) {
  1217. if (exclusive) {
  1218. //remove all other popups from screen
  1219. for (var i = this.popups.length - 1; i >= 0; --i) {
  1220. this.removePopup(this.popups[i]);
  1221. }
  1222. }
  1223. popup.map = this;
  1224. this.popups.push(popup);
  1225. var popupDiv = popup.draw();
  1226. if (popupDiv) {
  1227. popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +
  1228. this.popups.length;
  1229. this.layerContainerDiv.appendChild(popupDiv);
  1230. }
  1231. },
  1232. /**
  1233. * APIMethod: removePopup
  1234. *
  1235. * Parameters:
  1236. * popup - {<OpenLayers.Popup>}
  1237. */
  1238. removePopup: function(popup) {
  1239. OpenLayers.Util.removeItem(this.popups, popup);
  1240. if (popup.div) {
  1241. try { this.layerContainerDiv.removeChild(popup.div); }
  1242. catch (e) { } // Popups sometimes apparently get disconnected
  1243. // from the layerContainerDiv, and cause complaints.
  1244. }
  1245. popup.map = null;
  1246. },
  1247. /********************************************************/
  1248. /* */
  1249. /* Container Div Functions */
  1250. /* */
  1251. /* The following functions deal with the access to */
  1252. /* and maintenance of the size of the container div */
  1253. /* */
  1254. /********************************************************/
  1255. /**
  1256. * APIMethod: getSize
  1257. *
  1258. * Returns:
  1259. * {<OpenLayers.Size>} An <OpenLayers.Size> object that represents the
  1260. * size, in pixels, of the div into which OpenLayers
  1261. * has been loaded.
  1262. * Note - A clone() of this locally cached variable is
  1263. * returned, so as not to allow users to modify it.
  1264. */
  1265. getSize: function () {
  1266. var size = null;
  1267. if (this.size != null) {
  1268. size = this.size.clone();
  1269. }
  1270. return size;
  1271. },
  1272. /**
  1273. * APIMethod: updateSize
  1274. * This function should be called by any external code which dynamically
  1275. * changes the size of the map div (because mozilla wont let us catch
  1276. * the "onresize" for an element)
  1277. */
  1278. updateSize: function() {
  1279. // the div might have moved on the page, also
  1280. var newSize = this.getCurrentSize();
  1281. if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) {
  1282. this.events.clearMouseCache();
  1283. var oldSize = this.getSize();
  1284. if (oldSize == null) {
  1285. this.size = oldSize = newSize;
  1286. }
  1287. if (!newSize.equals(oldSize)) {
  1288. // store the new size
  1289. this.size = newSize;
  1290. //notify layers of mapresize
  1291. for(var i=0, len=this.layers.length; i<len; i++) {
  1292. this.layers[i].onMapResize();
  1293. }
  1294. var center = this.getCachedCenter();
  1295. if (this.baseLayer != null && center != null) {
  1296. var zoom = this.getZoom();
  1297. this.zoom = null;
  1298. this.setCenter(center, zoom);
  1299. }
  1300. }
  1301. }
  1302. },
  1303. /**
  1304. * Method: getCurrentSize
  1305. *
  1306. * Returns:
  1307. * {<OpenLayers.Size>} A new <OpenLayers.Size> object with the dimensions
  1308. * of the map div
  1309. */
  1310. getCurrentSize: function() {
  1311. var size = new OpenLayers.Size(this.div.clientWidth,
  1312. this.div.clientHeight);
  1313. if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
  1314. size.w = this.div.offsetWidth;
  1315. size.h = this.div.offsetHeight;
  1316. }
  1317. if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
  1318. size.w = parseInt(this.div.style.width);
  1319. size.h = parseInt(this.div.style.height);
  1320. }
  1321. return size;
  1322. },
  1323. /**
  1324. * Method: calculateBounds
  1325. *
  1326. * Parameters:
  1327. * center - {<OpenLayers.LonLat>} Default is this.getCenter()
  1328. * resolution - {float} Default is this.getResolution()
  1329. *
  1330. * Returns:
  1331. * {<OpenLayers.Bounds>} A bounds based on resolution, center, and
  1332. * current mapsize.
  1333. */
  1334. calculateBounds: function(center, resolution) {
  1335. var extent = null;
  1336. if (center == null) {
  1337. center = this.getCachedCenter();
  1338. }
  1339. if (resolution == null) {
  1340. resolution = this.getResolution();
  1341. }
  1342. if ((center != null) && (resolution != null)) {
  1343. var size = this.getSize();
  1344. var w_deg = size.w * resolution;
  1345. var h_deg = size.h * resolution;
  1346. extent = new OpenLayers.Bounds(center.lon - w_deg / 2,
  1347. center.lat - h_deg / 2,
  1348. center.lon + w_deg / 2,
  1349. center.lat + h_deg / 2);
  1350. }
  1351. return extent;
  1352. },
  1353. /********************************************************/
  1354. /* */
  1355. /* Zoom, Center, Pan Functions */
  1356. /* */
  1357. /* The following functions handle the validation, */
  1358. /* getting and setting of the Zoom Level and Center */
  1359. /* as well as the panning of the Map */
  1360. /* */
  1361. /********************************************************/
  1362. /**
  1363. * APIMethod: getCenter
  1364. *
  1365. * Returns:
  1366. * {<OpenLayers.LonLat>}
  1367. */
  1368. getCenter: function () {
  1369. var center = null;
  1370. var cachedCenter = this.getCachedCenter();
  1371. if (cachedCenter) {
  1372. center = cachedCenter.clone();
  1373. }
  1374. return center;
  1375. },
  1376. /**
  1377. * Method: getCachedCenter
  1378. *
  1379. * Returns:
  1380. * {<OpenLayers.LonLat>}
  1381. */
  1382. getCachedCenter: function() {
  1383. if (!this.center && this.size) {
  1384. this.center = this.getLonLatFromViewPortPx(
  1385. new OpenLayers.Pixel(this.size.w / 2, this.size.h / 2)
  1386. );
  1387. }
  1388. return this.center;
  1389. },
  1390. /**
  1391. * APIMethod: getZoom
  1392. *
  1393. * Returns:
  1394. * {Integer}
  1395. */
  1396. getZoom: function () {
  1397. return this.zoom;
  1398. },
  1399. /**
  1400. * APIMethod: pan
  1401. * Allows user to pan by a value of screen pixels
  1402. *
  1403. * Parameters:
  1404. * dx - {Integer}
  1405. * dy - {Integer}
  1406. * options - {Object} Options to configure panning:
  1407. * - *animate* {Boolean} Use panTo instead of setCenter. Default is true.
  1408. * - *dragging* {Boolean} Call setCenter with dragging true. Default is
  1409. * false.
  1410. */
  1411. pan: function(dx, dy, options) {
  1412. options = OpenLayers.Util.applyDefaults(options, {
  1413. animate: true,
  1414. dragging: false
  1415. });
  1416. if (options.dragging) {
  1417. if (dx != 0 || dy != 0) {
  1418. this.moveByPx(dx, dy);
  1419. }
  1420. } else {
  1421. // getCenter
  1422. var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter());
  1423. // adjust
  1424. var newCenterPx = centerPx.add(dx, dy);
  1425. if (this.dragging || !newCenterPx.equals(centerPx)) {
  1426. var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
  1427. if (options.animate) {
  1428. this.panTo(newCenterLonLat);
  1429. } else {
  1430. this.moveTo(newCenterLonLat);
  1431. this.dragging = false;
  1432. this.events.triggerEvent("moveend");
  1433. }
  1434. }
  1435. }
  1436. },
  1437. /**
  1438. * APIMethod: panTo
  1439. * Allows user to pan to a new lonlat
  1440. * If the new lonlat is in the current extent the map will slide smoothly
  1441. *
  1442. * Parameters:
  1443. * lonlat - {<OpenLayers.LonLat>}
  1444. */
  1445. panTo: function(lonlat) {
  1446. if (this.panMethod && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {
  1447. if (!this.panTween) {
  1448. this.panTween = new OpenLayers.Tween(this.panMethod);
  1449. }
  1450. var center = this.getCachedCenter();
  1451. // center will not change, don't do nothing
  1452. if (lonlat.equals(center)) {
  1453. return;
  1454. }
  1455. var from = this.getPixelFromLonLat(center);
  1456. var to = this.getPixelFromLonLat(lonlat);
  1457. var vector = { x: to.x - from.x, y: to.y - from.y };
  1458. var last = { x: 0, y: 0 };
  1459. this.panTween.start( { x: 0, y: 0 }, vector, this.panDuration, {
  1460. callbacks: {
  1461. eachStep: OpenLayers.Function.bind(function(px) {
  1462. var x = px.x - last.x,
  1463. y = px.y - last.y;
  1464. this.moveByPx(x, y);
  1465. last.x = Math.round(px.x);
  1466. last.y = Math.round(px.y);
  1467. }, this),
  1468. done: OpenLayers.Function.bind(function(px) {
  1469. this.moveTo(lonlat);
  1470. this.dragging = false;
  1471. this.events.triggerEvent("moveend");
  1472. }, this)
  1473. }
  1474. });
  1475. } else {
  1476. this.setCenter(lonlat);
  1477. }
  1478. },
  1479. /**
  1480. * APIMethod: setCenter
  1481. * Set the map center (and optionally, the zoom level).
  1482. *
  1483. * Parameters:
  1484. * lonlat - {<OpenLayers.LonLat>} The new center location.
  1485. * zoom - {Integer} Optional zoom level.
  1486. * dragging - {Boolean} Specifies whether or not to trigger
  1487. * movestart/end events
  1488. * forceZoomChange - {Boolean} Specifies whether or not to trigger zoom
  1489. * change events (needed on baseLayer change)
  1490. *
  1491. * TBD: reconsider forceZoomChange in 3.0
  1492. */
  1493. setCenter: function(lonlat, zoom, dragging, forceZoomChange) {
  1494. this.panTween && this.panTween.stop();
  1495. this.moveTo(lonlat, zoom, {
  1496. 'dragging': dragging,
  1497. 'forceZoomChange': forceZoomChange
  1498. });
  1499. },
  1500. /**
  1501. * Method: moveByPx
  1502. * Drag the map by pixels.
  1503. *
  1504. * Parameters:
  1505. * dx - {Number}
  1506. * dy - {Number}
  1507. */
  1508. moveByPx: function(dx, dy) {
  1509. var hw = this.size.w / 2;
  1510. var hh = this.size.h / 2;
  1511. var x = hw + dx;
  1512. var y = hh + dy;
  1513. var wrapDateLine = this.baseLayer.wrapDateLine;
  1514. var xRestriction = 0;
  1515. var yRestriction = 0;
  1516. if (this.restrictedExtent) {
  1517. xRestriction = hw;
  1518. yRestriction = hh;
  1519. // wrapping the date line makes no sense for restricted extents
  1520. wrapDateLine = false;
  1521. }
  1522. dx = wrapDateLine ||
  1523. x <= this.maxPx.x - xRestriction &&
  1524. x >= this.minPx.x + xRestriction ? Math.round(dx) : 0;
  1525. dy = y <= this.maxPx.y - yRestriction &&
  1526. y >= this.minPx.y + yRestriction ? Math.round(dy) : 0;
  1527. var minX = this.minPx.x, maxX = this.maxPx.x;
  1528. if (dx || dy) {
  1529. if (!this.dragging) {
  1530. this.dragging = true;
  1531. this.events.triggerEvent("movestart");
  1532. }
  1533. this.center = null;
  1534. if (dx) {
  1535. this.layerContainerDiv.style.left =
  1536. parseInt(this.layerContainerDiv.style.left) - dx + "px";
  1537. this.minPx.x -= dx;
  1538. this.maxPx.x -= dx;
  1539. if (wrapDateLine) {
  1540. if (this.maxPx.x > maxX) {
  1541. this.maxPx.x -= (maxX - minX);
  1542. }
  1543. if (this.minPx.x < minX) {
  1544. this.minPx.x += (maxX - minX);
  1545. }
  1546. }
  1547. }
  1548. if (dy) {
  1549. this.layerContainerDiv.style.top =
  1550. parseInt(this.layerContainerDiv.style.top) - dy + "px";
  1551. this.minPx.y -= dy;
  1552. this.maxPx.y -= dy;
  1553. }
  1554. var layer, i, len;
  1555. for (i=0, len=this.layers.length; i<len; ++i) {
  1556. layer = this.layers[i];
  1557. if (layer.visibility &&
  1558. (layer === this.baseLayer || layer.inRange)) {
  1559. layer.moveByPx(dx, dy);
  1560. layer.events.triggerEvent("move");
  1561. }
  1562. }
  1563. this.events.triggerEvent("move");
  1564. }
  1565. },
  1566. /**
  1567. * Method: moveTo
  1568. *
  1569. * Parameters:
  1570. * lonlat - {<OpenLayers.LonLat>}
  1571. * zoom - {Integer}
  1572. * options - {Object}
  1573. */
  1574. moveTo: function(lonlat, zoom, options) {
  1575. if (!options) {
  1576. options = {};
  1577. }
  1578. if (zoom != null) {
  1579. zoom = parseFloat(zoom);
  1580. if (!this.fractionalZoom) {
  1581. zoom = Math.round(zoom);
  1582. }
  1583. }
  1584. // dragging is false by default
  1585. var dragging = options.dragging || this.dragging;
  1586. // forceZoomChange is false by default
  1587. var forceZoomChange = options.forceZoomChange;
  1588. if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) {
  1589. lonlat = this.maxExtent.getCenterLonLat();
  1590. this.center = lonlat.clone();
  1591. }
  1592. if(this.restrictedExtent != null) {
  1593. // In 3.0, decide if we want to change interpretation of maxExtent.
  1594. if(lonlat == null) {
  1595. lonlat = this.center;
  1596. }
  1597. if(zoom == null) {
  1598. zoom = this.getZoom();
  1599. }
  1600. var resolution = this.getResolutionForZoom(zoom);
  1601. var extent = this.calculateBounds(lonlat, resolution);
  1602. if(!this.restrictedExtent.containsBounds(extent)) {
  1603. var maxCenter = this.restrictedExtent.getCenterLonLat();
  1604. if(extent.getWidth() > this.restrictedExtent.getWidth()) {
  1605. lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat);
  1606. } else if(extent.left < this.restrictedExtent.left) {
  1607. lonlat = lonlat.add(this.restrictedExtent.left -
  1608. extent.left, 0);
  1609. } else if(extent.right > this.restrictedExtent.right) {
  1610. lonlat = lonlat.add(this.restrictedExtent.right -
  1611. extent.right, 0);
  1612. }
  1613. if(extent.getHeight() > this.restrictedExtent.getHeight()) {
  1614. lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat);
  1615. } else if(extent.bottom < this.restrictedExtent.bottom) {
  1616. lonlat = lonlat.add(0, this.restrictedExtent.bottom -
  1617. extent.bottom);
  1618. }
  1619. else if(extent.top > this.restrictedExtent.top) {
  1620. lonlat = lonlat.add(0, this.restrictedExtent.top -
  1621. extent.top);
  1622. }
  1623. }
  1624. }
  1625. var zoomChanged = forceZoomChange || (
  1626. (this.isValidZoomLevel(zoom)) &&
  1627. (zoom != this.getZoom()) );
  1628. var centerChanged = (this.isValidLonLat(lonlat)) &&
  1629. (!lonlat.equals(this.center));
  1630. // if neither center nor zoom will change, no need to do anything
  1631. if (zoomChanged || centerChanged || dragging) {
  1632. dragging || this.events.triggerEvent("movestart");
  1633. if (centerChanged) {
  1634. if (!zoomChanged && this.center) {
  1635. // if zoom hasnt changed, just slide layerContainer
  1636. // (must be done before setting this.center to new value)
  1637. this.centerLayerContainer(lonlat);
  1638. }
  1639. this.center = lonlat.clone();
  1640. }
  1641. var res = zoomChanged ?
  1642. this.getResolutionForZoom(zoom) : this.getResolution();
  1643. // (re)set the layerContainerDiv's location
  1644. if (zoomChanged || this.layerContainerOrigin == null) {
  1645. this.layerContainerOrigin = this.getCachedCenter();
  1646. this.layerContainerDiv.style.left = "0px";
  1647. this.layerContainerDiv.style.top = "0px";
  1648. var maxExtent = this.getMaxExtent({restricted: true});
  1649. var maxExtentCenter = maxExtent.getCenterLonLat();
  1650. var lonDelta = this.center.lon - maxExtentCenter.lon;
  1651. var latDelta = maxExtentCenter.lat - this.center.lat;
  1652. var extentWidth = Math.round(maxExtent.getWidth() / res);
  1653. var extentHeight = Math.round(maxExtent.getHeight() / res);
  1654. var left = (this.size.w - extentWidth) / 2 - lonDelta / res;
  1655. var top = (this.size.h - extentHeight) / 2 - latDelta / res;
  1656. this.minPx = new OpenLayers.Pixel(left, top);
  1657. this.maxPx = new OpenLayers.Pixel(left + extentWidth, top + extentHeight);
  1658. }
  1659. if (zoomChanged) {
  1660. this.zoom = zoom;
  1661. this.resolution = res;
  1662. // zoom level has changed, increment viewRequestID.
  1663. this.viewRequestID++;
  1664. }
  1665. var bounds = this.getExtent();
  1666. //send the move call to the baselayer and all the overlays
  1667. if(this.baseLayer.visibility) {
  1668. this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);
  1669. options.dragging || this.baseLayer.events.triggerEvent(
  1670. "moveend", {zoomChanged: zoomChanged}
  1671. );
  1672. }
  1673. bounds = this.baseLayer.getExtent();
  1674. for (var i=this.layers.length-1; i>=0; --i) {
  1675. var layer = this.layers[i];
  1676. if (layer !== this.baseLayer && !layer.isBaseLayer) {
  1677. var inRange = layer.calculateInRange();
  1678. if (layer.inRange != inRange) {
  1679. // the inRange property has changed. If the layer is
  1680. // no longer in range, we turn it off right away. If
  1681. // the layer is no longer out of range, the moveTo
  1682. // call below will turn on the layer.
  1683. layer.inRange = inRange;
  1684. if (!inRange) {
  1685. layer.display(false);
  1686. }
  1687. this.events.triggerEvent("changelayer", {
  1688. layer: layer, property: "visibility"
  1689. });
  1690. }
  1691. if (inRange && layer.visibility) {
  1692. layer.moveTo(bounds, zoomChanged, options.dragging);
  1693. options.dragging || layer.events.triggerEvent(
  1694. "moveend", {zoomChanged: zoomChanged}
  1695. );
  1696. }
  1697. }
  1698. }
  1699. this.events.triggerEvent("move");
  1700. dragging || this.events.triggerEvent("moveend");
  1701. if (zoomChanged) {
  1702. //redraw popups
  1703. for (var i=0, len=this.popups.length; i<len; i++) {
  1704. this.popups[i].updatePosition();
  1705. }
  1706. this.events.triggerEvent("zoomend");
  1707. }
  1708. }
  1709. },
  1710. /**
  1711. * Method: centerLayerContainer
  1712. * This function takes care to recenter the layerContainerDiv.
  1713. *
  1714. * Parameters:
  1715. * lonlat - {<OpenLayers.LonLat>}
  1716. */
  1717. centerLayerContainer: function (lonlat) {
  1718. var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
  1719. var newPx = this.getViewPortPxFromLonLat(lonlat);
  1720. if ((originPx != null) && (newPx != null)) {
  1721. var oldLeft = parseInt(this.layerContainerDiv.style.left);
  1722. var oldTop = parseInt(this.layerContainerDiv.style.top);
  1723. var newLeft = Math.round(originPx.x - newPx.x);
  1724. var newTop = Math.round(originPx.y - newPx.y);
  1725. this.layerContainerDiv.style.left = newLeft + "px";
  1726. this.layerContainerDiv.style.top = newTop + "px";
  1727. var dx = oldLeft - newLeft;
  1728. var dy = oldTop - newTop;
  1729. this.minPx.x -= dx;
  1730. this.maxPx.x -= dx;
  1731. this.minPx.y -= dy;
  1732. this.maxPx.y -= dy;
  1733. }
  1734. },
  1735. /**
  1736. * Method: isValidZoomLevel
  1737. *
  1738. * Parameters:
  1739. * zoomLevel - {Integer}
  1740. *
  1741. * Returns:
  1742. * {Boolean} Whether or not the zoom level passed in is non-null and
  1743. * within the min/max range of zoom levels.
  1744. */
  1745. isValidZoomLevel: function(zoomLevel) {
  1746. return ( (zoomLevel != null) &&
  1747. (zoomLevel >= 0) &&
  1748. (zoomLevel < this.getNumZoomLevels()) );
  1749. },
  1750. /**
  1751. * Method: isValidLonLat
  1752. *
  1753. * Parameters:
  1754. * lonlat - {<OpenLayers.LonLat>}
  1755. *
  1756. * Returns:
  1757. * {Boolean} Whether or not the lonlat passed in is non-null and within
  1758. * the maxExtent bounds
  1759. */
  1760. isValidLonLat: function(lonlat) {
  1761. var valid = false;
  1762. if (lonlat != null) {
  1763. var maxExtent = this.getMaxExtent();
  1764. valid = maxExtent.containsLonLat(lonlat);
  1765. }
  1766. return valid;
  1767. },
  1768. /********************************************************/
  1769. /* */
  1770. /* Layer Options */
  1771. /* */
  1772. /* Accessor functions to Layer Options parameters */
  1773. /* */
  1774. /********************************************************/
  1775. /**
  1776. * APIMethod: getProjection
  1777. * This method returns a string representing the projection. In
  1778. * the case of projection support, this will be the srsCode which
  1779. * is loaded -- otherwise it will simply be the string value that
  1780. * was passed to the projection at startup.
  1781. *
  1782. * FIXME: In 3.0, we will remove getProjectionObject, and instead
  1783. * return a Projection object from this function.
  1784. *
  1785. * Returns:
  1786. * {String} The Projection string from the base layer or null.
  1787. */
  1788. getProjection: function() {
  1789. var projection = this.getProjectionObject();
  1790. return projection ? projection.getCode() : null;
  1791. },
  1792. /**
  1793. * APIMethod: getProjectionObject
  1794. * Returns the projection obect from the baselayer.
  1795. *
  1796. * Returns:
  1797. * {<OpenLayers.Projection>} The Projection of the base layer.
  1798. */
  1799. getProjectionObject: function() {
  1800. var projection = null;
  1801. if (this.baseLayer != null) {
  1802. projection = this.baseLayer.projection;
  1803. }
  1804. return projection;
  1805. },
  1806. /**
  1807. * APIMethod: getMaxResolution
  1808. *
  1809. * Returns:
  1810. * {String} The Map's Maximum Resolution
  1811. */
  1812. getMaxResolution: function() {
  1813. var maxResolution = null;
  1814. if (this.baseLayer != null) {
  1815. maxResolution = this.baseLayer.maxResolution;
  1816. }
  1817. return maxResolution;
  1818. },
  1819. /**
  1820. * APIMethod: getMaxExtent
  1821. *
  1822. * Parameters:
  1823. * options - {Object}
  1824. *
  1825. * Allowed Options:
  1826. * restricted - {Boolean} If true, returns restricted extent (if it is
  1827. * available.)
  1828. *
  1829. * Returns:
  1830. * {<OpenLayers.Bounds>} The maxExtent property as set on the current
  1831. * baselayer, unless the 'restricted' option is set, in which case
  1832. * the 'restrictedExtent' option from the map is returned (if it
  1833. * is set).
  1834. */
  1835. getMaxExtent: function (options) {
  1836. var maxExtent = null;
  1837. if(options && options.restricted && this.restrictedExtent){
  1838. maxExtent = this.restrictedExtent;
  1839. } else if (this.baseLayer != null) {
  1840. maxExtent = this.baseLayer.maxExtent;
  1841. }
  1842. return maxExtent;
  1843. },
  1844. /**
  1845. * APIMethod: getNumZoomLevels
  1846. *
  1847. * Returns:
  1848. * {Integer} The total number of zoom levels that can be displayed by the
  1849. * current baseLayer.
  1850. */
  1851. getNumZoomLevels: function() {
  1852. var numZoomLevels = null;
  1853. if (this.baseLayer != null) {
  1854. numZoomLevels = this.baseLayer.numZoomLevels;
  1855. }
  1856. return numZoomLevels;
  1857. },
  1858. /********************************************************/
  1859. /* */
  1860. /* Baselayer Functions */
  1861. /* */
  1862. /* The following functions, all publicly exposed */
  1863. /* in the API?, are all merely wrappers to the */
  1864. /* the same calls on whatever layer is set as */
  1865. /* the current base layer */
  1866. /* */
  1867. /********************************************************/
  1868. /**
  1869. * APIMethod: getExtent
  1870. *
  1871. * Returns:
  1872. * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
  1873. * bounds of the current viewPort.
  1874. * If no baselayer is set, returns null.
  1875. */
  1876. getExtent: function () {
  1877. var extent = null;
  1878. if (this.baseLayer != null) {
  1879. extent = this.baseLayer.getExtent();
  1880. }
  1881. return extent;
  1882. },
  1883. /**
  1884. * APIMethod: getResolution
  1885. *
  1886. * Returns:
  1887. * {Float} The current resolution of the map.
  1888. * If no baselayer is set, returns null.
  1889. */
  1890. getResolution: function () {
  1891. var resolution = null;
  1892. if (this.baseLayer != null) {
  1893. resolution = this.baseLayer.getResolution();
  1894. } else if(this.allOverlays === true && this.layers.length > 0) {
  1895. // while adding the 1st layer to the map in allOverlays mode,
  1896. // this.baseLayer is not set yet when we need the resolution
  1897. // for calculateInRange.
  1898. resolution = this.layers[0].getResolution();
  1899. }
  1900. return resolution;
  1901. },
  1902. /**
  1903. * APIMethod: getUnits
  1904. *
  1905. * Returns:
  1906. * {Float} The current units of the map.
  1907. * If no baselayer is set, returns null.
  1908. */
  1909. getUnits: function () {
  1910. var units = null;
  1911. if (this.baseLayer != null) {
  1912. units = this.baseLayer.units;
  1913. }
  1914. return units;
  1915. },
  1916. /**
  1917. * APIMethod: getScale
  1918. *
  1919. * Returns:
  1920. * {Float} The current scale denominator of the map.
  1921. * If no baselayer is set, returns null.
  1922. */
  1923. getScale: function () {
  1924. var scale = null;
  1925. if (this.baseLayer != null) {
  1926. var res = this.getResolution();
  1927. var units = this.baseLayer.units;
  1928. scale = OpenLayers.Util.getScaleFromResolution(res, units);
  1929. }
  1930. return scale;
  1931. },
  1932. /**
  1933. * APIMethod: getZoomForExtent
  1934. *
  1935. * Parameters:
  1936. * bounds - {<OpenLayers.Bounds>}
  1937. * closest - {Boolean} Find the zoom level that most closely fits the
  1938. * specified bounds. Note that this may result in a zoom that does
  1939. * not exactly contain the entire extent.
  1940. * Default is false.
  1941. *
  1942. * Returns:
  1943. * {Integer} A suitable zoom level for the specified bounds.
  1944. * If no baselayer is set, returns null.
  1945. */
  1946. getZoomForExtent: function (bounds, closest) {
  1947. var zoom = null;
  1948. if (this.baseLayer != null) {
  1949. zoom = this.baseLayer.getZoomForExtent(bounds, closest);
  1950. }
  1951. return zoom;
  1952. },
  1953. /**
  1954. * APIMethod: getResolutionForZoom
  1955. *
  1956. * Parameter:
  1957. * zoom - {Float}
  1958. *
  1959. * Returns:
  1960. * {Float} A suitable resolution for the specified zoom. If no baselayer
  1961. * is set, returns null.
  1962. */
  1963. getResolutionForZoom: function(zoom) {
  1964. var resolution = null;
  1965. if(this.baseLayer) {
  1966. resolution = this.baseLayer.getResolutionForZoom(zoom);
  1967. }
  1968. return resolution;
  1969. },
  1970. /**
  1971. * APIMethod: getZoomForResolution
  1972. *
  1973. * Parameter:
  1974. * resolution - {Float}
  1975. * closest - {Boolean} Find the zoom level that corresponds to the absolute
  1976. * closest resolution, which may result in a zoom whose corresponding
  1977. * resolution is actually smaller than we would have desired (if this
  1978. * is being called from a getZoomForExtent() call, then this means that
  1979. * the returned zoom index might not actually contain the entire
  1980. * extent specified... but it'll be close).
  1981. * Default is false.
  1982. *
  1983. * Returns:
  1984. * {Integer} A suitable zoom level for the specified resolution.
  1985. * If no baselayer is set, returns null.
  1986. */
  1987. getZoomForResolution: function(resolution, closest) {
  1988. var zoom = null;
  1989. if (this.baseLayer != null) {
  1990. zoom = this.baseLayer.getZoomForResolution(resolution, closest);
  1991. }
  1992. return zoom;
  1993. },
  1994. /********************************************************/
  1995. /* */
  1996. /* Zooming Functions */
  1997. /* */
  1998. /* The following functions, all publicly exposed */
  1999. /* in the API, are all merely wrappers to the */
  2000. /* the setCenter() function */
  2001. /* */
  2002. /********************************************************/
  2003. /**
  2004. * APIMethod: zoomTo
  2005. * Zoom to a specific zoom level
  2006. *
  2007. * Parameters:
  2008. * zoom - {Integer}
  2009. */
  2010. zoomTo: function(zoom) {
  2011. if (this.isValidZoomLevel(zoom)) {
  2012. this.setCenter(null, zoom);
  2013. }
  2014. },
  2015. /**
  2016. * APIMethod: zoomIn
  2017. *
  2018. */
  2019. zoomIn: function() {
  2020. this.zoomTo(this.getZoom() + 1);
  2021. },
  2022. /**
  2023. * APIMethod: zoomOut
  2024. *
  2025. */
  2026. zoomOut: function() {
  2027. this.zoomTo(this.getZoom() - 1);
  2028. },
  2029. /**
  2030. * APIMethod: zoomToExtent
  2031. * Zoom to the passed in bounds, recenter
  2032. *
  2033. * Parameters:
  2034. * bounds - {<OpenLayers.Bounds>}
  2035. * closest - {Boolean} Find the zoom level that most closely fits the
  2036. * specified bounds. Note that this may result in a zoom that does
  2037. * not exactly contain the entire extent.
  2038. * Default is false.
  2039. *
  2040. */
  2041. zoomToExtent: function(bounds, closest) {
  2042. var center = bounds.getCenterLonLat();
  2043. if (this.baseLayer.wrapDateLine) {
  2044. var maxExtent = this.getMaxExtent();
  2045. //fix straddling bounds (in the case of a bbox that straddles the
  2046. // dateline, it's left and right boundaries will appear backwards.
  2047. // we fix this by allowing a right value that is greater than the
  2048. // max value at the dateline -- this allows us to pass a valid
  2049. // bounds to calculate zoom)
  2050. //
  2051. bounds = bounds.clone();
  2052. while (bounds.right < bounds.left) {
  2053. bounds.right += maxExtent.getWidth();
  2054. }
  2055. //if the bounds was straddling (see above), then the center point
  2056. // we got from it was wrong. So we take our new bounds and ask it
  2057. // for the center. Because our new bounds is at least partially
  2058. // outside the bounds of maxExtent, the new calculated center
  2059. // might also be. We don't want to pass a bad center value to
  2060. // setCenter, so we have it wrap itself across the date line.
  2061. //
  2062. center = bounds.getCenterLonLat().wrapDateLine(maxExtent);
  2063. }
  2064. this.setCenter(center, this.getZoomForExtent(bounds, closest));
  2065. },
  2066. /**
  2067. * APIMethod: zoomToMaxExtent
  2068. * Zoom to the full extent and recenter.
  2069. *
  2070. * Parameters:
  2071. * options -
  2072. *
  2073. * Allowed Options:
  2074. * restricted - {Boolean} True to zoom to restricted extent if it is
  2075. * set. Defaults to true.
  2076. */
  2077. zoomToMaxExtent: function(options) {
  2078. //restricted is true by default
  2079. var restricted = (options) ? options.restricted : true;
  2080. var maxExtent = this.getMaxExtent({
  2081. 'restricted': restricted
  2082. });
  2083. this.zoomToExtent(maxExtent);
  2084. },
  2085. /**
  2086. * APIMethod: zoomToScale
  2087. * Zoom to a specified scale
  2088. *
  2089. * Parameters:
  2090. * scale - {float}
  2091. * closest - {Boolean} Find the zoom level that most closely fits the
  2092. * specified scale. Note that this may result in a zoom that does
  2093. * not exactly contain the entire extent.
  2094. * Default is false.
  2095. *
  2096. */
  2097. zoomToScale: function(scale, closest) {
  2098. var res = OpenLayers.Util.getResolutionFromScale(scale,
  2099. this.baseLayer.units);
  2100. var size = this.getSize();
  2101. var w_deg = size.w * res;
  2102. var h_deg = size.h * res;
  2103. var center = this.getCachedCenter();
  2104. var extent = new OpenLayers.Bounds(center.lon - w_deg / 2,
  2105. center.lat - h_deg / 2,
  2106. center.lon + w_deg / 2,
  2107. center.lat + h_deg / 2);
  2108. this.zoomToExtent(extent, closest);
  2109. },
  2110. /********************************************************/
  2111. /* */
  2112. /* Translation Functions */
  2113. /* */
  2114. /* The following functions translate between */
  2115. /* LonLat, LayerPx, and ViewPortPx */
  2116. /* */
  2117. /********************************************************/
  2118. //
  2119. // TRANSLATION: LonLat <-> ViewPortPx
  2120. //
  2121. /**
  2122. * Method: getLonLatFromViewPortPx
  2123. *
  2124. * Parameters:
  2125. * viewPortPx - {<OpenLayers.Pixel>}
  2126. *
  2127. * Returns:
  2128. * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
  2129. * port <OpenLayers.Pixel>, translated into lon/lat
  2130. * by the current base layer.
  2131. */
  2132. getLonLatFromViewPortPx: function (viewPortPx) {
  2133. var lonlat = null;
  2134. if (this.baseLayer != null) {
  2135. lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);
  2136. }
  2137. return lonlat;
  2138. },
  2139. /**
  2140. * APIMethod: getViewPortPxFromLonLat
  2141. *
  2142. * Parameters:
  2143. * lonlat - {<OpenLayers.LonLat>}
  2144. *
  2145. * Returns:
  2146. * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
  2147. * <OpenLayers.LonLat>, translated into view port
  2148. * pixels by the current base layer.
  2149. */
  2150. getViewPortPxFromLonLat: function (lonlat) {
  2151. var px = null;
  2152. if (this.baseLayer != null) {
  2153. px = this.baseLayer.getViewPortPxFromLonLat(lonlat);
  2154. }
  2155. return px;
  2156. },
  2157. //
  2158. // CONVENIENCE TRANSLATION FUNCTIONS FOR API
  2159. //
  2160. /**
  2161. * APIMethod: getLonLatFromPixel
  2162. *
  2163. * Parameters:
  2164. * px - {<OpenLayers.Pixel>}
  2165. *
  2166. * Returns:
  2167. * {<OpenLayers.LonLat>} An OpenLayers.LonLat corresponding to the given
  2168. * OpenLayers.Pixel, translated into lon/lat by the
  2169. * current base layer
  2170. */
  2171. getLonLatFromPixel: function (px) {
  2172. return this.getLonLatFromViewPortPx(px);
  2173. },
  2174. /**
  2175. * APIMethod: getPixelFromLonLat
  2176. * Returns a pixel location given a map location. The map location is
  2177. * translated to an integer pixel location (in viewport pixel
  2178. * coordinates) by the current base layer.
  2179. *
  2180. * Parameters:
  2181. * lonlat - {<OpenLayers.LonLat>} A map location.
  2182. *
  2183. * Returns:
  2184. * {<OpenLayers.Pixel>} An OpenLayers.Pixel corresponding to the
  2185. * <OpenLayers.LonLat> translated into view port pixels by the current
  2186. * base layer.
  2187. */
  2188. getPixelFromLonLat: function (lonlat) {
  2189. var px = this.getViewPortPxFromLonLat(lonlat);
  2190. px.x = Math.round(px.x);
  2191. px.y = Math.round(px.y);
  2192. return px;
  2193. },
  2194. /**
  2195. * Method: getGeodesicPixelSize
  2196. *
  2197. * Parameters:
  2198. * px - {<OpenLayers.Pixel>} The pixel to get the geodesic length for. If
  2199. * not provided, the center pixel of the map viewport will be used.
  2200. *
  2201. * Returns:
  2202. * {<OpenLayers.Size>} The geodesic size of the pixel in kilometers.
  2203. */
  2204. getGeodesicPixelSize: function(px) {
  2205. var lonlat = px ? this.getLonLatFromPixel(px) : (
  2206. this.getCachedCenter() || new OpenLayers.LonLat(0, 0));
  2207. var res = this.getResolution();
  2208. var left = lonlat.add(-res / 2, 0);
  2209. var right = lonlat.add(res / 2, 0);
  2210. var bottom = lonlat.add(0, -res / 2);
  2211. var top = lonlat.add(0, res / 2);
  2212. var dest = new OpenLayers.Projection("EPSG:4326");
  2213. var source = this.getProjectionObject() || dest;
  2214. if(!source.equals(dest)) {
  2215. left.transform(source, dest);
  2216. right.transform(source, dest);
  2217. bottom.transform(source, dest);
  2218. top.transform(source, dest);
  2219. }
  2220. return new OpenLayers.Size(
  2221. OpenLayers.Util.distVincenty(left, right),
  2222. OpenLayers.Util.distVincenty(bottom, top)
  2223. );
  2224. },
  2225. //
  2226. // TRANSLATION: ViewPortPx <-> LayerPx
  2227. //
  2228. /**
  2229. * APIMethod: getViewPortPxFromLayerPx
  2230. *
  2231. * Parameters:
  2232. * layerPx - {<OpenLayers.Pixel>}
  2233. *
  2234. * Returns:
  2235. * {<OpenLayers.Pixel>} Layer Pixel translated into ViewPort Pixel
  2236. * coordinates
  2237. */
  2238. getViewPortPxFromLayerPx:function(layerPx) {
  2239. var viewPortPx = null;
  2240. if (layerPx != null) {
  2241. var dX = parseInt(this.layerContainerDiv.style.left);
  2242. var dY = parseInt(this.layerContainerDiv.style.top);
  2243. viewPortPx = layerPx.add(dX, dY);
  2244. }
  2245. return viewPortPx;
  2246. },
  2247. /**
  2248. * APIMethod: getLayerPxFromViewPortPx
  2249. *
  2250. * Parameters:
  2251. * viewPortPx - {<OpenLayers.Pixel>}
  2252. *
  2253. * Returns:
  2254. * {<OpenLayers.Pixel>} ViewPort Pixel translated into Layer Pixel
  2255. * coordinates
  2256. */
  2257. getLayerPxFromViewPortPx:function(viewPortPx) {
  2258. var layerPx = null;
  2259. if (viewPortPx != null) {
  2260. var dX = -parseInt(this.layerContainerDiv.style.left);
  2261. var dY = -parseInt(this.layerContainerDiv.style.top);
  2262. layerPx = viewPortPx.add(dX, dY);
  2263. if (isNaN(layerPx.x) || isNaN(layerPx.y)) {
  2264. layerPx = null;
  2265. }
  2266. }
  2267. return layerPx;
  2268. },
  2269. //
  2270. // TRANSLATION: LonLat <-> LayerPx
  2271. //
  2272. /**
  2273. * Method: getLonLatFromLayerPx
  2274. *
  2275. * Parameters:
  2276. * px - {<OpenLayers.Pixel>}
  2277. *
  2278. * Returns:
  2279. * {<OpenLayers.LonLat>}
  2280. */
  2281. getLonLatFromLayerPx: function (px) {
  2282. //adjust for displacement of layerContainerDiv
  2283. px = this.getViewPortPxFromLayerPx(px);
  2284. return this.getLonLatFromViewPortPx(px);
  2285. },
  2286. /**
  2287. * APIMethod: getLayerPxFromLonLat
  2288. *
  2289. * Parameters:
  2290. * lonlat - {<OpenLayers.LonLat>} lonlat
  2291. *
  2292. * Returns:
  2293. * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
  2294. * <OpenLayers.LonLat>, translated into layer pixels
  2295. * by the current base layer
  2296. */
  2297. getLayerPxFromLonLat: function (lonlat) {
  2298. //adjust for displacement of layerContainerDiv
  2299. var px = this.getPixelFromLonLat(lonlat);
  2300. return this.getLayerPxFromViewPortPx(px);
  2301. },
  2302. CLASS_NAME: "OpenLayers.Map"
  2303. });
  2304. /**
  2305. * Constant: TILE_WIDTH
  2306. * {Integer} 256 Default tile width (unless otherwise specified)
  2307. */
  2308. OpenLayers.Map.TILE_WIDTH = 256;
  2309. /**
  2310. * Constant: TILE_HEIGHT
  2311. * {Integer} 256 Default tile height (unless otherwise specified)
  2312. */
  2313. OpenLayers.Map.TILE_HEIGHT = 256;