Reusable Accessible Mapping Platform

API Docs for: 4.0.0
Show:

File: src\js\RAMP\Modules\map.js

  1. /*global define, esri, i18n, console, $, RAMP */
  2.  
  3. /**
  4. *
  5. * A RAMP Map module with ESRI and Dojo AMD Modules
  6. * This module provides function to create an ESRI web map control. A global config object is
  7. * required to initialize the map.
  8. *
  9. * @module RAMP
  10. * @submodule Map
  11. * @main Map
  12. */
  13.  
  14. /**
  15. * Map class represents the ESRI map object. The map is generated based on the application configuration and templates.
  16. *
  17. * @class Map
  18. * @uses dojo/_base/declare
  19. * @uses dojo/_base/array
  20. * @uses dojo/dom
  21. * @uses dojo/dom-construct
  22. * @uses dojo/number
  23. * @uses dojo/query
  24. * @uses dojo/topic
  25. * @uses dojo/on
  26. * @uses esri/map
  27. * @uses esri/layers/FeatureLayer
  28. * @uses esri/layers/ArcGISTiledMapServiceLayer
  29. * @uses esri/layers/ArcGISDynamicMapServiceLayer
  30. * @uses esri/layers/WMSLayer
  31. * @uses esri/SpatialReference
  32. * @uses esri/dijit/Scalebar
  33. * @uses esri/geometry/Extent
  34. * @uses GlobalStorage
  35. * @uses RAMP
  36. * @uses FeatureClickHandler
  37. * @uses Navigation
  38. * @uses EventManager
  39. * @uses Util
  40. * @uses Array
  41. */
  42.  
  43. define([
  44. /* Dojo */
  45. "dojo/_base/declare", "dojo/_base/array", "dojo/dom",
  46. "dojo/dom-construct", "dojo/number", "dojo/query", "dojo/topic", "dojo/on",
  47.  
  48. /* Esri */
  49. "esri/map", "esri/layers/FeatureLayer", "esri/layers/GraphicsLayer", "esri/layers/ArcGISTiledMapServiceLayer", "esri/layers/ArcGISDynamicMapServiceLayer",
  50. "esri/SpatialReference", "esri/dijit/Scalebar", "esri/geometry/Extent", "esri/layers/WMSLayer",
  51.  
  52. /* Ramp */
  53. "ramp/globalStorage", "ramp/ramp", "ramp/featureClickHandler", "ramp/mapClickHandler", "ramp/navigation", "ramp/eventManager",
  54.  
  55. /* Util */
  56. "utils/util", "utils/array", "utils/dictionary"],
  57.  
  58. function (
  59. /* Dojo */
  60. declare, dojoArray, dom, domConstruct, number, query, topic, dojoOn,
  61.  
  62. /* Esri */
  63. EsriMap, FeatureLayer, GraphicsLayer, ArcGISTiledMapServiceLayer, ArcGISDynamicMapServiceLayer,
  64. SpatialReference, EsriScalebar, EsriExtent, WMSLayer,
  65.  
  66. /* Ramp */
  67. GlobalStorage, Ramp, FeatureClickHandler, MapClickHandler, Navigation, EventManager,
  68.  
  69. /* Util */
  70. UtilMisc, UtilArray, UtilDict) {
  71. "use strict";
  72.  
  73. /**
  74. * An Array of {{#crossLink "Esri/layer/FeatureLayer"}}{{/crossLink}} objects.
  75. *
  76. * @private
  77. * @property featureLayers {Array}
  78. */
  79. var featureLayers,
  80.  
  81. /**
  82. * An Array of {{#crossLink "Esri/layer/WMSLayer"}}{{/crossLink}} objects.
  83. *
  84. * @private
  85. * @property wmsLayers {Array}
  86. */
  87. wmsLayers = [],
  88.  
  89. /**
  90. * Maps the id of a graphic layer to the GraphicsLayer Object that represents its extent bounding box.
  91. * A dictionary of String, {{#crossLink "Esri/layer/GraphicsLayer"}}{{/crossLink}} pairs.
  92. *
  93. * @private
  94. * @property boundingBoxMapping {Object}
  95. */
  96. boundingBoxMapping,
  97.  
  98. /**
  99. * The map not only contains feature layers, but also other layers such as the
  100. * basemap layer, highlight layer, bounding box layer, etc. This variable is
  101. * used to store the starting index of the feature layers in the map.
  102. *
  103. * @private
  104. * @property featureLayerStartIndex {Integer}
  105. */
  106. featureLayerStartIndex,
  107.  
  108. map,
  109.  
  110. fullExtent,
  111. maxExtent,
  112. initExtent;
  113.  
  114. /**
  115. * Shows the loading image.
  116. *
  117. * @private
  118. * @method _showLoadingImg
  119. */
  120. function _showLoadingImg() {
  121. $("#map-load-indicator").removeClass("hidden");
  122. }
  123.  
  124. /**
  125. * Hides the loading image.
  126. *
  127. * @private
  128. * @method _hideLoadingImg
  129. */
  130. function _hideLoadingImg() {
  131. $("#map-load-indicator").addClass("hidden");
  132. }
  133.  
  134. /**
  135. * Update Map Scale when zoom level changes
  136. *
  137. * @private
  138. * @method _updateScale
  139. * @param {Object} event
  140. */
  141. function _updateScale(event) {
  142. if (event.levelChange) {
  143. var currentScale = number.format(event.lod.scale),
  144. scaleLabelText = "1 : " + currentScale;
  145.  
  146. domConstruct.empty('scaleLabel');
  147. $("#scaleLabel").text(scaleLabelText);
  148. }
  149. }
  150.  
  151. /**
  152. * Initialize Map Scale
  153. *
  154. * @private
  155. * @method _initScale
  156. * @param {Object} event
  157. */
  158. function _initScale(event) {
  159. var map = event.map,
  160. scaleDiv = domConstruct.create("div", {
  161. id: "scaleDiv",
  162. class: "esriScalebarLabel"
  163. }),
  164. currentScale,
  165. scaleLabelText;
  166. $(scaleDiv).html("<span>" + i18n.t('map.scale') + "</span><br><span id='scaleLabel'><span/>");
  167. currentScale = number.format(map.getScale());
  168. scaleLabelText = "1 : " + currentScale;
  169.  
  170. domConstruct.place(scaleDiv, query(".esriScalebarRuler")[0], "before");
  171. domConstruct.empty('scaleLabel');
  172. $("#scaleLabel").text(scaleLabelText);
  173.  
  174. // Change the css class of the scale bar so it shows up against
  175. // the map
  176. topic.subscribe(EventManager.BasemapSelector.BASEMAP_CHANGED, function (attr) {
  177. $(".esriScalebar > div").removeClass().addClass(attr.cssStyle);
  178. });
  179. }
  180.  
  181. /**
  182. * Republishes map events to the outside using topic.publish
  183. *
  184. * @method _initRepublishers
  185. * @param {Object} map object
  186. * @private
  187. */
  188. function _initRepublishers(map) {
  189. var prefix = "map";
  190.  
  191. /**
  192. * Republish map events using topic.publish
  193. *
  194. * @method republish
  195. * @param {String} name
  196. * @private
  197. */
  198. function republish(name) {
  199. map.on(name, function (event) {
  200. topic.publish(prefix + "/" + name, event);
  201. });
  202. }
  203.  
  204. republish("update-end");
  205. republish("extent-change");
  206. republish("zoom-start");
  207. republish("zoom-end");
  208. republish("pan-start");
  209. republish("pan-end");
  210. }
  211.  
  212. /**
  213. * Subscribe to external events (published using topic.publish)
  214. * and react accordingly
  215. *
  216. * @method _initListeners
  217. * @param {Object} map map object
  218. * @private
  219. */
  220. function _initListeners(map) {
  221. /* SUBSCRIBED EVENTS */
  222. topic.subscribe(EventManager.Map.CENTER_AT, function (event) {
  223. map.centerAt(event.point);
  224. });
  225.  
  226. topic.subscribe(EventManager.Map.CENTER_AND_ZOOM, function (event) {
  227. var point = new esri.geometry.Point(event.graphic.geometry.x, event.graphic.geometry.y, map.spatialReference),
  228. d = map.centerAndZoom(point, event.level); // Last parameter is the level
  229.  
  230. if (event.callback) {
  231. d.then(event.callback);
  232. }
  233. });
  234.  
  235. topic.subscribe(EventManager.Map.SET_EXTENT, function (event) {
  236. event.extent.spatialReference = map.spatialReference;
  237. var d = map.setExtent(event.extent);
  238.  
  239. if (event.callback) {
  240. d.then(event.callback);
  241. }
  242. });
  243.  
  244. /* NAVIGATION EVENTS */
  245. topic.subscribe(EventManager.Navigation.PAN, function (event) {
  246. // event.direction panUp, panDown, panLeft, panRight
  247. // this same as calling map.panUp(), map.panDown(), map.panLeft(), map.panRight()
  248. map[event.direction]();
  249. });
  250.  
  251. topic.subscribe(EventManager.Navigation.ZOOM_STEP, function (event) {
  252. map.setLevel(map.getLevel() + event.level);
  253. });
  254.  
  255. topic.subscribe(EventManager.Navigation.ZOOM, function (event) {
  256. map.setLevel(event.level);
  257. });
  258.  
  259. topic.subscribe(EventManager.Navigation.FULL_EXTENT, function () {
  260. map.setExtent(fullExtent);
  261. });
  262.  
  263. /* GUI EVENTS */
  264. topic.subscribe(EventManager.GUI.LAYOUT_CHANGE, function () {
  265. map.resize(true);
  266. });
  267.  
  268. // Unhighlight the point when the subpanel is collapsed
  269. topic.subscribe(EventManager.GUI.SUBPANEL_CHANGE, function (eventArg) {
  270. // unhighlight only if the panel closing is the details panel
  271. if (!eventArg.visible &&
  272. eventArg.isComplete &&
  273. (eventArg.origin === ("rampPopup" || "datagrid"))) {
  274. topic.publish(EventManager.FeatureHighlighter.HIGHLIGHT_HIDE, {});
  275. }
  276. });
  277.  
  278. /* START BOUNDING BOX TOGGLE */
  279.  
  280. topic.subscribe(EventManager.FilterManager.LAYER_VISIBILITY_TOGGLED, function (evt) {
  281. var setTo = evt.state,
  282. layerId = evt.id,
  283. // either take url (would need mapping to layer on map),
  284. // map id in config, graphic layer id
  285. layer = map.getLayer(layerId);
  286.  
  287. layer.setVisibility(setTo);
  288. //loops through any static layers that are mapped to the feature layer being toggled
  289. try {
  290. dojoArray.forEach(GlobalStorage.LayerMap[layerId], function (staticLayer) {
  291. var layer = map.getLayer(staticLayer);
  292. layer.setVisibility(setTo);
  293. });
  294. }
  295. catch (err) {
  296. }
  297. });
  298.  
  299. topic.subscribe(EventManager.FilterManager.LAYER_TRANSPARENCY_CHANGED, function (evt) {
  300. var layer = map.getLayer(evt.layerId);
  301.  
  302. layer.setOpacity(evt.value);
  303. //loops through any static layers that are mapped to the feature layer being toggled
  304. try {
  305. dojoArray.forEach(GlobalStorage.LayerMap[evt.layerId], function (staticLayer) {
  306. var layer = map.getLayer(staticLayer);
  307. layer.setOpacity(evt.value);
  308. });
  309. }
  310. catch (err) {
  311. }
  312. });
  313.  
  314. topic.subscribe(EventManager.FilterManager.BOX_VISIBILITY_TOGGLED, function (evt) {
  315. setBoundingBoxVisibility(evt.id, evt.state);
  316. });
  317. /* END BOUNDING BOX TOGGLE */
  318.  
  319. topic.subscribe(EventManager.FilterManager.SELECTION_CHANGED, function (evt) {
  320. // this is handling the user trying to re-order the layers
  321. // graphical and feature layers must be handled separately from
  322. // all other layers as ESRI separates the two internally
  323.  
  324. var newIndex = evt.index,
  325. featureLayers;
  326.  
  327. if (map.layerIds.contains(evt.id)) {
  328. featureLayers = dojoArray.map(map.graphicsLayerIds, function (x) {
  329. return map.getLayer(x).type === 'Feature Layer' ? 1 : 0;
  330. }).sum();
  331. newIndex += 1 - featureLayers; // offset by 1 basemap not accounted for
  332. console.log('newIndex ' + newIndex);
  333. console.log(map.layerIds);
  334. } else {
  335. if (!featureLayerStartIndex) {
  336. // Find the index of the first feature layer
  337. featureLayerStartIndex = UtilArray.indexOf(map.graphicsLayerIds, function (layerId) {
  338. var layer = map.getLayer(layerId);
  339. return layer.type && layer.type === "Feature Layer";
  340. });
  341. }
  342. newIndex += featureLayerStartIndex;
  343. }
  344. map.reorderLayer(map.getLayer(evt.id), newIndex);
  345. topic.publish(EventManager.Map.REORDER_END);
  346. });
  347.  
  348. /* Add Layer subscription*/
  349. topic.subscribe(EventManager.Map.ADD_LAYER, function () {
  350. var type = dom.byId("addLayer-select-type").value,
  351. URL = dom.byId("addLayer-URL-input").value,
  352. opacity = dom.byId("addLayer-Opacity").value;
  353.  
  354. console.log(type + " | " + URL + " | " + opacity);
  355. addStaticLayer(type, URL, opacity);
  356. });
  357.  
  358. topic.subscribe(EventManager.Map.ADD_LAYER_READY, function (temp_layer) {
  359. map.addLayer(temp_layer);
  360. });
  361. }
  362. /**
  363. * Creates event handlers for the map control: click, mouse-over, load, extent change, and update events.
  364. *
  365. * @private
  366. * @method _initEventHandlers
  367. * @param {Object} map A ESRI map object
  368. */
  369. function _initEventHandlers(map) {
  370. var handle,
  371. // filter out non static layers for any feature interaction: maptip
  372. nonStaticLayers = dojoArray.filter(featureLayers, function (layer) {
  373. return layer.ramp.type !== GlobalStorage.layerType.Static;
  374. }
  375. );
  376.  
  377. // original value : featureLayers
  378. // updated with nonStaticLayer
  379. dojoArray.forEach(nonStaticLayers, function (fl) {
  380. //TODO: set timer for maptips onMouseOver event
  381.  
  382. fl.on("click", function (evt) {
  383. evt.stopImmediatePropagation();
  384. FeatureClickHandler.onFeatureSelect(evt);
  385. });
  386.  
  387. fl.on("mouse-over", function (evt) {
  388. FeatureClickHandler.onFeatureMouseOver(evt);
  389.  
  390. //console.log("hover on point", evt);
  391. });
  392.  
  393. fl.on("mouse-out", function (evt) {
  394. FeatureClickHandler.onFeatureMouseOut(evt);
  395. });
  396. });
  397.  
  398. map.on("load", _initScale);
  399. map.on("extent-change", function (event) {
  400. _updateScale(event);
  401.  
  402. console.log("map - >> extent-change", event);
  403. dojoOn.once(map, "update-end", function () {
  404. console.log("map - >> update-end - >> Apply extent Filter");
  405. topic.publish(EventManager.Datagrid.APPLY_EXTENT_FILTER);
  406. });
  407. });
  408.  
  409. // Deselect all highlighted points if the map is clicked
  410. map.on("click", function (evt) {
  411. FeatureClickHandler.onFeatureDeselect(evt);
  412. topic.publish(EventManager.Map.CLICK, evt);
  413. });
  414.  
  415. // Hide all the maptips if the map finishes updating
  416. map.on("update-end", function () {
  417. //topic.publish(EventManager.Maptips.HIDE, {});
  418. });
  419.  
  420. // Show/Hide spinner for map loading
  421. map.on("update-start", _showLoadingImg);
  422. map.on("update-end", _hideLoadingImg);
  423.  
  424. handle = map.on("update-end", function () {
  425. var isAllLoaded = dojoArray.every(
  426. map.graphicsLayerIds.concat(map.layerIds),
  427. function (layerId) {
  428. var layer = map.getLayer(layerId);
  429. console.log(layer.loaded, layerId, layer);
  430. return layer.loaded;
  431. }
  432. );
  433.  
  434. console.log("map -> is all layers loaded: ", isAllLoaded);
  435.  
  436. if (isAllLoaded) {
  437. handle.remove();
  438. console.log("map ->", EventManager.Map.ALL_LAYERS_LOADED);
  439. topic.publish(EventManager.Map.ALL_LAYERS_LOADED);
  440. }
  441. });
  442. }
  443.  
  444. /**
  445. * Instantiates an extent from a JSON config object and spatial reference
  446. *
  447. * @private
  448. * @method createExtent
  449. * @param {Object} extentConfig the JSON config object
  450. * @param {Esri/SpatialReference} sr the {{#crossLink "Esri/SpatialReference"}}{{/crossLink}}
  451. * @return {esri/geometry/Extent} An ESRI extent object based on the config data
  452. */
  453. function createExtent(extentConfig, sr) {
  454. return new EsriExtent(
  455. extentConfig.xmin, extentConfig.ymin, extentConfig.xmax, extentConfig.ymax, sr);
  456. }
  457.  
  458. /**
  459. * Add a static, non-interactive Layer to the map
  460. *
  461. * @private
  462. * @method AddStaticLayer
  463. * @param {String} layer_type A value which controls how the layer is going to be added to the map
  464. * @param {String} layer_url A URL pointing to a valid map service endpoint
  465. * @param {Number} layer_op A value between 0.0 and 1.0 which determines the transparency of the layer
  466. */
  467. function addStaticLayer(layer_type, layer_url, layer_op) {
  468. layer_op = layer_op / 100; // change percentage to decimal
  469. var tempLayer;
  470. switch (layer_type) {
  471. case "feature":
  472. tempLayer = new FeatureLayer(layer_url, {
  473. opacity: layer_op,
  474. mode: FeatureLayer.MODE_SNAPSHOT
  475. });
  476. break;
  477.  
  478. case "tile":
  479. tempLayer = new ArcGISTiledMapServiceLayer(layer_url, {
  480. opacity: layer_op
  481. });
  482. break;
  483.  
  484. case "dynamic":
  485. tempLayer = new ArcGISDynamicMapServiceLayer(layer_url, {
  486. opacity: layer_op
  487. });
  488. break;
  489.  
  490. default:
  491.  
  492. break;
  493. }
  494.  
  495. topic.publish(EventManager.Map.ADD_LAYER_READY, tempLayer);
  496. topic.publish(EventManager.GUI.ADD_LAYER_PANEL_CHANGE, {
  497. visible: false
  498. });
  499. }
  500.  
  501. /**
  502. * Sets the visibility of the bounding box that belongs to the layer with the given layerId.
  503. * Note: the layerId needs to be the ID of the featurelayer, not the ID of the actual bounding
  504. * box layer.
  505. *
  506. * @private
  507. * @method setBoundingBoxVisibility
  508. * @param {String} layerId the id of the layer whose bounding box visibility should be set
  509. */
  510. function setBoundingBoxVisibility(layerId, visibility) {
  511. var boxLayer = boundingBoxMapping[layerId];
  512.  
  513. //if (boxLayer.graphics.isEmpty() && visibility) {
  514. // // get bounding box info from config object
  515. // var boundingBoxExtent;
  516. // var layerConfig = dojoArray.find(config.featureLayers, function (layerConfig) {
  517. // return layerConfig.id === layerId;
  518. // });
  519.  
  520. // boundingBoxExtent.xmin = layerConfig.boundingBox.extent.xmin;
  521. // boundingBoxExtent.ymin = layerConfig.boundingBox.extent.ymin;
  522. // boundingBoxExtent.xmax = layerConfig.boundingBox.extent.xmax;
  523. // boundingBoxExtent.ymax = layerConfig.boundingBox.extent.ymax;
  524.  
  525. // var extentGraphic = new esri.Graphic({
  526. // geometry: boundingBoxExtent,
  527. // symbol: {
  528. // color: [255, 0, 0, 64],
  529. // outline: {
  530. // color: [240, 128, 128, 255],
  531. // width: 1,
  532. // type: "esriSLS",
  533. // style: "esriSLSSolid"
  534. // },
  535. // type: "esriSFS",
  536. // style: "esriSFSSolid"
  537. // }
  538. // });
  539. // boxLayer.add(extentGraphic);
  540. //}
  541.  
  542. boxLayer.setVisibility(visibility);
  543. }
  544.  
  545. function resolveLayerOpacity(layerOpacity) {
  546. return layerOpacity.default || 1;
  547. }
  548.  
  549. function generateStaticLayer(staticLayer) {
  550. var tempLayer,
  551. layerType = staticLayer.layerType || "feature";
  552. //determine layer type and process
  553. switch (layerType) {
  554. case "feature":
  555. tempLayer = new FeatureLayer(staticLayer.url, {
  556. opacity: resolveLayerOpacity(staticLayer.settings.opacity),
  557. mode: FeatureLayer.MODE_SNAPSHOT,
  558. id: staticLayer.id
  559. });
  560. tempLayer.ramp = {
  561. type: GlobalStorage.layerType.Static
  562. };
  563. break;
  564.  
  565. case "tile":
  566. tempLayer = new ArcGISTiledMapServiceLayer(staticLayer.url, {
  567. opacity: resolveLayerOpacity(staticLayer.settings.opacity),
  568. id: staticLayer.id
  569. });
  570. console.log("tile layer added. " + staticLayer.id);
  571. break;
  572.  
  573. case "dynamic":
  574. tempLayer = new ArcGISDynamicMapServiceLayer(staticLayer.url, {
  575. opacity: resolveLayerOpacity(staticLayer.settings.opacity),
  576. id: staticLayer.id
  577. });
  578. console.log("dynamic layer added. " + staticLayer.id);
  579. break;
  580.  
  581. default:
  582. //TODO add in other types of maps... wms? non-esri tile?
  583. break;
  584. }
  585. return tempLayer;
  586. }
  587.  
  588. return {
  589. /**
  590. * The maximum extent of the map control is allowed to go to
  591. * @property getMaxExtent
  592. * @type {Object}
  593. *
  594. */
  595. getMaxExtent: function () {
  596. return maxExtent;
  597. },
  598. /**
  599. * Return the map control object
  600. * @property getMap
  601. * @type {Object}
  602. *
  603. */
  604. getMap: function () {
  605. if (UtilMisc.isUndefined(map)) {
  606. console.log("trying to get map before it is available!");
  607. }
  608. return map;
  609. },
  610.  
  611. /**
  612. * Returns a list of feature layers that are currently visible on the map.
  613. * @method getVisibleFeatureLayers
  614. * @return {Array} an array of {{#crossLink "Esri/layer/FeatureLayer"}}{{/crossLink}} objects
  615. *
  616. */
  617. getVisibleFeatureLayers: function () {
  618. // Return only the feature layers
  619. //TODO do we need to consider static layers here?
  620. return dojoArray.filter(map.getLayersVisibleAtScale(), function (layer) {
  621. return layer.type && (layer.type === "Feature Layer") && layer.visible;
  622. });
  623. },
  624.  
  625. /**
  626. * Return the feature layer corresponding to the given url.
  627. *
  628. * @method getFeatureLayer
  629. * @private
  630. * @param {String} featureUrl the url of the feature layer
  631. * @return {Esri/layer/FeatureLayer} feature layer
  632. */
  633. getFeatureLayer: function (featureUrl) {
  634. return UtilArray.find(featureLayers,
  635. function (featureLayer) {
  636. return featureLayer.url === featureUrl;
  637. });
  638. },
  639.  
  640. /**
  641. * Given an ESRI Extent Object, returns a new ESRI Extent Object that
  642. * contains the extent adjusted according to this map's maximum extent
  643. *
  644. * NOTE: this method is currently unused!
  645. *
  646. * @param {esri/geometry/Extent} e the extent Object
  647. * @param {esri/geometry/Extent} maxExtent the maximum extent
  648. * @return {esri/geometry/Extent} An adjusted extent, if the target extent is outside the boundary
  649. * @method checkBoundary
  650. */
  651. checkBoundary: function (e, maxExtent) {
  652. var extent = e,
  653. width = extent.width(),
  654. height = extent.height(),
  655. centerX = extent.centerX(),
  656. centerY = extent.centerY(),
  657. flag, adjustedEx;
  658.  
  659. adjustedEx = extent.clone();
  660.  
  661. var maxHeight = maxExtent.height();
  662. if (height > maxHeight) {
  663. height = maxHeight;
  664. }
  665.  
  666. if (centerY > maxExtent.ymax) {
  667. adjustedEx.ymax = maxExtent.ymax;
  668. adjustedEx.ymin = maxExtent.ymax - height;
  669. flag = true;
  670. //} else if (extent.ymin < maxExtent.ymin) {
  671. } else if (centerY < maxExtent.ymin) {
  672. adjustedEx.ymin = maxExtent.ymin;
  673. adjustedEx.ymax = maxExtent.ymin + height;
  674. flag = true;
  675. }
  676.  
  677. var maxWidth = maxExtent.width();
  678. if (width > maxWidth) {
  679. width = maxWidth;
  680. }
  681.  
  682. if (centerX > maxExtent.xmax) {
  683. adjustedEx.xmax = maxExtent.xmax;
  684. adjustedEx.xmin = maxExtent.xmax - width;
  685. flag = true;
  686. } else if (centerX < maxExtent.xmin) {
  687. adjustedEx.xmin = maxExtent.xmin;
  688. adjustedEx.xmax = maxExtent.xmin + width;
  689. flag = true;
  690. }
  691.  
  692. if (flag) {
  693. return adjustedEx;
  694. }
  695. },
  696. /*
  697. * Initialize map control with configuration objects provided in the bootstrapper.js file.
  698. *
  699. * Initialize extent
  700. * Add base map from the config.basemaps array that has the showOnInit()
  701. * Add Static layer from config.featureLayers.staticLayers
  702. * Add feature layers from config.featureLayers
  703. * Create bounding layers and add to map control
  704. * Add map tip events to each feature layer (click/hover/out)
  705. * Show scalebar
  706. * Publish events to outside for other modules to use
  707. * Subscribe events to update map control
  708. *
  709. * Note: Not sure if we want to include all the config requirements here.
  710. * Map control is initialized with div id provided. The following config file entries are used:
  711. * config.spatialReference
  712. * config.extents.defaultExtent xmin, ymin, xmax, ymax
  713. * config.levelOfDetails.minLevel
  714. * config.levelOfDetails.maxLevel
  715. * config.extents.maximumExtent
  716. * config.extents.fullExtent
  717. * config.basemaps arrays of basemap, only one or first one with showOnInit set to true
  718. * config.featureLayers
  719. *
  720. * @method init
  721. * @param {Object} mapDiv the HTML div that will store the map control
  722. * @constructor
  723. *
  724. */
  725. init: function () {
  726. //config object is loaded in bootstrapper.js
  727. var config = RAMP.config,
  728.  
  729. /**
  730. * The spatial reference of the map
  731. *
  732. * @property spatialReference
  733. * @private
  734. * @type {esri/SpatialReference}
  735. */
  736. spatialReference = new esri.SpatialReference(config.spatialReference),
  737.  
  738. /**
  739. * The URL of the basemap that is on by default
  740. *
  741. * @property url
  742. * @private
  743. * @type {String}
  744. */
  745. url = UtilArray.find(config.basemaps, function (basemap) {
  746. return basemap.showOnInit;
  747. }).url,
  748.  
  749. /**
  750. * The basemap layer
  751. *
  752. * @property baseLayer
  753. * @private
  754. * @type {Esri/layers/ArcGISTiledMapServiceLayer}
  755. */
  756. baseLayer = new ArcGISTiledMapServiceLayer(url, {
  757. id: "basemapLayer"
  758. });
  759.  
  760. /**
  761. * The maximum extent of the map
  762. *
  763. * @property maxExtent
  764. * @private
  765. * @type {esri/geometry/Extent}
  766. */
  767. maxExtent = createExtent(config.extents.maximumExtent, spatialReference);
  768.  
  769. /**
  770. * The initial extent of the map
  771. *
  772. * @property InitExtent
  773. * @private
  774. * @type {esri/geometry/Extent}
  775. */
  776. initExtent = createExtent(config.extents.defaultExtent, spatialReference);
  777.  
  778. /**
  779. * Used for full extent in nav widget
  780. *
  781. * @property fullExtent
  782. * @private
  783. * @type {esri/geometry/Extent}
  784. */
  785. fullExtent = createExtent(config.extents.fullExtent, spatialReference);
  786.  
  787. //generate WMS layers array
  788. wmsLayers = dojoArray.map(config.layers.wms, function (layer) {
  789. var wmsl = new WMSLayer(layer.url, {
  790. id: layer.id,
  791. format: layer.format,
  792. opacity: resolveLayerOpacity(layer.settings.opacity),
  793. visibleLayers: [layer.layerName]
  794. //resourceInfo: {
  795. // extent: new EsriExtent(layer.extent),
  796. // layerInfos: [new WMSLayerInfo({name:layer.layerName,title:layer.displayName})]
  797. //}
  798. });
  799. wmsl.ramp = {
  800. type: GlobalStorage.layerType.WMS
  801. };
  802.  
  803. // WMS binding for getFeatureInfo calls
  804.  
  805. if (layer.featureInfo !== undefined) {
  806. console.log('registering ' + layer.displayName + ' for WMS getFeatureInfo');
  807. MapClickHandler.registerWMSClick({ wmsLayer: wmsl, layerConfig: layer });
  808. }
  809.  
  810. //wmsl.setVisibleLayers(layer.layerName);
  811. wmsl.setVisibility(layer.settings.visible);
  812.  
  813. console.log("wms registered: " + layer.id);
  814. console.log(wmsl);
  815. return wmsl;
  816. });
  817.  
  818. //generate feature layers array
  819. featureLayers = dojoArray.map(config.layers.feature, function (layerConfig) {
  820. var fl;
  821.  
  822. if (layerConfig.isStatic) {
  823. fl = generateStaticLayer(layerConfig);
  824. } else {
  825. fl = new FeatureLayer(layerConfig.url, {
  826. id: layerConfig.id,
  827. mode: FeatureLayer.MODE_SNAPSHOT,
  828. outFields: [layerConfig.layerAttributes],
  829. visible: layerConfig.settings.visible,
  830. opacity: resolveLayerOpacity(layerConfig.settings.opacity)
  831. });
  832. fl.ramp = { type: GlobalStorage.layerType.Feature };
  833. if (layerConfig.settings.boundingBoxVisible === true) {
  834. dojoOn.once(fl, "update-end", function () {
  835. setBoundingBoxVisibility(layerConfig.id, true);
  836. });
  837. }
  838. }
  839.  
  840. if (layerConfig.settings.visible === false) {
  841. dojoOn.once(fl, "update-end", function () {
  842. fl.setVisibility(false);
  843. });
  844. }
  845. return fl;
  846. });
  847.  
  848. /**
  849. * A list GraphicsLayer that represent the extent bounding box of the feature layers.
  850. * {[esr/layer/featurelayers]} featureLayers A list of feature layers found in the application config
  851. * {[esri/layer/graphiclayer]} An array of graphic layers to add to the map
  852. *
  853. * @property boundingBoxLayers
  854. * @type {array of esri/layer/GraphicsLayer}
  855. */
  856.  
  857. var boundingBoxLayers = dojoArray.map(config.layers.feature, function (layer) {
  858.  
  859. // Map a list of featurelayers into a list of GraphicsLayer representing
  860. // the extent bounding box of the feature layer. Note each bounding box layer
  861. // at this point are empty, the actual graphic that represent the bounding box
  862. // will be generated the first time the user toggles it on.
  863. var boundingBox = new GraphicsLayer({
  864. id: String.format("boundingBoxLayer_{0}", layer.id),
  865. visible: layer.settings.boundingBoxVisible
  866. });
  867. boundingBox.ramp = { type: GlobalStorage.layerType.BoundingBox };
  868.  
  869. var boundingBoxExtent;
  870. if (typeof layer.layerExtent !== "undefined") {
  871.  
  872. boundingBoxExtent = createExtent(layer.layerExtent, spatialReference);
  873.  
  874. var extentGraphic = new esri.Graphic({
  875. geometry: boundingBoxExtent,
  876. symbol: {
  877. color: [255, 0, 0, 64],
  878. outline: {
  879. color: [240, 128, 128, 255],
  880. width: 1,
  881. type: "esriSLS",
  882. style: "esriSLSSolid"
  883. },
  884. type: "esriSFS",
  885. style: "esriSFSSolid"
  886. }
  887. });
  888.  
  889. boundingBox.add(extentGraphic);
  890. }
  891. return boundingBox;
  892. });
  893.  
  894. // Maps layerId to a GraphicsLayer Object that represents the extent bounding box
  895. // for that layer
  896. boundingBoxMapping = UtilDict.zip(dojoArray.map(config.layers.feature, function (layer) {
  897. return layer.id;
  898. }), boundingBoxLayers);
  899.  
  900. //the map!
  901. map = new EsriMap(config.divNames.map, {
  902. extent: initExtent,
  903. logo: false,
  904. minZoom: config.levelOfDetails.minLevel,
  905. maxZoom: config.levelOfDetails.maxLevel,
  906. slider: false
  907. });
  908.  
  909. GlobalStorage.map = map;
  910. MapClickHandler.init(map);
  911.  
  912. /* START - Add static layers */
  913.  
  914. var staticLayers = [],
  915. perLayerStaticMaps = [],
  916. staticLayerMap = [];
  917.  
  918. dojoArray.forEach(config.layers.feature, function (layer) {
  919. perLayerStaticMaps = [];
  920. dojoArray.forEach(layer.staticLayers, function (staticLayer, i) {
  921. var tempLayer = map.generateStaticLayer(staticLayer);
  922.  
  923. staticLayers.push(tempLayer);
  924. //creates an array of all static layers defined for the current, single feature layer
  925. perLayerStaticMaps[i] = staticLayer.id;
  926. });
  927. //adds the static layer id array as a value to an array indexed by feature layer id
  928. staticLayerMap[layer.id] = perLayerStaticMaps;
  929. });
  930.  
  931. GlobalStorage.LayerMap = staticLayerMap;
  932. /* End - Add static layers */
  933.  
  934. baseLayer.ramp = {
  935. type: GlobalStorage.layerType.Basemap
  936. };
  937. // Combine all layer arrays then add them all at once (for efficiency)
  938. console.log('adding wmses');
  939. console.log(wmsLayers);
  940. map.addLayers([baseLayer].concat(wmsLayers, staticLayers, boundingBoxLayers, featureLayers));
  941.  
  942. /* Start - Show scalebar */
  943. var scalebar = new EsriScalebar({
  944. map: map,
  945. attachTo: "bottom-left",
  946. scalebarUnit: "metric"
  947. });
  948.  
  949. scalebar.show();
  950.  
  951. /* End - Show scalebar */
  952.  
  953. _initRepublishers(map);
  954. _initListeners(map);
  955. _initEventHandlers(map, featureLayers);
  956. }
  957. };
  958. });