ol3-layerswitcher.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. /*!
  2. *
  3. * Layer control for OpenLayers 3+(https://github.com/walkermatt/ol3-layerswitchery)
  4. * license: MIT (c) Matt Walker.
  5. * version: v1.1.2
  6. *
  7. */
  8. (function (root, factory) {
  9. if(typeof define === "function" && define.amd) {
  10. define(["openlayers"], factory);
  11. } else if(typeof module === "object" && module.exports) {
  12. module.exports = factory(require("openlayers"));
  13. } else {
  14. root.LayerSwitcher = factory(root.ol);
  15. }
  16. }(this, function(ol) {
  17. /**
  18. * OpenLayers v3/v4 Layer Switcher Control.
  19. * See [the examples](./examples) for usage.
  20. * @constructor
  21. * @extends {ol.control.Control}
  22. * @param {Object} opt_options Control options, extends olx.control.ControlOptions adding:
  23. * **`tipLabel`** `String` - the button tooltip.
  24. */
  25. ol.control.LayerSwitcher = function(opt_options) {
  26. var options = opt_options || {};
  27. var tipLabel = options.tipLabel ?
  28. options.tipLabel : 'Legend';
  29. this.mapListeners = [];
  30. this.hiddenClassName = 'ol-unselectable ol-control layer-switcher';
  31. if (ol.control.LayerSwitcher.isTouchDevice_()) {
  32. this.hiddenClassName += ' touch';
  33. }
  34. this.shownClassName = 'shown';
  35. var element = document.createElement('div');
  36. element.className = this.hiddenClassName;
  37. var button = document.createElement('button');
  38. button.setAttribute('title', tipLabel);
  39. element.appendChild(button);
  40. this.panel = document.createElement('div');
  41. this.panel.className = 'panel';
  42. element.appendChild(this.panel);
  43. ol.control.LayerSwitcher.enableTouchScroll_(this.panel);
  44. var this_ = this;
  45. button.onmouseover = function(e) {
  46. this_.showPanel();
  47. };
  48. button.onclick = function(e) {
  49. e = e || window.event;
  50. this_.showPanel();
  51. e.preventDefault();
  52. };
  53. this_.panel.onmouseout = function(e) {
  54. e = e || window.event;
  55. if (!this_.panel.contains(e.toElement || e.relatedTarget)) {
  56. this_.hidePanel();
  57. }
  58. };
  59. ol.control.Control.call(this, {
  60. element: element,
  61. target: options.target
  62. });
  63. };
  64. ol.inherits(ol.control.LayerSwitcher, ol.control.Control);
  65. /**
  66. * Show the layer panel.
  67. */
  68. ol.control.LayerSwitcher.prototype.showPanel = function() {
  69. if (!this.element.classList.contains(this.shownClassName)) {
  70. this.element.classList.add(this.shownClassName);
  71. this.renderPanel();
  72. }
  73. };
  74. /**
  75. * Hide the layer panel.
  76. */
  77. ol.control.LayerSwitcher.prototype.hidePanel = function() {
  78. if (this.element.classList.contains(this.shownClassName)) {
  79. this.element.classList.remove(this.shownClassName);
  80. }
  81. };
  82. /**
  83. * Re-draw the layer panel to represent the current state of the layers.
  84. */
  85. ol.control.LayerSwitcher.prototype.renderPanel = function() {
  86. this.ensureTopVisibleBaseLayerShown_();
  87. while(this.panel.firstChild) {
  88. this.panel.removeChild(this.panel.firstChild);
  89. }
  90. var ul = document.createElement('ul');
  91. this.panel.appendChild(ul);
  92. this.renderLayers_(this.getMap(), ul);
  93. };
  94. /**
  95. * Set the map instance the control is associated with.
  96. * @param {ol.Map} map The map instance.
  97. */
  98. ol.control.LayerSwitcher.prototype.setMap = function(map) {
  99. // Clean up listeners associated with the previous map
  100. for (var i = 0, key; i < this.mapListeners.length; i++) {
  101. ol.Observable.unByKey(this.mapListeners[i]);
  102. }
  103. this.mapListeners.length = 0;
  104. // Wire up listeners etc. and store reference to new map
  105. ol.control.Control.prototype.setMap.call(this, map);
  106. if (map) {
  107. var this_ = this;
  108. this.mapListeners.push(map.on('pointerdown', function() {
  109. this_.hidePanel();
  110. }));
  111. this.renderPanel();
  112. }
  113. };
  114. /**
  115. * Ensure only the top-most base layer is visible if more than one is visible.
  116. * @private
  117. */
  118. ol.control.LayerSwitcher.prototype.ensureTopVisibleBaseLayerShown_ = function() {
  119. var lastVisibleBaseLyr;
  120. ol.control.LayerSwitcher.forEachRecursive(this.getMap(), function(l, idx, a) {
  121. if (l.get('type') === 'base' && l.getVisible()) {
  122. lastVisibleBaseLyr = l;
  123. }
  124. });
  125. if (lastVisibleBaseLyr) this.setVisible_(lastVisibleBaseLyr, true);
  126. };
  127. /**
  128. * Toggle the visible state of a layer.
  129. * Takes care of hiding other layers in the same exclusive group if the layer
  130. * is toggle to visible.
  131. * @private
  132. * @param {ol.layer.Base} The layer whos visibility will be toggled.
  133. */
  134. ol.control.LayerSwitcher.prototype.setVisible_ = function(lyr, visible) {
  135. var map = this.getMap();
  136. lyr.setVisible(visible);
  137. if (visible && lyr.get('type') === 'base') {
  138. // Hide all other base layers regardless of grouping
  139. ol.control.LayerSwitcher.forEachRecursive(map, function(l, idx, a) {
  140. if (l != lyr && l.get('type') === 'base') {
  141. l.setVisible(false);
  142. }
  143. });
  144. }
  145. };
  146. /**
  147. * Render all layers that are children of a group.
  148. * @private
  149. * @param {ol.layer.Base} lyr Layer to be rendered (should have a title property).
  150. * @param {Number} idx Position in parent group list.
  151. */
  152. ol.control.LayerSwitcher.prototype.renderLayer_ = function(lyr, idx) {
  153. var this_ = this;
  154. var li = document.createElement('li');
  155. var lyrTitle = lyr.get('title');
  156. var lyrId = ol.control.LayerSwitcher.uuid();
  157. var label = document.createElement('label');
  158. if (lyr.getLayers && !lyr.get('combine')) {
  159. li.className = 'group';
  160. label.innerHTML = lyrTitle;
  161. li.appendChild(label);
  162. var ul = document.createElement('ul');
  163. li.appendChild(ul);
  164. this.renderLayers_(lyr, ul);
  165. } else {
  166. li.className = 'layer';
  167. var input = document.createElement('input');
  168. if (lyr.get('type') === 'base') {
  169. input.type = 'radio';
  170. input.name = 'base';
  171. } else {
  172. input.type = 'checkbox';
  173. }
  174. input.id = lyrId;
  175. input.checked = lyr.get('visible');
  176. input.onchange = function(e) {
  177. this_.setVisible_(lyr, e.target.checked);
  178. };
  179. li.appendChild(input);
  180. label.htmlFor = lyrId;
  181. label.innerHTML = lyrTitle;
  182. var rsl = this.getMap().getView().getResolution();
  183. if (rsl > lyr.getMaxResolution() || rsl < lyr.getMinResolution()){
  184. label.className += ' disabled';
  185. }
  186. li.appendChild(label);
  187. }
  188. return li;
  189. };
  190. /**
  191. * Render all layers that are children of a group.
  192. * @private
  193. * @param {ol.layer.Group} lyr Group layer whos children will be rendered.
  194. * @param {Element} elm DOM element that children will be appended to.
  195. */
  196. ol.control.LayerSwitcher.prototype.renderLayers_ = function(lyr, elm) {
  197. var lyrs = lyr.getLayers().getArray().slice().reverse();
  198. for (var i = 0, l; i < lyrs.length; i++) {
  199. l = lyrs[i];
  200. if (l.get('title')) {
  201. elm.appendChild(this.renderLayer_(l, i));
  202. }
  203. }
  204. };
  205. /**
  206. * **Static** Call the supplied function for each layer in the passed layer group
  207. * recursing nested groups.
  208. * @param {ol.layer.Group} lyr The layer group to start iterating from.
  209. * @param {Function} fn Callback which will be called for each `ol.layer.Base`
  210. * found under `lyr`. The signature for `fn` is the same as `ol.Collection#forEach`
  211. */
  212. ol.control.LayerSwitcher.forEachRecursive = function(lyr, fn) {
  213. lyr.getLayers().forEach(function(lyr, idx, a) {
  214. fn(lyr, idx, a);
  215. if (lyr.getLayers) {
  216. ol.control.LayerSwitcher.forEachRecursive(lyr, fn);
  217. }
  218. });
  219. };
  220. /**
  221. * Generate a UUID
  222. * @returns {String} UUID
  223. *
  224. * Adapted from http://stackoverflow.com/a/2117523/526860
  225. */
  226. ol.control.LayerSwitcher.uuid = function() {
  227. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  228. var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
  229. return v.toString(16);
  230. });
  231. }
  232. /**
  233. * @private
  234. * @desc Apply workaround to enable scrolling of overflowing content within an
  235. * element. Adapted from https://gist.github.com/chrismbarr/4107472
  236. */
  237. ol.control.LayerSwitcher.enableTouchScroll_ = function(elm) {
  238. if(ol.control.LayerSwitcher.isTouchDevice_()){
  239. var scrollStartPos = 0;
  240. elm.addEventListener("touchstart", function(event) {
  241. scrollStartPos = this.scrollTop + event.touches[0].pageY;
  242. }, false);
  243. elm.addEventListener("touchmove", function(event) {
  244. this.scrollTop = scrollStartPos - event.touches[0].pageY;
  245. }, false);
  246. }
  247. };
  248. /**
  249. * @private
  250. * @desc Determine if the current browser supports touch events. Adapted from
  251. * https://gist.github.com/chrismbarr/4107472
  252. */
  253. ol.control.LayerSwitcher.isTouchDevice_ = function() {
  254. try {
  255. document.createEvent("TouchEvent");
  256. return true;
  257. } catch(e) {
  258. return false;
  259. }
  260. };
  261. var LayerSwitcher = ol.control.LayerSwitcher;
  262. return LayerSwitcher;
  263. }));