@eeacms/volto-arcgis-block 0.1.340 → 0.1.342

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ ### [0.1.342](https://github.com/eea/volto-arcgis-block/compare/0.1.341...0.1.342) - 20 March 2025
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - Merge pull request #909 from eea/CLMS-280000-FIX-DELETE-USER-SERVICE [Unai Bolivar - [`90458d4`](https://github.com/eea/volto-arcgis-block/commit/90458d4ea49c35960bf578e1053aa140874eb0e8)]
12
+ - CLMS-28000 (bug): styles fixed and errors management in place [Unai Bolivar - [`9bfdcc8`](https://github.com/eea/volto-arcgis-block/commit/9bfdcc879043a27fd394277dbc7864637f4b7596)]
13
+ - CLMS-28000 (bug): full extent for layer is working) [Unai Bolivar - [`88cb01f`](https://github.com/eea/volto-arcgis-block/commit/88cb01fd252cff11425871f2d3f712b9c0e4a9d7)]
14
+ - CLMS-285179 (bug): At this commit I have recovered functionality doe loding and unloading the users wms services without losing control of the app. [Unai Bolivar - [`005846e`](https://github.com/eea/volto-arcgis-block/commit/005846efff3e6045dd15d711beb393b3fd213855)]
15
+ - CLMS-285179 (bug): work in progress but going to load stash so this is a checkpoint save [Unai Bolivar - [`157ee6a`](https://github.com/eea/volto-arcgis-block/commit/157ee6a000810f069c12ce20141732f2e0b58546)]
16
+ - CLMS-280000 (feat): user wms service layers appear in active layers tab. need to match the options to figma and set the correct actions for them. [Unai Bolivar - [`8a0f04c`](https://github.com/eea/volto-arcgis-block/commit/8a0f04c416b0067e27f1e34c1e5cf05f7f818b48)]
17
+ - CLMS-280000 (feat): Services update mapviewer state to re upload the same url after deleting it previously now [Unai Bolivar - [`4c69979`](https://github.com/eea/volto-arcgis-block/commit/4c69979fa636601c1b2ad83e1a2d8df65bd5ed12)]
18
+ - CLMS-284630 (bug): Default active remove bug solved [Urkorue - [`5ab8937`](https://github.com/eea/volto-arcgis-block/commit/5ab8937d6907e81766c53004d916898bf265464d)]
19
+ - CLMS-280000 (feat): services load and delete from the map and menu. Need to update mapviewer state to re upload the same url after deleting it [Unai Bolivar - [`cec867a`](https://github.com/eea/volto-arcgis-block/commit/cec867a1550e841706e7ee5a1a0c282a7e3109ee)]
20
+ - CLMS-280000 (feat): Component style is correct [Unai Bolivar - [`7b2eff5`](https://github.com/eea/volto-arcgis-block/commit/7b2eff5ff2254d14689ea648a4a7cc2f74692986)]
21
+ ### [0.1.341](https://github.com/eea/volto-arcgis-block/compare/0.1.340...0.1.341) - 6 March 2025
22
+
7
23
  ### [0.1.340](https://github.com/eea/volto-arcgis-block/compare/0.1.339...0.1.340) - 5 March 2025
8
24
 
9
25
  #### :hammer_and_wrench: Others
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-arcgis-block",
3
- "version": "0.1.340",
3
+ "version": "0.1.342",
4
4
  "description": "volto-arcgis-block: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: CodeSyntax",
@@ -60,6 +60,7 @@ class MapViewer extends React.Component {
60
60
  layerLoading: false,
61
61
  layers: {},
62
62
  uploadedFile: true,
63
+ wmsServiceUrl: '',
63
64
  };
64
65
  this.activeLayersHandler = this.activeLayersHandler.bind(this);
65
66
  this.activeLayersArray = {};
@@ -72,6 +73,8 @@ class MapViewer extends React.Component {
72
73
  this.bookmarkHandler = this.bookmarkHandler.bind(this);
73
74
  this.prepackageHandler = this.prepackageHandler.bind(this);
74
75
  this.uploadFileHandler = this.uploadFileHandler.bind(this);
76
+ this.uploadFileErrorHandler = this.uploadFileErrorHandler.bind(this);
77
+ this.uploadUrlServiceHandler = this.uploadUrlServiceHandler.bind(this);
75
78
  //this.getTaxonomy = this.props.getTaxonomy.bind(this);
76
79
  }
77
80
 
@@ -97,20 +100,57 @@ class MapViewer extends React.Component {
97
100
  this.setState({ bookmarkData: newBookmarkData });
98
101
  }
99
102
 
103
+ // Function to remove circular references
104
+ removeCircularReferences(obj) {
105
+ const seen = new WeakSet();
106
+ return JSON.parse(
107
+ JSON.stringify(obj, (key, value) => {
108
+ if (typeof value === 'object' && value !== null) {
109
+ if (seen.has(value)) {
110
+ return;
111
+ }
112
+ seen.add(value);
113
+ }
114
+ return value;
115
+ }),
116
+ );
117
+ }
118
+
100
119
  activeLayersHandler(newActiveLayers) {
101
- this.activeLayers = newActiveLayers;
102
- mapStatus.activeLayers = newActiveLayers;
103
- sessionStorage.setItem('mapStatus', JSON.stringify(mapStatus));
120
+ try {
121
+ const layersWithoutCircularReferences = this.removeCircularReferences(
122
+ newActiveLayers,
123
+ );
124
+ this.activeLayers = layersWithoutCircularReferences;
125
+ mapStatus.activeLayers = layersWithoutCircularReferences;
126
+ sessionStorage.setItem('mapStatus', JSON.stringify(mapStatus));
127
+ } catch (error) {
128
+ //setup some sort of error message
129
+ }
104
130
  }
105
131
 
106
132
  setCenterState(centerStatus) {
107
133
  mapStatus.center = centerStatus;
108
- sessionStorage.setItem('mapStatus', JSON.stringify(mapStatus));
134
+ try {
135
+ sessionStorage.setItem('mapStatus', JSON.stringify(mapStatus));
136
+ } catch (e) {
137
+ if (e.name === 'QuotaExceededError') {
138
+ sessionStorage.clear();
139
+ sessionStorage.setItem('mapStatus', JSON.stringify(mapStatus));
140
+ }
141
+ }
109
142
  }
110
143
 
111
144
  setZoomState(zoomStatus) {
112
145
  mapStatus.zoom = zoomStatus;
113
- sessionStorage.setItem('mapStatus', JSON.stringify(mapStatus));
146
+ try {
147
+ sessionStorage.setItem('mapStatus', JSON.stringify(mapStatus));
148
+ } catch (e) {
149
+ if (e.name === 'QuotaExceededError') {
150
+ sessionStorage.clear();
151
+ sessionStorage.setItem('mapStatus', JSON.stringify(mapStatus));
152
+ }
153
+ }
114
154
  }
115
155
 
116
156
  recoverState() {
@@ -129,6 +169,33 @@ class MapViewer extends React.Component {
129
169
  this.setState({ uploadedFile: message });
130
170
  }
131
171
 
172
+ uploadFileErrorHandler = (error) => {
173
+ this.setState({
174
+ showInfoPopup: true,
175
+ infoPopupType: 'uploadError',
176
+ });
177
+ setTimeout(() => {
178
+ this.setState({
179
+ showInfoPopup: false,
180
+ infoPopupType: '',
181
+ });
182
+ }, 3000);
183
+ };
184
+
185
+ uploadUrlServiceHandler = (newUrl) => {
186
+ if (newUrl && typeof newUrl === 'string') {
187
+ this.setState({ wmsServiceUrl: newUrl });
188
+ } else {
189
+ //set popup error messsage
190
+ this.setState({ wmsServiceUrl: '' });
191
+ }
192
+ };
193
+
194
+ serviceAddedHandler = () => {
195
+ // Reset wmsServiceUrl without causing a new update of the children
196
+ this.setState({ wmsServiceUrl: '' });
197
+ };
198
+
132
199
  loader() {
133
200
  return loadModules([
134
201
  'esri/WebMap',
@@ -275,6 +342,13 @@ class MapViewer extends React.Component {
275
342
  sessionStorage.clear();
276
343
  sessionStorage.setItem('toc_panel_scrolls', toc_panel_scrolls);
277
344
  }
345
+ // if (
346
+ // prevState.wmsServiceUrl !== this.state.wmsServiceUrl &&
347
+ // this.state.wmsServiceUrl === ''
348
+ // ) {
349
+ // // Reset wmsServiceUrl without causing a new update of the children
350
+ // this.setState({ wmsServiceUrl: '' });
351
+ // }
278
352
  }
279
353
 
280
354
  componentWillUnmount() {
@@ -432,6 +506,10 @@ class MapViewer extends React.Component {
432
506
  prepackageHandler={this.prepackageHandler}
433
507
  uploadedFile={this.state.uploadedFile}
434
508
  uploadFileHandler={this.uploadFileHandler}
509
+ uploadUrlServiceHandler={this.uploadUrlServiceHandler}
510
+ wmsServiceUrl={this.state.wmsServiceUrl}
511
+ onServiceAdded={this.serviceAddedHandler}
512
+ uploadFileErrorHandler={this.uploadFileErrorHandler}
435
513
  //getTaxonomy={this.getTaxonomy}
436
514
  />
437
515
  ); //call conf
@@ -459,6 +537,9 @@ class MapViewer extends React.Component {
459
537
  view={this.view}
460
538
  map={this.map}
461
539
  mapViewer={this}
540
+ wmsServiceUrl={this.state.wmsServiceUrl}
541
+ uploadUrlServiceHandler={this.uploadUrlServiceHandler}
542
+ uploadfileErrorHandler={this.uploadFileErrorHandler}
462
543
  />
463
544
  );
464
545
  }
@@ -412,6 +412,8 @@ class MenuWidget extends React.Component {
412
412
  draggedElements: [],
413
413
  popup: false,
414
414
  filterArrow: 'chevron-down',
415
+ wmsUserServiceLayers: [],
416
+ //wmsServiceUrl: this.props.wmsServiceUrl,
415
417
  };
416
418
  this.menuClass =
417
419
  'esri-icon-drag-horizontal esri-widget--button esri-widget esri-interactive';
@@ -429,6 +431,7 @@ class MenuWidget extends React.Component {
429
431
  this.getLimitScale = this.getLimitScale.bind(this);
430
432
  this.handleOpenPopup = this.handleOpenPopup.bind(this);
431
433
  this.filtersApplied = false;
434
+ this.filtersApplied = false;
432
435
  // add zoomend listener to map to show/hide zoom in message
433
436
  this.view.watch('stationary', (isStationary) => {
434
437
  let snowAndIceInSessionStorage = sessionStorage.getItem('snowAndIce');
@@ -549,6 +552,15 @@ class MenuWidget extends React.Component {
549
552
  );
550
553
  }
551
554
 
555
+ static getDerivedStateFromProps(nextProps, prevState) {
556
+ if (nextProps.wmsServiceUrl !== prevState.wmsServiceUrl) {
557
+ return {
558
+ wmsServiceUrl: nextProps.wmsServiceUrl,
559
+ };
560
+ }
561
+ return null;
562
+ }
563
+
552
564
  stringMatch(str1, str2) {
553
565
  if (!str1 || !str2) {
554
566
  return '';
@@ -982,6 +994,12 @@ class MenuWidget extends React.Component {
982
994
  this.props.bookmarkHandler(bookmarkData);
983
995
  });
984
996
  });
997
+
998
+ // Add "My Services" component to the UI
999
+ const myServicesComponent = this.createMyServicesComponent();
1000
+ const myServicesContainer = document.createElement('div');
1001
+ ReactDOM.render(myServicesComponent, myServicesContainer);
1002
+ this.container.current.appendChild(myServicesContainer.firstChild);
985
1003
  }
986
1004
 
987
1005
  setSliderTag(val) {
@@ -1135,6 +1153,10 @@ class MenuWidget extends React.Component {
1135
1153
  }
1136
1154
  }
1137
1155
  }
1156
+
1157
+ // Add "My Services" component
1158
+ components.push(this.createMyServicesComponent());
1159
+
1138
1160
  return components;
1139
1161
  }
1140
1162
 
@@ -1935,6 +1957,252 @@ class MenuWidget extends React.Component {
1935
1957
  }
1936
1958
  }
1937
1959
 
1960
+ async handleNewMapServiceLayer(viewService) {
1961
+ // Check if the URL is already in state to prevent duplicates
1962
+ if (
1963
+ this.state.wmsUserServiceLayers.some((layer) => layer.url === viewService)
1964
+ ) {
1965
+ return;
1966
+ }
1967
+
1968
+ // CREATE A TEMPORARY LAYER OBJECT TO EXTRACT DATA
1969
+ let resourceLayer;
1970
+ try {
1971
+ resourceLayer = new WMSLayer({
1972
+ url: viewService,
1973
+ });
1974
+ } catch (error) {
1975
+ // Set a popup error message in here
1976
+ this.props.uploadFileErrorHandler();
1977
+ return;
1978
+ }
1979
+
1980
+ const legendRequest =
1981
+ 'request=GetLegendGraphic&version=1.0.0&format=image/png&layer=';
1982
+ let layerId, layerObj;
1983
+
1984
+ await resourceLayer.load().then(() => {
1985
+ // EXTRACT DATA FOR NEW LAYER REQUEST
1986
+ let { featureInfoUrl, title } = resourceLayer;
1987
+ layerId = title.toUpperCase().replace(/ /g, '_');
1988
+ const constructedSublayers = resourceLayer.sublayers?.items?.map(
1989
+ (sublayer) => {
1990
+ const { index, name, title, legendUrl, featureInfoUrl } = sublayer;
1991
+ return {
1992
+ index,
1993
+ name,
1994
+ title,
1995
+ popupEnabled: true,
1996
+ queryable: true,
1997
+ visible: true,
1998
+ legendEnabled: true,
1999
+ legendUrl: legendUrl
2000
+ ? legendUrl
2001
+ : viewService + legendRequest + name,
2002
+ featureInfoUrl: featureInfoUrl ? featureInfoUrl : viewService,
2003
+ };
2004
+ },
2005
+ );
2006
+
2007
+ layerObj = {
2008
+ url: viewService,
2009
+ featureInfoFormat: 'text/html',
2010
+ featureInfoUrl: featureInfoUrl ? featureInfoUrl : viewService,
2011
+ title,
2012
+ legendEnabled: true,
2013
+ sublayers: constructedSublayers,
2014
+ ViewService: viewService,
2015
+ LayerId: layerId,
2016
+ };
2017
+ return layerObj;
2018
+ });
2019
+
2020
+ // DESTROY THE TEMPORARY LAYER OBJECT
2021
+ resourceLayer.destroy();
2022
+ resourceLayer = null; // Important: clear the reference to the old layer
2023
+
2024
+ const { LayerId } = layerObj;
2025
+
2026
+ // Check if the layer already exists in this.layers before adding
2027
+ if (!this.layers[LayerId]) {
2028
+ try {
2029
+ // Create and add the new layer
2030
+ this.layers[LayerId] = new WMSLayer(layerObj);
2031
+
2032
+ // Update state to include the new layer, which will trigger componentDidUpdate
2033
+ this.setState((prevState) => {
2034
+ return {
2035
+ wmsUserServiceLayers: [
2036
+ ...prevState.wmsUserServiceLayers,
2037
+ this.layers[LayerId],
2038
+ ],
2039
+ };
2040
+ });
2041
+
2042
+ this.props.onServiceAdded();
2043
+
2044
+ // Add the layer to the map
2045
+ const node = document.getElementById(LayerId);
2046
+ if (node) {
2047
+ node.checked = true;
2048
+ this.toggleLayer(node);
2049
+ }
2050
+ } catch (error) {
2051
+ // Set a popup error message in here
2052
+ this.props.uploadFileErrorHandler();
2053
+ return;
2054
+ }
2055
+ }
2056
+ }
2057
+
2058
+ createMyServicesComponent() {
2059
+ let dropdowns = document.querySelectorAll('.map-menu-dropdown');
2060
+ let i = dropdowns.length === 0 ? 0 : dropdowns.length - 1;
2061
+ let componentId = `component_${i}`;
2062
+ let dropdownId = `dropdown_${i}`;
2063
+
2064
+ // Create "My Services" component from the start and set its display to none
2065
+ let myServicesStyle =
2066
+ this.state.wmsUserServiceLayers.length > 0 ? {} : { display: 'none' };
2067
+ return (
2068
+ <div
2069
+ className="map-menu-dropdown"
2070
+ id={componentId}
2071
+ key="a5"
2072
+ style={myServicesStyle}
2073
+ >
2074
+ <div
2075
+ id={dropdownId}
2076
+ className="ccl-expandable__button"
2077
+ aria-expanded="false"
2078
+ onClick={this.toggleDropdownContent.bind(this)}
2079
+ onKeyDown={this.toggleDropdownContent.bind(this)}
2080
+ tabIndex="0"
2081
+ role="button"
2082
+ >
2083
+ <div className="dropdown-icon">
2084
+ <FontAwesomeIcon icon={['fas', 'caret-right']} />
2085
+ </div>
2086
+ {<span>{'My Service'}</span>}
2087
+ </div>
2088
+ <div className="map-menu-components-container" id="map-menu-services" />
2089
+ </div>
2090
+ );
2091
+ }
2092
+
2093
+ createUserServices(serviceLayers) {
2094
+ const fieldset = document.getElementById('map-menu-services');
2095
+ if (!fieldset) return;
2096
+
2097
+ // Create an array of all layer elements
2098
+ const layerElements = serviceLayers.map((layer, index) => {
2099
+ const { LayerId, title, description } = layer;
2100
+ const parentIndex = this.layers[layer.id];
2101
+ const checkboxId = LayerId;
2102
+
2103
+ return (
2104
+ <div className="map-menu-dataset-dropdown">
2105
+ <fieldset className="ccl-fieldset">
2106
+ <div className="ccl-expandable__button" aria-expanded="false">
2107
+ <div className="dropdown-icon">
2108
+ <div className="ccl-form map-dataset-checkbox">
2109
+ <div
2110
+ className="ccl-form-group map-menu-service"
2111
+ key={`service_layer_${LayerId}`}
2112
+ >
2113
+ <input
2114
+ type="checkbox"
2115
+ id={checkboxId}
2116
+ parentid={parentIndex}
2117
+ layerid={LayerId}
2118
+ name="layerCheckbox"
2119
+ value="name"
2120
+ className="ccl-checkbox ccl-required ccl-form-check-input"
2121
+ title={layer.title}
2122
+ onChange={(e) => {
2123
+ this.toggleLayer(e.target);
2124
+ }}
2125
+ />
2126
+ <label
2127
+ className="ccl-form-check-label"
2128
+ htmlFor={checkboxId}
2129
+ >
2130
+ <legend className="ccl-form-legend">
2131
+ {description ? (
2132
+ <Popup
2133
+ trigger={<span>{title}</span>}
2134
+ content={description}
2135
+ basic
2136
+ className="custom"
2137
+ style={{ transform: 'translateX(-4rem)' }}
2138
+ />
2139
+ ) : (
2140
+ <span>{title || `Layer ${index + 1}`}</span>
2141
+ )}
2142
+ </legend>
2143
+ </label>
2144
+ <span
2145
+ className="map-menu-icon map-menu-service-icon"
2146
+ onClick={() => this.deleteServiceLayer(LayerId)}
2147
+ onKeyDown={() => this.deleteServiceLayer(LayerId)}
2148
+ tabIndex="0"
2149
+ role="button"
2150
+ >
2151
+ <FontAwesomeIcon icon={['fas', 'trash']} />
2152
+ </span>
2153
+ </div>
2154
+ </div>
2155
+ </div>
2156
+ </div>
2157
+ </fieldset>
2158
+ </div>
2159
+ );
2160
+ });
2161
+
2162
+ // Render all layers at once to avoid overwriting previous layers
2163
+ ReactDOM.render(layerElements, fieldset);
2164
+ }
2165
+
2166
+ deleteServiceLayer(elemId) {
2167
+ // Remove the layer from the map
2168
+ const node = document.getElementById(elemId);
2169
+ if (node) {
2170
+ node.checked = false;
2171
+ this.toggleLayer(node);
2172
+ }
2173
+
2174
+ // Delete from layers object
2175
+ if (this.layers[elemId]) delete this.layers[elemId];
2176
+
2177
+ // Remove from ArcGIS map
2178
+ let removeLayer = this.props.map.findLayerById(elemId) || null;
2179
+ if (removeLayer) {
2180
+ removeLayer.clear();
2181
+ removeLayer.destroy();
2182
+ this.props.map.remove(removeLayer);
2183
+ removeLayer = null;
2184
+ }
2185
+
2186
+ // Update state to trigger componentDidUpdate
2187
+ this.setState((prevState) => {
2188
+ const layerExists = prevState.wmsUserServiceLayers.some(
2189
+ (layer) => layer.LayerId === elemId,
2190
+ );
2191
+
2192
+ if (layerExists) {
2193
+ const newWmsUserServiceLayers = prevState.wmsUserServiceLayers.filter(
2194
+ (layer) => layer.LayerId !== elemId,
2195
+ );
2196
+ return {
2197
+ wmsUserServiceLayers: newWmsUserServiceLayers,
2198
+ };
2199
+ }
2200
+
2201
+ // If layer doesn't exist, return unchanged state to avoid issues
2202
+ return null;
2203
+ });
2204
+ }
2205
+
1938
2206
  /**
1939
2207
  * Method to show/hide a layer. Update checkboxes from dataset and products
1940
2208
  * @param {*} elem Is the checkbox
@@ -2140,17 +2408,23 @@ class MenuWidget extends React.Component {
2140
2408
  }
2141
2409
 
2142
2410
  async toggleLayer(elem) {
2143
- if (elem.checked) {
2411
+ const userService =
2412
+ this.state.wmsUserServiceLayers.find(
2413
+ (layer) => layer.LayerId === elem.id,
2414
+ ) || null;
2415
+ if (elem.checked && !userService) {
2144
2416
  this.findCheckedDatasetNoServiceToVisualize(elem);
2145
2417
  }
2146
2418
  if (this.layers[elem.id] === undefined) return;
2147
2419
  if (!this.visibleLayers) this.visibleLayers = {};
2148
2420
  if (!this.timeLayers) this.timeLayers = {};
2149
- let parentId = elem.getAttribute('parentid');
2150
- let productContainerId = document
2151
- .getElementById(parentId)
2152
- .closest('.map-menu-product-dropdown')
2153
- .getAttribute('productid');
2421
+ let parentId = !userService ? elem.getAttribute('parentid') : null;
2422
+ let productContainerId = !userService
2423
+ ? document
2424
+ .getElementById(parentId)
2425
+ .closest('.map-menu-product-dropdown')
2426
+ .getAttribute('productid')
2427
+ : null;
2154
2428
 
2155
2429
  let group = this.getGroup(elem);
2156
2430
  if (elem.checked) {
@@ -2158,7 +2432,8 @@ class MenuWidget extends React.Component {
2158
2432
  if (
2159
2433
  this.props.download ||
2160
2434
  this.location.search.includes('product=') ||
2161
- this.location.search.includes('dataset=')
2435
+ this.location.search.includes('dataset=') ||
2436
+ !userService
2162
2437
  ) {
2163
2438
  if (
2164
2439
  this.extentInitiated === false &&
@@ -2171,8 +2446,9 @@ class MenuWidget extends React.Component {
2171
2446
  }
2172
2447
  }
2173
2448
  if (
2174
- (elem.id.includes('all_lcc') || elem.id.includes('all_present')) &&
2175
- (this.layers['lc_filter'] || this.layers['lcc_filter'])
2449
+ ((elem.id.includes('all_lcc') || elem.id.includes('all_present')) &&
2450
+ (this.layers['lc_filter'] || this.layers['lcc_filter'])) ||
2451
+ !userService
2176
2452
  ) {
2177
2453
  let bookmarkHotspotFilter = localStorage.getItem(
2178
2454
  'bookmarkHotspotFilter',
@@ -2249,14 +2525,14 @@ class MenuWidget extends React.Component {
2249
2525
  if (nuts) {
2250
2526
  this.map.reorder(nuts, this.map.layers.items.length + 1);
2251
2527
  }
2252
- this.checkForHotspots(elem, productContainerId);
2528
+ if (!userService) this.checkForHotspots(elem, productContainerId);
2253
2529
  } else {
2254
2530
  sessionStorage.removeItem('downloadButtonClicked');
2255
2531
  sessionStorage.removeItem('timeSliderTag');
2256
2532
  this.deleteCheckedLayer(elem.id);
2257
2533
  this.layers[elem.id].opacity = 1;
2258
2534
  this.layers[elem.id].visible = false;
2259
- this.deleteFilteredLayer(elem.id);
2535
+ if (!userService) this.deleteFilteredLayer(elem.id);
2260
2536
  let mapLayer = this.map.findLayerById(elem.id);
2261
2537
  if (mapLayer) {
2262
2538
  if (mapLayer.type && mapLayer.type !== 'base-tile') mapLayer.clear();
@@ -2267,7 +2543,7 @@ class MenuWidget extends React.Component {
2267
2543
  delete this.visibleLayers[elem.id];
2268
2544
  delete this.timeLayers[elem.id];
2269
2545
  }
2270
- this.updateCheckDataset(parentId);
2546
+ if (!userService) this.updateCheckDataset(parentId);
2271
2547
  this.layersReorder();
2272
2548
  this.checkInfoWidget();
2273
2549
  // toggle custom legend for WMTS and TMS
@@ -2482,7 +2758,6 @@ class MenuWidget extends React.Component {
2482
2758
  for (var i in this.activeLayersJSON) {
2483
2759
  activeLayersArray.push(this.activeLayersJSON[i]);
2484
2760
  }
2485
-
2486
2761
  if (!activeLayersArray.length) {
2487
2762
  messageLayers && (messageLayers.style.display = 'block');
2488
2763
  } else messageLayers && (messageLayers.style.display = 'none');
@@ -2895,11 +3170,19 @@ class MenuWidget extends React.Component {
2895
3170
  }
2896
3171
 
2897
3172
  async FullExtentDataset(elem) {
3173
+ const serviceLayer = this.state.wmsUserServiceLayers.find(
3174
+ (layer) => layer.LayerId === elem.id,
3175
+ );
3176
+
3177
+ if (!serviceLayer) {
3178
+ this.findCheckedDataset(elem);
3179
+ } else {
3180
+ this.url = serviceLayer.ViewService;
3181
+ }
2898
3182
  let BBoxes = {};
2899
- this.findCheckedDataset(elem);
2900
3183
  if (this.url?.toLowerCase().endsWith('mapserver')) {
2901
3184
  BBoxes = await this.parseBBOXMAPSERVER(this.layers[elem.id]);
2902
- } else if (this.url?.toLowerCase().includes('wms')) {
3185
+ } else if (this.url?.toLowerCase().includes('wms') || serviceLayer) {
2903
3186
  await this.getCapabilities(this.url, 'wms');
2904
3187
  BBoxes = this.parseBBOXWMS(this.xml);
2905
3188
  } else if (this.url?.toLowerCase().includes('wmts')) {
@@ -2928,18 +3211,40 @@ class MenuWidget extends React.Component {
2928
3211
  }
2929
3212
 
2930
3213
  async fullExtent(elem) {
2931
- this.findCheckedDataset(elem);
3214
+ const serviceLayer = this.state.wmsUserServiceLayers.find(
3215
+ (layer) => layer.LayerId === elem.id,
3216
+ );
3217
+
3218
+ if (!serviceLayer) {
3219
+ this.findCheckedDataset(elem);
3220
+ } else {
3221
+ this.productId = null;
3222
+ this.url = serviceLayer.ViewService;
3223
+ }
2932
3224
  let BBoxes = {};
2933
3225
  let firstLayer;
3226
+ let landCoverAndLandUseMapping = document.querySelector('#component_0');
3227
+ let productIds = [];
3228
+ if (landCoverAndLandUseMapping) {
3229
+ const productElements = landCoverAndLandUseMapping.querySelectorAll(
3230
+ '.map-menu-product-dropdown',
3231
+ );
3232
+ productElements.forEach((productElement) => {
3233
+ const productId = productElement.getAttribute('productid');
3234
+ if (productId) {
3235
+ productIds.push(productId);
3236
+ }
3237
+ });
3238
+ }
2934
3239
 
2935
- if (this.productId.includes('333e4100b79045daa0ff16466ac83b7f')) {
3240
+ if (this.productId?.includes('333e4100b79045daa0ff16466ac83b7f')) {
2936
3241
  //global dynamic landCover
2937
3242
  this.findDatasetBoundingBox(elem);
2938
3243
 
2939
3244
  BBoxes = this.parseBBOXJSON(this.dataBBox);
2940
3245
  } else if (
2941
- this.productId.includes('fe8209dffe13454891cea05998c8e456') || //Low Resolution Vegetation Parameters
2942
- this.productId.includes('8914fde2241a4035818af8f0264fd55e') // Water Parameters
3246
+ this.productId?.includes('fe8209dffe13454891cea05998c8e456') || // Low Resolution Vegetation Parameters
3247
+ this.productId?.includes('8914fde2241a4035818af8f0264fd55e') // Water Parameters
2943
3248
  ) {
2944
3249
  if (
2945
3250
  this.layers[elem.id].fullExtents &&
@@ -2958,7 +3263,7 @@ class MenuWidget extends React.Component {
2958
3263
  }
2959
3264
  } else if (this.url?.toLowerCase().endsWith('mapserver')) {
2960
3265
  BBoxes = await this.parseBBOXMAPSERVER(this.layers[elem.id]);
2961
- } else if (this.url?.toLowerCase().includes('wms')) {
3266
+ } else if (this.url?.toLowerCase().includes('wms') || serviceLayer) {
2962
3267
  await this.getCapabilities(this.url, 'wms');
2963
3268
  BBoxes = this.parseBBOXWMS(this.xml);
2964
3269
  } else if (this.url?.toLowerCase().includes('wmts')) {
@@ -2973,72 +3278,87 @@ class MenuWidget extends React.Component {
2973
3278
  ) {
2974
3279
  if (
2975
3280
  this.extentInitiated === false &&
2976
- !this.productId.includes('333e4100b79045daa0ff16466ac83b7f') &&
3281
+ !this.productId?.includes('333e4100b79045daa0ff16466ac83b7f') &&
2977
3282
  this.location.search !== ''
2978
3283
  ) {
2979
3284
  firstLayer = BBoxes.dataset;
2980
- } else if (this.productId.includes('130299ac96e54c30a12edd575eff80f7')) {
3285
+ }
3286
+ if (productIds?.includes(this.productId)) {
3287
+ // Your code here for when productIds includes this.productId
2981
3288
  let str = elem.parentNode.outerHTML;
2982
- let match = str.match(/layerid="(\d+)"/);
3289
+ let match = str.match(/layerid="([a-zA-Z0-9_:-]+)"/);
2983
3290
  let layerid = match ? match[1] : null;
2984
3291
  if (layerid === null || layerid === undefined) return;
2985
- if (this.url?.toLowerCase().endsWith('mapserver')) {
2986
- switch (layerid) {
2987
- case '1':
2988
- firstLayer = this.findBBoxById(BBoxes, 13);
2989
- break;
2990
- case '2':
2991
- firstLayer = this.findBBoxById(BBoxes, 12);
2992
- break;
2993
- case '3':
2994
- firstLayer = this.findBBoxById(BBoxes, 11);
2995
- break;
2996
- case '4':
2997
- firstLayer = this.findBBoxById(BBoxes, 10);
2998
- break;
2999
- case '5':
3000
- firstLayer = this.findBBoxById(BBoxes, 9);
3001
- break;
3002
- case '7':
3003
- firstLayer = this.findBBoxById(BBoxes, 7);
3004
- break;
3005
- case '8':
3006
- firstLayer = this.findBBoxById(BBoxes, 6);
3007
- break;
3008
- case '9':
3009
- firstLayer = this.findBBoxById(BBoxes, 5);
3010
- break;
3011
- case '10':
3012
- firstLayer = this.findBBoxById(BBoxes, 4);
3013
- break;
3014
- case '11':
3015
- firstLayer = this.findBBoxById(BBoxes, 3);
3016
- break;
3017
- case '12':
3018
- firstLayer = this.findBBoxById(BBoxes, 0);
3019
- break;
3020
- case '13':
3021
- firstLayer = this.findBBoxById(BBoxes, 1);
3022
- break;
3023
- default:
3024
- return;
3025
- }
3026
- } else {
3027
- if (layerid === '12' || layerid === '13') {
3028
- firstLayer = BBoxes['dataset'];
3029
- } else if (layerid === '1' || layerid === '7') {
3030
- firstLayer = BBoxes[Object.keys(BBoxes)[0]];
3031
- } else if (layerid === '2' || layerid === '8') {
3032
- firstLayer = BBoxes[Object.keys(BBoxes)[1]];
3033
- } else if (layerid === '3' || layerid === '9') {
3034
- firstLayer = BBoxes[Object.keys(BBoxes)[2]];
3035
- } else if (layerid === '4' || layerid === '10') {
3036
- firstLayer = BBoxes[Object.keys(BBoxes)[3]];
3037
- } else if (layerid === '5' || layerid === '11') {
3038
- firstLayer = BBoxes[Object.keys(BBoxes)[4]];
3292
+ if (
3293
+ this.productId?.includes('130299ac96e54c30a12edd575eff80f7') &&
3294
+ layerid.length <= 2
3295
+ ) {
3296
+ //let match = str.match(/layerid="(\d+)"/);
3297
+ //let layerid = match ? match[1] : null;
3298
+ if (this.url?.toLowerCase().endsWith('mapserver')) {
3299
+ switch (layerid) {
3300
+ case '1':
3301
+ firstLayer = this.findBBoxById(BBoxes, 13);
3302
+ break;
3303
+ case '2':
3304
+ firstLayer = this.findBBoxById(BBoxes, 12);
3305
+ break;
3306
+ case '3':
3307
+ firstLayer = this.findBBoxById(BBoxes, 11);
3308
+ break;
3309
+ case '4':
3310
+ firstLayer = this.findBBoxById(BBoxes, 10);
3311
+ break;
3312
+ case '5':
3313
+ firstLayer = this.findBBoxById(BBoxes, 9);
3314
+ break;
3315
+ case '7':
3316
+ firstLayer = this.findBBoxById(BBoxes, 7);
3317
+ break;
3318
+ case '8':
3319
+ firstLayer = this.findBBoxById(BBoxes, 6);
3320
+ break;
3321
+ case '9':
3322
+ firstLayer = this.findBBoxById(BBoxes, 5);
3323
+ break;
3324
+ case '10':
3325
+ firstLayer = this.findBBoxById(BBoxes, 4);
3326
+ break;
3327
+ case '11':
3328
+ firstLayer = this.findBBoxById(BBoxes, 3);
3329
+ break;
3330
+ case '12':
3331
+ firstLayer = this.findBBoxById(BBoxes, 0);
3332
+ break;
3333
+ case '13':
3334
+ firstLayer = this.findBBoxById(BBoxes, 1);
3335
+ break;
3336
+ default:
3337
+ return;
3338
+ }
3039
3339
  } else {
3040
- firstLayer = BBoxes['dataset'];
3340
+ if (layerid === '12' || layerid === '13') {
3341
+ firstLayer = BBoxes['dataset'];
3342
+ } else if (layerid === '1' || layerid === '7') {
3343
+ firstLayer = BBoxes[Object.keys(BBoxes)[0]];
3344
+ } else if (layerid === '2' || layerid === '8') {
3345
+ firstLayer = BBoxes[Object.keys(BBoxes)[1]];
3346
+ } else if (layerid === '3' || layerid === '9') {
3347
+ firstLayer = BBoxes[Object.keys(BBoxes)[2]];
3348
+ } else if (layerid === '4' || layerid === '10') {
3349
+ firstLayer = BBoxes[Object.keys(BBoxes)[3]];
3350
+ } else if (layerid === '5' || layerid === '11') {
3351
+ firstLayer = BBoxes[Object.keys(BBoxes)[4]];
3352
+ } else {
3353
+ firstLayer = BBoxes['dataset'];
3354
+ }
3041
3355
  }
3356
+ } else if (layerid.length > 2) {
3357
+ firstLayer = BBoxes[layerid];
3358
+ } else if (
3359
+ this.productId?.includes('333e4100b79045daa0ff16466ac83b7f')
3360
+ ) {
3361
+ firstLayer = BBoxes[0];
3042
3362
  }
3043
3363
  } else if (
3044
3364
  elem.id.includes('all_present') ||
@@ -3047,11 +3367,11 @@ class MenuWidget extends React.Component {
3047
3367
  elem.id.includes('protected_areas')
3048
3368
  ) {
3049
3369
  firstLayer = BBoxes['all_present_lc_a_pol'];
3370
+ } else if (serviceLayer) {
3371
+ // Full extent treatment for service layers
3372
+ firstLayer = BBoxes['dataset'];
3050
3373
  } else {
3051
- // Takes the BBOX corresponding to the layer.
3052
- if (this.productId.includes('333e4100b79045daa0ff16466ac83b7f')) {
3053
- firstLayer = BBoxes[0];
3054
- } else firstLayer = BBoxes[elem.attributes.layerid.value];
3374
+ firstLayer = BBoxes[elem.attributes.layerid.value];
3055
3375
  }
3056
3376
 
3057
3377
  let myExtent = new Extent({
@@ -3588,8 +3908,15 @@ class MenuWidget extends React.Component {
3588
3908
  }, 100);
3589
3909
  }
3590
3910
 
3911
+ waitForDataFill(obj) {
3912
+ while (obj.length === 0) {
3913
+ new Promise((resolve) => setTimeout(resolve, 100)); // wait for 100ms
3914
+ }
3915
+ return obj;
3916
+ }
3917
+
3591
3918
  setLegendOpacity() {
3592
- const collection = document.getElementsByClassName('esri-legend__symbol');
3919
+ let collection = document.getElementsByClassName('esri-legend__symbol');
3593
3920
 
3594
3921
  Array.prototype.forEach.call(collection, function (element) {
3595
3922
  let img = {};
@@ -3667,7 +3994,15 @@ class MenuWidget extends React.Component {
3667
3994
  * @param {*} id id from elem
3668
3995
  */
3669
3996
  eyeLayer(elem) {
3670
- this.findCheckedDataset(elem);
3997
+ // Check if this is a user service layer
3998
+ const isUserServiceLayer = this.state.wmsUserServiceLayers.some(
3999
+ (layer) => layer.LayerId === elem.id,
4000
+ );
4001
+
4002
+ // Only call findCheckedDataset for non-service layers (this method looks for parent datasets)
4003
+ if (!isUserServiceLayer) {
4004
+ this.findCheckedDataset(elem);
4005
+ }
3671
4006
  if (
3672
4007
  this.visibleLayers[elem.id] &&
3673
4008
  this.visibleLayers[elem.id][1] === 'eye'
@@ -3713,7 +4048,11 @@ class MenuWidget extends React.Component {
3713
4048
  this.visibleLayers[elem.id] = ['fas', 'eye'];
3714
4049
  }
3715
4050
 
3716
- if (this.productId.includes('333e4100b79045daa0ff16466ac83b7f')) {
4051
+ if (
4052
+ !isUserServiceLayer &&
4053
+ this.productId &&
4054
+ this.productId.includes('333e4100b79045daa0ff16466ac83b7f')
4055
+ ) {
3717
4056
  // global dynamic land cover
3718
4057
  if (this.visibleLayers[elem.id][1] === 'eye-slash') {
3719
4058
  this.map.findLayerById(elem.id).visible = false;
@@ -3739,9 +4078,44 @@ class MenuWidget extends React.Component {
3739
4078
  this.setState({});
3740
4079
  }
3741
4080
 
3742
- componentDidUpdate(prevState, prevProps) {
4081
+ componentDidUpdate(prevProps, prevState) {
3743
4082
  if (this.props.download) return;
3744
4083
 
4084
+ if (prevProps.wmsServiceUrl !== this.props.wmsServiceUrl) {
4085
+ const { wmsServiceUrl } = this.props;
4086
+ if (
4087
+ wmsServiceUrl &&
4088
+ typeof wmsServiceUrl === 'string' &&
4089
+ wmsServiceUrl !== ''
4090
+ ) {
4091
+ this.handleNewMapServiceLayer(wmsServiceUrl);
4092
+ }
4093
+ }
4094
+
4095
+ if (prevState.wmsUserServiceLayers !== this.state.wmsUserServiceLayers) {
4096
+ this.createUserServices(this.state.wmsUserServiceLayers);
4097
+ }
4098
+ if (
4099
+ this.state.wmsUserServiceLayers.length > 0 &&
4100
+ prevState.wmsUserServiceLayers.length === 0
4101
+ ) {
4102
+ // Close other tabs and open "My Services"
4103
+ let dropdownsMapMenu = document.querySelectorAll('.map-menu-dropdown');
4104
+ let i = dropdownsMapMenu.length - 1;
4105
+ // let j = 0;
4106
+ let dropdownId = 'dropdown_' + i;
4107
+ // let myServicesId = 'component_' + i;
4108
+ // let mapMenuServiceDropdownId = 'product_' + i + '_' + j;
4109
+ dropdownsMapMenu.forEach((dropdown) => {
4110
+ if (dropdown.id !== dropdownId) {
4111
+ dropdown
4112
+ .querySelector('.ccl-expandable__button')
4113
+ .setAttribute('aria-expanded', 'false');
4114
+ }
4115
+ });
4116
+ document.getElementById(dropdownId).setAttribute('aria-expanded', 'true');
4117
+ }
4118
+
3745
4119
  if (sessionStorage.getItem('snowAndIce') === 'true') {
3746
4120
  //grab all checkedLayers from sessionstorage store them in checkedLayeers
3747
4121
  let checkedLayers = JSON.parse(sessionStorage.getItem('checkedLayers'));
@@ -3823,6 +4197,12 @@ class MenuWidget extends React.Component {
3823
4197
  let layerId = layer.getAttribute('layer-id');
3824
4198
  let elem = document.getElementById(layerId);
3825
4199
  this.deleteCrossEvent(elem);
4200
+ if (
4201
+ this.state.wmsUserServiceLayers ||
4202
+ this.state.wmsUserServiceLayers.length > 0
4203
+ ) {
4204
+ this.setState({ wmsUserServiceLayers: [] });
4205
+ }
3826
4206
  });
3827
4207
  }
3828
4208
 
@@ -26,6 +26,7 @@ class UploadWidget extends React.Component {
26
26
  'esri-icon-sketch-rectangle esri-widget--button esri-widget esri-interactive';
27
27
  this.mapviewer_config = this.props.mapviewer_config;
28
28
  this.fileInput = createRef();
29
+ this.uploadUrlServiceHandler = this.props.uploadUrlServiceHandler;
29
30
  }
30
31
 
31
32
  loader() {
@@ -107,15 +108,15 @@ class UploadWidget extends React.Component {
107
108
  clearWidget() {
108
109
  window.document.querySelector('.pan-container').style.display = 'none';
109
110
  this.props.mapViewer.view.popup.close();
110
- const { wmsLayer } = this.state;
111
- if (wmsLayer) {
112
- this.props.view.map.remove(wmsLayer);
113
- // this.props.view.graphics.removeAll();
114
- }
115
- this.setState({
116
- wmsLayer: null,
117
- wmsServiceUrl: '',
118
- });
111
+ //const { wmsLayer } = this.state;
112
+ //if (wmsLayer) {
113
+ // this.props.view.map.remove(wmsLayer);
114
+ // // this.props.view.graphics.removeAll();
115
+ //}
116
+ //this.setState({
117
+ // wmsLayer: null,
118
+ // wmsServiceUrl: '',
119
+ //});
119
120
 
120
121
  document.querySelector('.esri-attribution__powered-by').style.display =
121
122
  'none';
@@ -125,33 +126,32 @@ class UploadWidget extends React.Component {
125
126
  this.setState({ wmsServiceUrl: event.target.value });
126
127
  };
127
128
 
128
- handleUploadService = async () => {
129
- const { wmsServiceUrl, wmsLayer } = this.state;
130
- if (wmsLayer) {
131
- this.props.view.map.remove(wmsLayer);
132
- }
129
+ handleUploadService = () => {
130
+ const { wmsServiceUrl } = this.state;
133
131
 
134
- try {
135
- const newWmsLayer = new WMSLayer({
136
- url: wmsServiceUrl,
137
- });
138
-
139
- await newWmsLayer.load();
140
- this.props.view.map.add(newWmsLayer);
132
+ if (
133
+ wmsServiceUrl &&
134
+ wmsServiceUrl.trim() !== '' &&
135
+ wmsServiceUrl.toLowerCase().includes('wms')
136
+ ) {
137
+ this.uploadUrlServiceHandler(wmsServiceUrl);
141
138
  this.setState({
142
- wmsLayer: newWmsLayer,
143
- showInfoPopup: false,
144
- infoPopupType: '',
145
139
  wmsServiceUrl: '',
146
140
  });
147
- } catch (error) {
141
+ } else {
148
142
  this.setState({
149
143
  showInfoPopup: true,
150
144
  infoPopupType: 'uploadError',
151
- wmsServiceUrl: '',
152
145
  });
146
+ setTimeout(() => {
147
+ this.setState({
148
+ showInfoPopup: false,
149
+ infoPopupType: '',
150
+ });
151
+ }, 3000);
153
152
  }
154
153
  };
154
+
155
155
  /**
156
156
  * This method is executed after the render method is executed
157
157
  */
@@ -160,6 +160,14 @@ class UploadWidget extends React.Component {
160
160
  this.props.view.when(() => {
161
161
  this.container.current !== null &&
162
162
  this.props.view.ui.add(this.container.current, 'top-right');
163
+ //load an empty wms layer to use the variable
164
+ const wmsLayer = new WMSLayer({
165
+ url: '',
166
+ title: 'WMS Layer',
167
+ });
168
+ this.setState({
169
+ wmsLayer: wmsLayer,
170
+ });
163
171
  });
164
172
  }
165
173
 
@@ -773,6 +773,10 @@ div.upload-container.esri-component
773
773
  text-decoration: none;
774
774
  }
775
775
 
776
+ .land .map-menu-icon.map-menu-service-icon {
777
+ margin-left: 1.75rem;
778
+ }
779
+
776
780
  .map-menu-dropdown i:hover {
777
781
  color: black;
778
782
  }