Source: viewer/loader/Map.js

  1. import { WMSLayer } from '../../map/layer/WMSLayer/WMSLayer.js';
  2. import { Map2D } from '../../map/Map2D.js';
  3. import { MapPopup } from '../../map/popup/MapPopup.js';
  4. import { BaselayerSwitcher } from '../../map/tools/BaselayerSwitcher.js';
  5. import { ProjectionSwitcher } from '../../map/tools/ProjectionSwitcher.js';
  6. import { CoordinateControl } from '../../map/tools/CoordinateControl.js';
  7. import { DrawTools } from '../../map/tools/DrawTools.js';
  8. import { FitToScreenButton } from '../../map/tools/FitToScreenButton.js';
  9. import { LayerLegendButton } from '../../map/tools/LayerLegendButton.js';
  10. import { ScreenshotButton } from '../../map/tools/ScreenshotButton.js';
  11. import { ZoomButton } from '../../map/tools/ZoomButton.js';
  12. import { Attribution } from '../../map/tools/Attribution.js';
  13. import { ScaleControl } from '../../map/tools/ScaleControl.js';
  14. /**
  15. * Loader for the Map module
  16. *
  17. * @author sjaswal <shahzeib.jaswal@awi.de>
  18. * @author rhess <robin.hess@awi.de>
  19. */
  20. /**
  21. * A function that loads the Map
  22. *
  23. * @memberof vef.viewer.loader
  24. *
  25. * @param {object} options
  26. * @param {Viewer} viewer
  27. */
  28. export function Map(options, viewer) {
  29. const map = new Map2D(null, options.options);
  30. initActiveArea_(map, options, viewer);
  31. if (options.popup) initPopup_(map, options, viewer);
  32. setBaseLayer_(map, viewer);
  33. initTools_(map, options.tools, viewer);
  34. viewer.addElement(options.id, map);
  35. };
  36. function initPopup_(map, options, viewer) {
  37. let sidebar = null;
  38. let sidebarGroup = null;
  39. let clickPopupVisible = false;
  40. const popup = new MapPopup(map, {
  41. showMore: !!(options.popup?.targetSidebar && options.popup?.sidebarGroup),
  42. showEmptyMessage: options.options?.showPopupEmptyMessage ?? true,
  43. showLoading: options.options?.showPopupLoading ?? true,
  44. });
  45. const clickListener = e => {
  46. if (map.editing) return;
  47. // sort layers according to LayerTree order (Feature based layers are always on top)
  48. const layers = map.getLayers();
  49. layers.sort((a, b) => {
  50. if ((a instanceof WMSLayer) && !(b instanceof WMSLayer)) return 1;
  51. if (!(a instanceof WMSLayer) && (b instanceof WMSLayer)) return -1;
  52. // index order needs to be inverted, because higher index layers are on top
  53. if ((a.zIndex_ < b.zIndex_)) return 1;
  54. if ((a.zIndex_ > b.zIndex_)) return -1;
  55. return 0;
  56. });
  57. popup.requestPopup(layers, e.lat, e.lng);
  58. }
  59. // dont open popup when clicking too fast (moslty double click)
  60. let previousClick = performance.now();
  61. let popupTimeout = null;
  62. map.on("click", e => {
  63. if (popupTimeout) {
  64. clearTimeout(popupTimeout);
  65. popupTimeout = null;
  66. }
  67. map.closePopup();
  68. const currentClick = performance.now();
  69. if ((currentClick - previousClick) > 500) {
  70. popupTimeout = setTimeout(() => clickListener(e), 200);
  71. }
  72. previousClick = currentClick;
  73. });
  74. // init the popup sidebar
  75. if (options?.popup?.targetSidebar && options?.popup?.sidebarGroup) {
  76. sidebar = viewer.elements[options.popup.targetSidebar];
  77. sidebarGroup = options.popup.sidebarGroup;
  78. }
  79. popup.on("show_popup", item => {
  80. if (sidebar && sidebarGroup) {
  81. sidebar.setContent(sidebarGroup, item.sidebar, true);
  82. sidebar.setTitle(sidebarGroup, item.sidebar.dataset.title || item.content.title || item.content.layer.title);
  83. item.onShowMore = () => sidebar.show(sidebarGroup);
  84. }
  85. clickPopupVisible = true;
  86. map.fire("change_popup", item)
  87. });
  88. // highlighting selected feature
  89. let popupVisible = false;
  90. map.addLayer(popup.selectedFeature);
  91. map.on("hide_popup", () => {
  92. if (!sidebar?.isOpen()) popup.selectedFeature.setData(null);
  93. popupVisible = false;
  94. clickPopupVisible = false;
  95. });
  96. map.on("show_popup", () => {
  97. popupVisible = true;
  98. });
  99. sidebar?.on("close", () => {
  100. if (!popupVisible) popup.selectedFeature.setData(null);
  101. })
  102. // feature hover popup
  103. const hoverPopup = new MapPopup(map, { showMore: false });
  104. viewer.layerManager.on("layermanager_layer_mouseover", e => {
  105. if (e.layer.hoverEvents && !clickPopupVisible) {
  106. const latlng = e.mouseEvent.latlng;
  107. hoverPopup.requestPopup([e.layer], latlng.lat, latlng.lng, map);
  108. }
  109. });
  110. hoverPopup.on("show_popup", item => map.fire("change_popup", item));
  111. viewer.layerManager.on("layermanager_layer_mouseout", e => {
  112. if (e.layer.hoverEvents && !clickPopupVisible) map.closePopup();
  113. });
  114. viewer.filters.on("change", () => map.closePopup());
  115. viewer.filters.setMap(map);
  116. }
  117. function initTools_(map, tools, viewer) {
  118. // push to the end of the current eventqueue to allow loading all other events
  119. // because a some tools rely on other Ui Elements already being initialized
  120. setTimeout(() => {
  121. for (let i = 0; i < tools.length; ++i) {
  122. switch (tools[i].type.toLowerCase()) {
  123. case "attribution":
  124. new Attribution(map, tools[i].position);
  125. break;
  126. case "zoom":
  127. new ZoomButton(map, tools[i].position);
  128. break;
  129. case "fittoscreen":
  130. new FitToScreenButton(map, tools[i].position);
  131. break;
  132. case "layerlegends":
  133. if (viewer.elements[tools[i].targetTree]) {
  134. new LayerLegendButton(map, tools[i].position, viewer.elements[tools[i].targetTree]);
  135. }
  136. break;
  137. case "screenshot":
  138. new ScreenshotButton(map, tools[i].position);
  139. break;
  140. case "drawtools":
  141. new DrawTools(map, tools[i].position);
  142. break;
  143. case "coordinatecontrol":
  144. new CoordinateControl(map, tools[i].position);
  145. break;
  146. case "baselayerswitcher":
  147. const baselayerSwitcher = new BaselayerSwitcher(map, tools[i].position, viewer.baseLayers);
  148. const infoSidebar = viewer.elements[tools[i].infoSidebar];
  149. if (infoSidebar && tools[i].infoSidebarGroup) {
  150. baselayerSwitcher.enableInfoButton();
  151. baselayerSwitcher.on("show_info", (layer) => {
  152. infoSidebar.setContent(tools[i].infoSidebarGroup, layer.printMetadata());
  153. infoSidebar.setTitle(tools[i].infoSidebarGroup, layer.title);
  154. infoSidebar.show(tools[i].infoSidebarGroup);
  155. });
  156. }
  157. break;
  158. case "projectionswitcher":
  159. new ProjectionSwitcher(map, tools[i].position, viewer.baseLayers, tools[i].projections);
  160. break;
  161. case "scalecontrol":
  162. new ScaleControl(map, tools[i].position);
  163. break;
  164. }
  165. }
  166. }, 0);
  167. }
  168. function initActiveArea_(map, options, viewer) {
  169. if (options.activeAreaSidebar) {
  170. let sidebar = viewer.elements[options.activeAreaSidebar];
  171. let sidebarOpen = !sidebar.getElement().classList.contains("collapsed");
  172. const showActiveArea = (ignoreSidebarOpen = false, centerMap = false) => {
  173. if (ignoreSidebarOpen || !sidebarOpen) {
  174. map.setActiveArea("sidebar-active-area", centerMap);
  175. }
  176. sidebarOpen = true;
  177. };
  178. const showActiveAreaAlt = (ignoreSidebarOpen = false, centerMap = false) => {
  179. if (ignoreSidebarOpen || sidebarOpen) {
  180. map.setActiveArea("sidebar-active-area-alt", centerMap);
  181. }
  182. sidebarOpen = false;
  183. }
  184. sidebar.on("show", () => showActiveArea());
  185. sidebar.on("close", () => showActiveAreaAlt());
  186. sidebarOpen ? showActiveArea(true, true) : showActiveAreaAlt(true, true);
  187. }
  188. }
  189. // set initial baselayer as defined in the map config
  190. function setBaseLayer_(map, viewer) {
  191. const key = map.options.baseLayer || Object.keys(viewer.baseLayers)[0];
  192. const crs = map.options.crs;
  193. if ((key in viewer.baseLayers) && (crs in viewer.baseLayers[key])) {
  194. const layer = viewer.baseLayers[key][crs];
  195. map.setBaseLayer(layer, null);
  196. }
  197. }