Map.js 101 KB

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