Reusable Accessible Mapping Platform

API Docs for: 3.0.0
Show:

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

  1. /*global define, esri, window, Snap, $ */
  2.  
  3. /**
  4. *
  5. *
  6. * @module RAMP
  7. * @submodule Map
  8. */
  9.  
  10. /**
  11. * A class responsible for highlighting points, lines, and shapes on the map upon selection and consequent hover actions.
  12. * The highlighting is achieved by manipulating svg rendering of the map as follows:
  13. * - all existing feature layers are wrapped into a group object
  14. * - three more group objects are created:
  15. * - `highlight group` is positioned after the `graphicGroup`
  16. * - `zoomlight group` is positioned before the `graphicGroup`
  17. * - `hoverlight group` is positioned before the `zoomlight group`
  18. *
  19. * and changing the opacity of the `graphicGroup` while adding shapes to one or more of the additional group objects.
  20. *
  21. * @class FeatureHighlighter
  22. * @static
  23. * @uses dojo/_base/declare
  24. * @uses dojo/topic
  25. * @uses GlobalStorage
  26. * @uses Map
  27. * @uses EventManager
  28. * @uses Util
  29. * @uses Dictionary
  30. */
  31.  
  32. define([
  33. /* Dojo */
  34. "dojo/_base/declare", "dojo/topic",
  35.  
  36. /* Ramp */
  37. "ramp/globalStorage", "ramp/map", "ramp/eventManager",
  38.  
  39. /* Util */
  40. "utils/util", "utils/dictionary"
  41. ],
  42.  
  43. function (
  44. /* Dojo */
  45. declare, topic,
  46.  
  47. /* Ramp */
  48. GlobalStorage, RampMap, EventManager,
  49.  
  50. /* Util*/
  51. UtilMisc, UtilDict) {
  52. "use strict";
  53.  
  54. var map,
  55. config,
  56.  
  57. highlightLayer,
  58. hoverlightLayer,
  59. zoomlightLayer,
  60.  
  61. graphicGroup,
  62.  
  63. highlightedGraphic,
  64.  
  65. zoomlightGraphic;
  66.  
  67. /**
  68. * Creates a copy of the given graphic object.
  69. *
  70. * @method cloneGraphic
  71. * @private
  72. * @param {Object} graphic Graphic object to clone
  73. * @return clone A cloned Graphic object
  74. */
  75. function cloneGraphic(graphic) {
  76. // Figure out how large the image should be based on the
  77. // shape or symbol object (which ever one exists)
  78.  
  79. var clone = new esri.Graphic({
  80. geometry: graphic.geometry,
  81. attributes: {}
  82. });
  83.  
  84. clone.symbol = graphic.getLayer().renderer.getSymbol(graphic);
  85.  
  86. // Pass on the attributes of the graphic to the highlight graphic
  87. // this way the maptip will work
  88. UtilDict.forEachEntry(graphic.attributes, function (key, value) {
  89. clone.attributes[key] = value;
  90. });
  91.  
  92. return clone;
  93. }
  94.  
  95. /**
  96. * Sorts and groups the svg representation of the map layers on the page to make highlighting work.
  97. * Group all the feature layers and create new groups for highlight, zoomlight, and hoverlight layers.
  98. *
  99. * @method sortLayers
  100. * @private
  101. */
  102. function sortLayers() {
  103. if (!graphicGroup) {
  104. var svg = Snap.select("svg"),
  105. layers = svg.selectAll("svg g"), // > g:not(.highlightLayer)"),
  106. hoverl = svg.select(".hoverlightLayer"),
  107. zooml = svg.select(".zoomlightLayer"),
  108. hightl = svg.select(".highlightLayer");
  109.  
  110. graphicGroup = svg.group(layers);
  111.  
  112. svg.add(graphicGroup);
  113. graphicGroup.after(hightl);
  114. graphicGroup.before(hoverl);
  115. graphicGroup.before(zooml);
  116. graphicGroup.attr({
  117. class: "graphics"
  118. });
  119. }
  120. }
  121.  
  122. /**
  123. * Clones the Graphic object from the event, adds it to the Highlight layer, and lowers the opacity of other map layers to make the cloned
  124. * Graphic stand out.
  125. *
  126. * @method highlightGraphic
  127. * @private
  128. * @param {Object} eventArg ???
  129. */
  130. function highlightGraphic(eventArg) {
  131. var graphic = eventArg.graphic,
  132. newGraphic = cloneGraphic(graphic);
  133.  
  134. sortLayers();
  135.  
  136. highlightLayer.clear();
  137.  
  138. // Highlights the selected point by adding a graphic object to the highLight layer
  139. highlightedGraphic = newGraphic;
  140. // Needed to find the symbol in maptip
  141. highlightLayer.url = graphic.getLayer().url;
  142. highlightLayer.objectIdField = graphic.getLayer().objectIdField;
  143.  
  144. highlightLayer.add(newGraphic);
  145.  
  146. graphicGroup.attr({
  147. opacity: 0.35
  148. });
  149. }
  150.  
  151. /**
  152. * Clears the Highlight layer and restores the opacity of the map layers.
  153. *
  154. * @method highlightGraphicHide
  155. * @private
  156. */
  157. function highlightGraphicHide() {
  158. if (highlightedGraphic) {
  159. highlightedGraphic = null;
  160.  
  161. if (graphicGroup) {
  162. graphicGroup.attr({
  163. opacity: 1
  164. });
  165. }
  166.  
  167. zoomLightHide();
  168. highlightLayer.clear();
  169. }
  170. }
  171.  
  172. /**
  173. * Clones the Graphic object from the event, adds it to the Hoverlight layer.
  174. *
  175. * @method hoverLight
  176. * @private
  177. * @param {Object} eventArg ???
  178. */
  179. function hoverLight(eventArg) {
  180. var graphic = eventArg.graphic,
  181. newGraphic = cloneGraphic(graphic);
  182.  
  183. sortLayers();
  184.  
  185. hoverlightLayer.clear();
  186. hoverlightLayer.add(newGraphic);
  187. }
  188.  
  189. /**
  190. * Clears the Hoverlight layer.
  191. *
  192. * @method hoverLightHide
  193. * @private
  194. */
  195. function hoverLightHide() {
  196. hoverlightLayer.clear();
  197. }
  198.  
  199. /**
  200. * Clones the Graphic object from the event, adds it to the Zoomlight layer, and lowers the opacity of other map layers to make the cloned
  201. * Graphic stand out.
  202. *
  203. * @method zoomLight
  204. * @private
  205. * @param {Object} eventArg ???
  206. */
  207. function zoomLight(eventArg) {
  208. var graphic = eventArg.graphic,
  209. newGraphic = cloneGraphic(graphic);
  210.  
  211. sortLayers();
  212.  
  213. //TODO: ensure that graphics are different
  214.  
  215. zoomlightGraphic = newGraphic;
  216.  
  217. zoomlightLayer.clear();
  218. zoomlightLayer.url = graphic.getLayer().url;
  219. zoomlightLayer.objectIdField = graphic.getLayer().objectIdField;
  220. zoomlightLayer.add(newGraphic);
  221.  
  222. if (graphicGroup) {
  223. graphicGroup.attr({ opacity: 0.35 });
  224. }
  225. }
  226.  
  227. /**
  228. * Clears the Zoomlight layer and restores the opacity of the map layers if the Highlight layer is empty.
  229. *
  230. * @method zoomLightHide
  231. * @private
  232. */
  233. function zoomLightHide() {
  234. if (zoomlightGraphic) {
  235. zoomlightGraphic = null;
  236. zoomlightLayer.clear();
  237.  
  238. if (!highlightedGraphic && graphicGroup) {
  239. graphicGroup.attr({
  240. opacity: 1
  241. });
  242. }
  243. }
  244. }
  245.  
  246. /**
  247. * If there a Graphic in the Highlihgh layer, resets it's bounding box and repositions an interactive maptip to match the top center of the
  248. * boudning box of the highlighted graphic.
  249. *
  250. * @method repositionInteractive
  251. * @private
  252. */
  253. function repositionInteractive() {
  254. if (highlightedGraphic) {
  255. window.setTimeout(function () {
  256. var snapGraphic = Snap.select("svg .highlightLayer > *:first-child"),
  257. offset;
  258.  
  259. if (snapGraphic) {
  260. snapGraphic._.dirty = true; // dirty hack to a bug: https://github.com/adobe-webplatform/Snap.svg/issues/80
  261. offset = snapGraphic.getBBox().width / 2;
  262.  
  263. topic.publish(EventManager.Maptips.REPOSITION_INTERACTIVE, {
  264. offset: offset
  265. });
  266. }
  267. }, 10);
  268. }
  269. }
  270.  
  271. /**
  272. * Initiates various listeners for the class.
  273. *
  274. * @method initListeners
  275. * @private
  276. */
  277. function initListeners() {
  278. // Subscribe to all the events that causes functionality changes
  279. topic.subscribe(EventManager.FeatureHighlighter.HIGHLIGHT_SHOW, highlightGraphic);
  280.  
  281. topic.subscribe(EventManager.FeatureHighlighter.HIGHLIGHT_HIDE, highlightGraphicHide);
  282.  
  283. topic.subscribe(EventManager.FeatureHighlighter.HOVERLIGHT_SHOW, hoverLight);
  284.  
  285. topic.subscribe(EventManager.FeatureHighlighter.HOVERLIGHT_HIDE, hoverLightHide);
  286.  
  287. topic.subscribe(EventManager.FeatureHighlighter.ZOOMLIGHT_SHOW, zoomLight);
  288.  
  289. topic.subscribe(EventManager.FeatureHighlighter.ZOOMLIGHT_HIDE, zoomLightHide);
  290.  
  291. // Trigger maptips/showInteractive --> display anchoredMapTip
  292. // detect when a new graphic is added to the highlihgt layer and display in interactive tooltip
  293. highlightLayer.on("graphic-node-add", function (evt) {
  294. topic.publish(EventManager.Maptips.SHOW_INTERACTIVE, {
  295. target: $(evt.node),
  296. graphic: highlightedGraphic
  297. });
  298. });
  299.  
  300. // detect when a new graphic is added to the zoomlight layer and display a
  301. // temporary tooltip only if the zoomlighted graphic is not already highlighted
  302. zoomlightLayer.on("graphic-node-add", function (evt) {
  303. var zgKey = "0",
  304. hgKey = "1",
  305. objectIdField,
  306. zgLayer,
  307. hgLayer;
  308.  
  309. if (highlightedGraphic) {
  310. zgLayer = zoomlightGraphic.getLayer();
  311. hgLayer = highlightedGraphic.getLayer();
  312. objectIdField = zgLayer.objectIdField;
  313. zgKey = zgLayer.url + zoomlightGraphic.attributes[objectIdField];
  314. hgKey = hgLayer.url + highlightedGraphic.attributes[objectIdField];
  315. }
  316.  
  317. if (zgKey !== hgKey) {
  318. topic.publish(EventManager.Maptips.SHOW, {
  319. target: $(evt.node),
  320. graphic: zoomlightGraphic
  321. });
  322. }
  323. });
  324.  
  325. // make sure the interactive tooltip is properly positioned when the user pans and moves the map
  326. topic.subscribe(EventManager.Map.ZOOM_END, repositionInteractive);
  327. topic.subscribe(EventManager.Map.PAN_END, repositionInteractive);
  328.  
  329. // cancel the graphicGroup after the layer reorder to force recreating of the lighthing layer groups
  330. topic.subscribe(EventManager.Map.REORDER_END, function () {
  331. graphicGroup = null;
  332. });
  333. }
  334.  
  335. return {
  336. /**
  337. * Initiates the FeatureHighlighter static class.
  338. *
  339. * @method init
  340. */
  341. init: function () {
  342. config = GlobalStorage.config;
  343. map = RampMap.getMap();
  344.  
  345. //
  346. highlightLayer = new esri.layers.GraphicsLayer({
  347. id: "highlightLayer",
  348. className: "highlightLayer"
  349. });
  350. highlightLayer.ramp = {
  351. type: GlobalStorage.layerType.Highlight
  352. };
  353.  
  354. // Layer for showing the graphic that appears when a point
  355. // has the mouse hovering over it but the point has not been
  356. // selected
  357. hoverlightLayer = new esri.layers.GraphicsLayer({
  358. id: "hoverlightLayer",
  359. className: "hoverlightLayer"
  360. });
  361. hoverlightLayer.ramp = {
  362. type: GlobalStorage.layerType.Hoverlight
  363. };
  364.  
  365. // Layer for showing the graphic that appears after the user
  366. // presses zoom to on a point
  367. zoomlightLayer = new esri.layers.GraphicsLayer({
  368. id: "zoomLightLayer",
  369. className: "zoomlightLayer"
  370. });
  371. zoomlightLayer.ramp = {
  372. type: GlobalStorage.layerType.Zoomlight
  373. };
  374.  
  375. map.addLayer(highlightLayer);
  376. map.addLayer(hoverlightLayer, 0);
  377. map.addLayer(zoomlightLayer, 0);
  378.  
  379. initListeners();
  380. }
  381. };
  382. });