@eeacms/volto-arcgis-block 0.1.476 → 0.1.478

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/.env CHANGED
@@ -1 +1 @@
1
- JEST_USE_SETUP=OFF # Jest configuration variables: ON, OFF
1
+ JEST_USE_SETUP=OFF # Jest configuration variables: ON, OFF
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.478](https://github.com/eea/volto-arcgis-block/compare/0.1.477...0.1.478) - 3 September 2026
8
+
9
+ #### :house: Internal changes
10
+
11
+ - style: Automated code fix [eea-jenkins - [`60974df`](https://github.com/eea/volto-arcgis-block/commit/60974df4c4d0af3f58e5b7845d408a9d0f022ccd)]
12
+
13
+ #### :hammer_and_wrench: Others
14
+
15
+ - (bug): Update WMS endpoint and parameters to restore map layer visualization [Unai Bolivar - [`1597090`](https://github.com/eea/volto-arcgis-block/commit/15970909fa22e24c78b2f6b3d83eb304e7fd039a)]
16
+ ### [0.1.477](https://github.com/eea/volto-arcgis-block/compare/0.1.476...0.1.477) - 2 September 2026
17
+
18
+ #### :hammer_and_wrench: Others
19
+
20
+ - (lint): lint and prettified [Unai Bolivar - [`e340270`](https://github.com/eea/volto-arcgis-block/commit/e340270b53e3591428cc13acb69041a6f90a448e)]
21
+ - (chore): update hotspots widget for hsm II data [Unai Bolivar - [`9c9d672`](https://github.com/eea/volto-arcgis-block/commit/9c9d67254f5f69faf23d6aa6cb721b7cdafe9b16)]
22
+ - (task): refactor hotspot widget to comply with updated JSON for geo klc hot spot data. [Unai Bolivar - [`39e8be7`](https://github.com/eea/volto-arcgis-block/commit/39e8be7e0fd0e1bfbf3dfe42e43ec7e3eabaafca)]
7
23
  ### [0.1.476](https://github.com/eea/volto-arcgis-block/compare/0.1.475...0.1.476) - 31 August 2026
8
24
 
9
25
  ### [0.1.475](https://github.com/eea/volto-arcgis-block/compare/0.1.474...0.1.475) - 19 August 2026
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-arcgis-block",
3
- "version": "0.1.476",
3
+ "version": "0.1.478",
4
4
  "description": "volto-arcgis-block: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: CodeSyntax",
@@ -137,80 +137,192 @@ class HotspotWidget extends React.Component {
137
137
  return legendLinkUrl + legend;
138
138
  }
139
139
 
140
+ getHotspotWmsConfig() {
141
+ return {
142
+ endpoint:
143
+ this.urls.hotspotWmsEndpoint ||
144
+ 'https://geospatial.jrc.ec.europa.eu/geoserver/hsm/wms',
145
+ version: this.urls.hotspotWmsVersion || '1.1.0',
146
+ srs: this.urls.hotspotWmsSrs || 'EPSG:3857',
147
+ layerNames: this.urls.hotspotWmsLayerNames || {
148
+ lcc: {
149
+ a: 'hsm:lcc_a_pols',
150
+ b: 'hsm:lcc_b_pols',
151
+ },
152
+ lc: {
153
+ a: 'hsm:present_lc_a_pols',
154
+ b: 'hsm:present_lc_b_pols',
155
+ },
156
+ klc: 'hsm:cop_klc',
157
+ pa: 'hsm:protected_areas',
158
+ },
159
+ };
160
+ }
161
+
162
+ resolveLayerVariant(activeLayers, type) {
163
+ if (!Array.isArray(activeLayers) || !activeLayers.length) return 'b';
164
+ const hasVariantA = activeLayers.some((layer) =>
165
+ layer.includes(type === 'lcc' ? 'all_lcc_a_pol' : 'all_present_lc_a_pol'),
166
+ );
167
+ return hasVariantA ? 'a' : 'b';
168
+ }
169
+
170
+ resolveWmsLayerName(type, variant) {
171
+ const config = this.getHotspotWmsConfig();
172
+ if (type === 'lcc') {
173
+ return config.layerNames.lcc[variant] || config.layerNames.lcc.b;
174
+ }
175
+ if (type === 'lc') {
176
+ return config.layerNames.lc[variant] || config.layerNames.lc.b;
177
+ }
178
+ if (type === 'klc') {
179
+ return config.layerNames.klc;
180
+ }
181
+ if (type === 'pa') {
182
+ return config.layerNames.pa;
183
+ }
184
+ return null;
185
+ }
186
+
187
+ buildCqlFilter(klcCode, date) {
188
+ const baseFilter =
189
+ "klc_code LIKE '" + klcCode + "' AND in_pa LIKE 'not_defined'";
190
+ if (!Number.isFinite(Number(date))) {
191
+ return baseFilter;
192
+ }
193
+ return baseFilter + ' AND date=' + Number(date);
194
+ }
195
+
196
+ buildWmsCustomLayerParameters(cqlFilter) {
197
+ const config = this.getHotspotWmsConfig();
198
+ return {
199
+ SERVICE: 'WMS',
200
+ REQUEST: 'GetMap',
201
+ FORMAT: 'image/png',
202
+ TRANSPARENT: 'true',
203
+ tiled: 'true',
204
+ STYLES: '',
205
+ VERSION: config.version,
206
+ SRS: config.srs,
207
+ CQL_FILTER: cqlFilter,
208
+ };
209
+ }
210
+
211
+ resolveLccDateOptions(
212
+ klcCode,
213
+ selectedLcYear,
214
+ lccDatesByLcYear,
215
+ lccDateList,
216
+ ) {
217
+ const klcLcDateMap = {
218
+ CAF_02: {
219
+ 2016: [2000, 2019],
220
+ 2019: [2024],
221
+ },
222
+ CAF_05: {
223
+ 2015: [2000, 2019],
224
+ 2019: [2024],
225
+ },
226
+ };
227
+
228
+ const mappedByKlc = klcLcDateMap[klcCode] || null;
229
+ if (mappedByKlc && Number.isFinite(Number(selectedLcYear))) {
230
+ const mapped = mappedByKlc[Number(selectedLcYear)];
231
+ if (Array.isArray(mapped) && mapped.length) {
232
+ return Array.from(new Set(mapped)).sort((a, b) => a - b);
233
+ }
234
+ }
235
+
236
+ const mappedByData = Number.isFinite(Number(selectedLcYear))
237
+ ? lccDatesByLcYear[Number(selectedLcYear)]
238
+ : null;
239
+ if (Array.isArray(mappedByData) && mappedByData.length) {
240
+ return mappedByData;
241
+ }
242
+
243
+ return lccDateList;
244
+ }
245
+
140
246
  layerModelInit() {
141
- const serviceUrl = this.urls.serviceUrl;
247
+ const serviceUrl = this.getHotspotWmsConfig().endpoint;
142
248
  this.esriLayer_lcc = new WMSLayer({
143
249
  url: serviceUrl,
250
+ version: this.getHotspotWmsConfig().version,
144
251
  title: '',
145
252
  //featureInfoFormat: "application/json",
146
- customLayerParameters: {},
253
+ customLayerParameters: this.buildWmsCustomLayerParameters(''),
147
254
  sublayers: [
148
255
  // LAND COVER CHANGE DATASET ________________________________________________________________________________________________________________
149
256
  {
150
- name: this.addLegendName('all_lcc_a_pol'),
257
+ name: this.addLegendName(this.resolveWmsLayerName('lcc', 'a')),
151
258
  legendUrl: this.addLegendNameToUrl('all_lcc_a_pol'),
152
259
  },
153
260
  ],
154
261
  });
155
262
  this.esriLayer_lcc = new WMSLayer({
156
263
  url: serviceUrl,
264
+ version: this.getHotspotWmsConfig().version,
157
265
  title: '',
158
266
  //featureInfoFormat: "application/json",
159
- customLayerParameters: {},
267
+ customLayerParameters: this.buildWmsCustomLayerParameters(''),
160
268
  sublayers: [
161
269
  // LAND COVER CHANGE DATASET ________________________________________________________________________________________________________________
162
270
  {
163
- name: this.addLegendName('all_lcc_b_pol'),
271
+ name: this.addLegendName(this.resolveWmsLayerName('lcc', 'b')),
164
272
  legendUrl: this.addLegendNameToUrl('all_lcc_b_pol'),
165
273
  },
166
274
  ],
167
275
  });
168
276
  this.esriLayer_lc = new WMSLayer({
169
277
  url: serviceUrl,
278
+ version: this.getHotspotWmsConfig().version,
170
279
  title: '',
171
280
  //featureInfoFormat: "application/json",
172
- customLayerParameters: {},
281
+ customLayerParameters: this.buildWmsCustomLayerParameters(''),
173
282
  sublayers: [
174
283
  // PRESENT LAND COVER DATASET ________________________________________________________________________________________________________________
175
284
  {
176
- name: this.addLegendName('all_present_lc_a_pol'),
285
+ name: this.addLegendName(this.resolveWmsLayerName('lc', 'a')),
177
286
  legendUrl: this.addLegendNameToUrl('all_present_lc_a_pol'),
178
287
  },
179
288
  ],
180
289
  });
181
290
  this.esriLayer_lc = new WMSLayer({
182
291
  url: serviceUrl,
292
+ version: this.getHotspotWmsConfig().version,
183
293
  title: '',
184
294
  //featureInfoFormat: "application/json",
185
- customLayerParameters: {},
295
+ customLayerParameters: this.buildWmsCustomLayerParameters(''),
186
296
  sublayers: [
187
297
  // PRESENT LAND COVER DATASET ________________________________________________________________________________________________________________
188
298
  {
189
- name: this.addLegendName('all_present_lc_b_pol'),
299
+ name: this.addLegendName(this.resolveWmsLayerName('lc', 'b')),
190
300
  legendUrl: this.addLegendNameToUrl('all_present_lc_b_pol'),
191
301
  },
192
302
  ],
193
303
  });
194
304
  this.esriLayer_klc = new WMSLayer({
195
305
  url: serviceUrl,
306
+ version: this.getHotspotWmsConfig().version,
196
307
  title: '',
197
308
  //featureInfoFormat: "application/json",
198
- customLayerParameters: {},
309
+ customLayerParameters: this.buildWmsCustomLayerParameters(''),
199
310
  sublayers: [
200
311
  {
201
- name: this.addLegendName('cop_klc'),
312
+ name: this.addLegendName(this.resolveWmsLayerName('klc')),
202
313
  legendUrl: this.addLegendNameToUrl('cop_klc'),
203
314
  },
204
315
  ],
205
316
  });
206
317
  this.esriLayer_pa = new WMSLayer({
207
318
  url: serviceUrl,
319
+ version: this.getHotspotWmsConfig().version,
208
320
  title: '',
209
321
  //featureInfoFormat: "application/json",
210
- customLayerParameters: {},
322
+ customLayerParameters: this.buildWmsCustomLayerParameters(''),
211
323
  sublayers: [
212
324
  {
213
- name: this.addLegendName('protected_areas'),
325
+ name: this.addLegendName(this.resolveWmsLayerName('pa')),
214
326
  legendUrl: this.addLegendNameToUrl('protected_areas'),
215
327
  },
216
328
  ],
@@ -277,17 +389,12 @@ class HotspotWidget extends React.Component {
277
389
  .value.match(/\d+/g)
278
390
  .map(Number)[0];
279
391
  }
280
- for (let i = 0; activeLayers[i]; i++) {
281
- let layer = activeLayers[i];
282
- if (layer.includes('all_lcc_a_pol')) {
283
- typeLegend = 'all_lcc_a_pol';
284
- title = 'Dichotomous Land Cover Change in selected Hot Spots';
285
- break;
286
- } else {
287
- typeLegend = 'all_lcc_b_pol';
288
- title = 'Modular Land Cover Change in selected Hot Spots';
289
- }
290
- }
392
+ const layerVariant = this.resolveLayerVariant(activeLayers, 'lcc');
393
+ typeLegend = layerVariant === 'a' ? 'all_lcc_a_pol' : 'all_lcc_b_pol';
394
+ title =
395
+ layerVariant === 'a'
396
+ ? 'Dichotomous Land Cover Change in selected Hot Spots'
397
+ : 'Modular Land Cover Change in selected Hot Spots';
291
398
 
292
399
  this.addFilteredLayersData(
293
400
  filteredLayersData,
@@ -298,7 +405,9 @@ class HotspotWidget extends React.Component {
298
405
 
299
406
  filterLayer = this.esriLayer_lcc;
300
407
 
301
- filterLayer.sublayers.items[0].name = this.addLegendName(typeLegend);
408
+ filterLayer.sublayers.items[0].name = this.addLegendName(
409
+ this.resolveWmsLayerName('lcc', layerVariant),
410
+ );
302
411
  filterLayer.sublayers.items[0].legendUrl =
303
412
  this.addLegendNameToUrl(typeLegend);
304
413
  filterLayer.sublayers.items[0].title = title;
@@ -307,30 +416,27 @@ class HotspotWidget extends React.Component {
307
416
  bookmarkHotspotFilter.filteredLayers &&
308
417
  bookmarkHotspotFilter.filteredLayers['lcc_filter'] !== undefined
309
418
  ) {
310
- filterLayer.customLayerParameters['CQL_FILTER'] =
311
- bookmarkHotspotFilter.filteredLayers['lcc_filter'];
419
+ filterLayer.customLayerParameters =
420
+ this.buildWmsCustomLayerParameters(
421
+ bookmarkHotspotFilter.filteredLayers['lcc_filter'],
422
+ );
312
423
  } else {
313
- filterLayer.customLayerParameters['CQL_FILTER'] =
314
- 'klc_code LIKE ' +
315
- "'" +
316
- this.dataKlc_code +
317
- "'" +
318
- " AND in_pa = 'not_defined' AND date = " +
319
- selectBoxHighlightsLcc;
424
+ filterLayer.customLayerParameters =
425
+ this.buildWmsCustomLayerParameters(
426
+ this.buildCqlFilter(this.dataKlc_code, selectBoxHighlightsLcc),
427
+ );
320
428
  }
321
429
  }
322
430
  if (type === 'lc') {
323
- for (let i = 0; i < activeLayers.length; i++) {
324
- let layer = activeLayers[i];
325
- if (layer.includes('all_present_lc_a_pol')) {
326
- typeLegend = 'all_present_lc_a_pol';
327
- title = 'Dichotomous Present Land Cover in selected Hot Spots';
328
- break;
329
- } else {
330
- typeLegend = 'all_present_lc_b_pol';
331
- title = 'Modular Present Land Cover in selected Hot Spots';
332
- }
333
- }
431
+ const layerVariant = this.resolveLayerVariant(activeLayers, 'lc');
432
+ typeLegend =
433
+ layerVariant === 'a'
434
+ ? 'all_present_lc_a_pol'
435
+ : 'all_present_lc_b_pol';
436
+ title =
437
+ layerVariant === 'a'
438
+ ? 'Dichotomous Present Land Cover in selected Hot Spots'
439
+ : 'Modular Present Land Cover in selected Hot Spots';
334
440
 
335
441
  let selectLcBoxTime =
336
442
  document.getElementById('select-klc-lcTime').value;
@@ -353,7 +459,9 @@ class HotspotWidget extends React.Component {
353
459
 
354
460
  filterLayer = this.esriLayer_lc;
355
461
 
356
- filterLayer.sublayers.items[0].name = this.addLegendName(typeLegend);
462
+ filterLayer.sublayers.items[0].name = this.addLegendName(
463
+ this.resolveWmsLayerName('lc', layerVariant),
464
+ );
357
465
  filterLayer.sublayers.items[0].legendUrl =
358
466
  this.addLegendNameToUrl(typeLegend);
359
467
  filterLayer.sublayers.items[0].title = title;
@@ -362,16 +470,15 @@ class HotspotWidget extends React.Component {
362
470
  bookmarkHotspotFilter.filteredLayers &&
363
471
  bookmarkHotspotFilter.filteredLayers['lc_filter'] !== undefined
364
472
  ) {
365
- filterLayer.customLayerParameters['CQL_FILTER'] =
366
- bookmarkHotspotFilter.filteredLayers['lc_filter'];
473
+ filterLayer.customLayerParameters =
474
+ this.buildWmsCustomLayerParameters(
475
+ bookmarkHotspotFilter.filteredLayers['lc_filter'],
476
+ );
367
477
  } else {
368
- filterLayer.customLayerParameters['CQL_FILTER'] =
369
- 'klc_code LIKE ' +
370
- "'" +
371
- this.dataKlc_code +
372
- "'" +
373
- " AND in_pa = 'not_defined' AND date = " +
374
- selectBoxHighlightsLc;
478
+ filterLayer.customLayerParameters =
479
+ this.buildWmsCustomLayerParameters(
480
+ this.buildCqlFilter(this.dataKlc_code, selectBoxHighlightsLc),
481
+ );
375
482
  }
376
483
  }
377
484
  if (type === 'klc') {
@@ -381,13 +488,20 @@ class HotspotWidget extends React.Component {
381
488
  bookmarkHotspotFilter.filteredLayers &&
382
489
  bookmarkHotspotFilter.filteredLayers['klc_filter'] !== undefined
383
490
  ) {
384
- this.esriLayer_klc.customLayerParameters['CQL_FILTER'] =
385
- bookmarkHotspotFilter.filteredLayers['klc_filter'];
491
+ this.esriLayer_klc.customLayerParameters =
492
+ this.buildWmsCustomLayerParameters(
493
+ bookmarkHotspotFilter.filteredLayers['klc_filter'],
494
+ );
386
495
  } else {
387
- this.esriLayer_klc.customLayerParameters['CQL_FILTER'] =
388
- "klc_code LIKE '" + this.dataKlc_code + "'";
496
+ this.esriLayer_klc.customLayerParameters =
497
+ this.buildWmsCustomLayerParameters(
498
+ "klc_code LIKE '" + this.dataKlc_code + "'",
499
+ );
389
500
  }
390
501
  filterLayer = this.esriLayer_klc;
502
+ filterLayer.sublayers.items[0].name = this.addLegendName(
503
+ this.resolveWmsLayerName('klc'),
504
+ );
391
505
  filterLayer.sublayers.items[0].title = title;
392
506
  }
393
507
  if (type === 'pa') {
@@ -398,13 +512,20 @@ class HotspotWidget extends React.Component {
398
512
  bookmarkHotspotFilter.filteredLayers &&
399
513
  bookmarkHotspotFilter.filteredLayers['pa_filter'] !== undefined
400
514
  ) {
401
- this.esriLayer_pa.customLayerParameters['CQL_FILTER'] =
402
- bookmarkHotspotFilter.filteredLayers['pa_filter'];
515
+ this.esriLayer_pa.customLayerParameters =
516
+ this.buildWmsCustomLayerParameters(
517
+ bookmarkHotspotFilter.filteredLayers['pa_filter'],
518
+ );
403
519
  } else {
404
- this.esriLayer_pa.customLayerParameters['CQL_FILTER'] =
405
- "klc_code LIKE '" + this.dataKlc_code + "'";
520
+ this.esriLayer_pa.customLayerParameters =
521
+ this.buildWmsCustomLayerParameters(
522
+ "klc_code LIKE '" + this.dataKlc_code + "'",
523
+ );
406
524
  }
407
525
  filterLayer = this.esriLayer_pa;
526
+ filterLayer.sublayers.items[0].name = this.addLegendName(
527
+ this.resolveWmsLayerName('pa'),
528
+ );
408
529
  filterLayer.sublayers.items[0].title = title;
409
530
  }
410
531
  layersToAdd[type + '_filter'] = filterLayer;
@@ -629,7 +750,13 @@ class HotspotWidget extends React.Component {
629
750
  data-target-property="layout"
630
751
  onBlur={() => {}}
631
752
  onChange={(e) => {
632
- this.disableButton();
753
+ this.setState(
754
+ { lcYear: e.target.value, lccYear: null },
755
+ () => {
756
+ this.updateLccOptionsForSelectedLc();
757
+ this.disableButton();
758
+ },
759
+ );
633
760
  }}
634
761
  ></select>
635
762
  </label>
@@ -647,9 +774,14 @@ class HotspotWidget extends React.Component {
647
774
  let dichotomousKLCAreas = [];
648
775
  let activeLayers = [];
649
776
 
650
- if (this.props.hotspotData['activeLayers'] === undefined) return;
651
-
652
- activeLayers = Object.keys(this.props.hotspotData['activeLayers']);
777
+ if (
778
+ this.props.hotspotData &&
779
+ this.props.hotspotData['activeLayers'] !== undefined
780
+ ) {
781
+ activeLayers = Object.keys(this.props.hotspotData['activeLayers']);
782
+ } else {
783
+ activeLayers = [];
784
+ }
653
785
 
654
786
  if (selectedOption === undefined) return;
655
787
 
@@ -657,19 +789,22 @@ class HotspotWidget extends React.Component {
657
789
  selectBoxLccTime = document.getElementById('select-klc-lccTime');
658
790
  selectBoxLcTime = document.getElementById('select-klc-lcTime');
659
791
 
660
- if (selectedOption !== this.state.selectedArea) {
661
- this.setState({
662
- lcYear: null,
663
- lccYear: null,
664
- });
665
- }
666
-
667
792
  //this.selectedArea = selectedOption;
668
793
 
669
794
  for (let i = 0; i < data.length; i++) {
670
795
  var option = data[i].node.klc_name;
671
796
 
672
- let keyMapInfoObj = JSON.parse(data[i].node.keymap_info) || {};
797
+ let rawKeymapInfo = data[i].node.keymap_info;
798
+ let keyMapInfoObj = {};
799
+ if (typeof rawKeymapInfo === 'string') {
800
+ try {
801
+ keyMapInfoObj = JSON.parse(rawKeymapInfo) || {};
802
+ } catch (e) {
803
+ keyMapInfoObj = {};
804
+ }
805
+ } else if (rawKeymapInfo && typeof rawKeymapInfo === 'object') {
806
+ keyMapInfoObj = rawKeymapInfo;
807
+ }
673
808
 
674
809
  if (keyMapInfoObj.b_classes === true) {
675
810
  modularKLCAreas.push(option);
@@ -695,36 +830,106 @@ class HotspotWidget extends React.Component {
695
830
  selectBoxLccTime.options[0].disabled = true;
696
831
  }
697
832
 
698
- if (
699
- data[i].node.keymap_info
700
- .toLowerCase()
701
- .includes('multiple_dates":true')
702
- ) {
703
- //new select year options values
704
- var optionLcTime = data[i].node.present_lc_year;
705
- var indexStart = data[i].node.keymap_info
706
- .toLowerCase()
707
- .indexOf('"dates":[ {');
708
- var indexEnd = data[i].node.keymap_info.toLowerCase().indexOf(' }],');
709
- var strOut = data[i].node.keymap_info.substring(indexStart, indexEnd);
710
- var numbers = strOut.match(/\d+/g).map(Number);
711
- numbers.forEach((element) => {
712
- selectBoxLccTime.options.add(new Option(element, element, element));
833
+ let lccDateList = [];
834
+ let lcDateList = [];
835
+ let lccDatesByLcYear = {};
836
+
837
+ if (Array.isArray(keyMapInfoObj.dates)) {
838
+ keyMapInfoObj.dates.forEach((entry) => {
839
+ const lccDate = Number(
840
+ entry && typeof entry === 'object' ? entry?.date : entry,
841
+ );
842
+ const lcDate = Number(
843
+ entry && typeof entry === 'object' ? entry?.lc_date : undefined,
844
+ );
845
+ if (Number.isFinite(lccDate)) {
846
+ lccDateList.push(lccDate);
847
+ }
848
+ if (Number.isFinite(lcDate)) {
849
+ lcDateList.push(lcDate);
850
+ if (!lccDatesByLcYear[lcDate]) {
851
+ lccDatesByLcYear[lcDate] = [];
852
+ }
853
+ if (Number.isFinite(lccDate)) {
854
+ lccDatesByLcYear[lcDate].push(lccDate);
855
+ }
856
+ }
713
857
  });
714
- selectBoxLcTime.options.add(
715
- new Option(optionLcTime, optionLcTime, optionLcTime),
716
- );
717
- } else {
718
- //new select year options values
719
- var optionLccTime = data[i].node.lcc_year;
720
- selectBoxLccTime.options.add(
721
- new Option(optionLccTime, optionLccTime, optionLccTime),
858
+ }
859
+
860
+ if (Array.isArray(keyMapInfoObj.multiple_lc_dates)) {
861
+ lcDateList = lcDateList.concat(
862
+ keyMapInfoObj.multiple_lc_dates
863
+ .map((yearEntry) =>
864
+ Number(
865
+ yearEntry && typeof yearEntry === 'object'
866
+ ? yearEntry?.date
867
+ : yearEntry,
868
+ ),
869
+ )
870
+ .filter((year) => Number.isFinite(year)),
722
871
  );
723
- optionLcTime = data[i].node.present_lc_year;
724
- selectBoxLcTime.options.add(
725
- new Option(optionLcTime, optionLcTime, optionLcTime),
872
+ }
873
+
874
+ const dynamicYearValues = Object.keys(keyMapInfoObj)
875
+ .filter((key) => /^year\d+$/.test(key))
876
+ .map((key) => Number(keyMapInfoObj[key]))
877
+ .filter((year) => Number.isFinite(year));
878
+ if (dynamicYearValues.length) {
879
+ lccDateList = lccDateList.concat(dynamicYearValues);
880
+ }
881
+
882
+ const presentLcYearFromKeymap = Number(keyMapInfoObj.year_present_lc);
883
+ if (Number.isFinite(presentLcYearFromKeymap)) {
884
+ lcDateList.push(presentLcYearFromKeymap);
885
+ }
886
+
887
+ if (
888
+ !lccDateList.length &&
889
+ Number.isFinite(Number(data[i].node.lcc_year))
890
+ ) {
891
+ lccDateList = [Number(data[i].node.lcc_year)];
892
+ }
893
+ if (
894
+ !lcDateList.length &&
895
+ Number.isFinite(Number(data[i].node.present_lc_year))
896
+ ) {
897
+ lcDateList = [Number(data[i].node.present_lc_year)];
898
+ }
899
+
900
+ lccDateList = Array.from(new Set(lccDateList)).sort((a, b) => a - b);
901
+ lcDateList = Array.from(new Set(lcDateList)).sort((a, b) => a - b);
902
+
903
+ Object.keys(lccDatesByLcYear).forEach((lcDateKey) => {
904
+ lccDatesByLcYear[lcDateKey] = Array.from(
905
+ new Set(lccDatesByLcYear[lcDateKey]),
906
+ ).sort((a, b) => a - b);
907
+ });
908
+
909
+ lcDateList.forEach((element) => {
910
+ selectBoxLcTime.options.add(new Option(element, element, element));
911
+ });
912
+
913
+ if (this.state.lcYear !== null) {
914
+ const hasLcYearOption = Array.from(selectBoxLcTime.options).some(
915
+ (option) => option.value === this.state.lcYear,
726
916
  );
917
+ selectBoxLcTime.value = hasLcYearOption
918
+ ? this.state.lcYear
919
+ : 'default';
727
920
  }
921
+
922
+ const selectedLcYear = Number(this.state.lcYear);
923
+ const lccOptionsToUse = this.resolveLccDateOptions(
924
+ data[i].node.klc_code,
925
+ selectedLcYear,
926
+ lccDatesByLcYear,
927
+ lccDateList,
928
+ );
929
+
930
+ lccOptionsToUse.forEach((element) => {
931
+ selectBoxLccTime.options.add(new Option(element, element, element));
932
+ });
728
933
  }
729
934
  }
730
935
  if (selectBox) {
@@ -811,6 +1016,22 @@ class HotspotWidget extends React.Component {
811
1016
  break;
812
1017
  }
813
1018
  }
1019
+ } else if (selectBox) {
1020
+ const allKLCAreas = Array.from(
1021
+ new Set(modularKLCAreas.concat(dichotomousKLCAreas)),
1022
+ ).sort((a, b) => a.localeCompare(b));
1023
+ for (let i = 0; i < allKLCAreas.length; i++) {
1024
+ let option = allKLCAreas[i];
1025
+ selectBox.options.add(new Option(option, option, option));
1026
+ }
1027
+ if (this.state.selectedArea !== null) {
1028
+ const hasSelectedOption = Array.from(selectBox.options).some(
1029
+ (option) => option.value === this.state.selectedArea,
1030
+ );
1031
+ selectBox.value = hasSelectedOption
1032
+ ? this.state.selectedArea
1033
+ : 'default';
1034
+ }
814
1035
  }
815
1036
  if (selectBox.value === 'default') {
816
1037
  if (selectBoxLcTime) {
@@ -840,6 +1061,98 @@ class HotspotWidget extends React.Component {
840
1061
  }
841
1062
  }
842
1063
 
1064
+ updateLccOptionsForSelectedLc() {
1065
+ const selectBoxLccTime = document.getElementById('select-klc-lccTime');
1066
+ if (
1067
+ !selectBoxLccTime ||
1068
+ !this.state.selectedArea ||
1069
+ !Array.isArray(this.dataJSONNames)
1070
+ ) {
1071
+ return;
1072
+ }
1073
+
1074
+ const selectedNode = this.dataJSONNames.find(
1075
+ (entry) => entry?.node?.klc_name === this.state.selectedArea,
1076
+ )?.node;
1077
+
1078
+ if (!selectedNode) {
1079
+ return;
1080
+ }
1081
+
1082
+ let rawKeymapInfo = selectedNode.keymap_info;
1083
+ let keyMapInfoObj = {};
1084
+ if (typeof rawKeymapInfo === 'string') {
1085
+ try {
1086
+ keyMapInfoObj = JSON.parse(rawKeymapInfo) || {};
1087
+ } catch (e) {
1088
+ keyMapInfoObj = {};
1089
+ }
1090
+ } else if (rawKeymapInfo && typeof rawKeymapInfo === 'object') {
1091
+ keyMapInfoObj = rawKeymapInfo;
1092
+ }
1093
+
1094
+ let lccDateList = [];
1095
+ let lccDatesByLcYear = {};
1096
+
1097
+ if (Array.isArray(keyMapInfoObj.dates)) {
1098
+ keyMapInfoObj.dates.forEach((entry) => {
1099
+ const lccDate = Number(
1100
+ entry && typeof entry === 'object' ? entry?.date : entry,
1101
+ );
1102
+ const lcDate = Number(
1103
+ entry && typeof entry === 'object' ? entry?.lc_date : undefined,
1104
+ );
1105
+ if (Number.isFinite(lccDate)) {
1106
+ lccDateList.push(lccDate);
1107
+ }
1108
+ if (Number.isFinite(lcDate)) {
1109
+ if (!lccDatesByLcYear[lcDate]) {
1110
+ lccDatesByLcYear[lcDate] = [];
1111
+ }
1112
+ if (Number.isFinite(lccDate)) {
1113
+ lccDatesByLcYear[lcDate].push(lccDate);
1114
+ }
1115
+ }
1116
+ });
1117
+ }
1118
+
1119
+ const dynamicYearValues = Object.keys(keyMapInfoObj)
1120
+ .filter((key) => /^year\d+$/.test(key))
1121
+ .map((key) => Number(keyMapInfoObj[key]))
1122
+ .filter((year) => Number.isFinite(year));
1123
+ if (dynamicYearValues.length) {
1124
+ lccDateList = lccDateList.concat(dynamicYearValues);
1125
+ }
1126
+
1127
+ if (!lccDateList.length && Number.isFinite(Number(selectedNode.lcc_year))) {
1128
+ lccDateList = [Number(selectedNode.lcc_year)];
1129
+ }
1130
+
1131
+ lccDateList = Array.from(new Set(lccDateList)).sort((a, b) => a - b);
1132
+ Object.keys(lccDatesByLcYear).forEach((lcDateKey) => {
1133
+ lccDatesByLcYear[lcDateKey] = Array.from(
1134
+ new Set(lccDatesByLcYear[lcDateKey]),
1135
+ ).sort((a, b) => a - b);
1136
+ });
1137
+
1138
+ const selectedLcYear = Number(this.state.lcYear);
1139
+ const lccOptionsToUse = this.resolveLccDateOptions(
1140
+ selectedNode.klc_code,
1141
+ selectedLcYear,
1142
+ lccDatesByLcYear,
1143
+ lccDateList,
1144
+ );
1145
+
1146
+ this.removeOptions(selectBoxLccTime);
1147
+ selectBoxLccTime.options.add(
1148
+ new Option('Select a year', 'default', true, true),
1149
+ );
1150
+ selectBoxLccTime.options[0].disabled = true;
1151
+ lccOptionsToUse.forEach((element) => {
1152
+ selectBoxLccTime.options.add(new Option(element, element, element));
1153
+ });
1154
+ }
1155
+
843
1156
  renderLandCoverChange() {
844
1157
  return (
845
1158
  <div className="measurement-dropdown" id="LandCoverChangeDropdown">
@@ -955,7 +1268,11 @@ class HotspotWidget extends React.Component {
955
1268
  <select
956
1269
  onBlur={() => {}}
957
1270
  onChange={(e) => {
958
- this.setState({ selectedArea: e.target.value });
1271
+ this.setState({
1272
+ selectedArea: e.target.value,
1273
+ lcYear: null,
1274
+ lccYear: null,
1275
+ });
959
1276
  }}
960
1277
  id="select-klc-area"
961
1278
  className="esri-select"
@@ -999,6 +1316,8 @@ class HotspotWidget extends React.Component {
999
1316
  });
1000
1317
  this.layerModelInit();
1001
1318
  this.getBBoxData();
1319
+ this.getKLCNames(this.dataJSONNames, this.state.selectedArea);
1320
+ this.disableButton();
1002
1321
  this.arcgisEventHandles = [];
1003
1322
  this.props.view.when(() => {
1004
1323
  const handle = this.props.view.map.layers.on('change', () => {
@@ -1070,11 +1389,54 @@ class HotspotWidget extends React.Component {
1070
1389
  }
1071
1390
  }
1072
1391
 
1073
- componentDidUpdate(prevState, prevProps) {
1074
- if (prevProps.hotspotData !== this.props.hotspotData) {
1392
+ getHotspotLayerSignature(hotspotData) {
1393
+ if (!hotspotData || typeof hotspotData !== 'object') {
1394
+ return '';
1395
+ }
1396
+ const activeLayers = hotspotData.activeLayers;
1397
+ if (!activeLayers || typeof activeLayers !== 'object') {
1398
+ return '';
1399
+ }
1400
+ return Object.keys(activeLayers)
1401
+ .filter(
1402
+ (key) => key.includes('all_present_lc_') || key.includes('all_lcc_'),
1403
+ )
1404
+ .sort()
1405
+ .join('|');
1406
+ }
1407
+
1408
+ componentDidUpdate(prevProps, prevState) {
1409
+ const prevHotspotLayerSignature = this.getHotspotLayerSignature(
1410
+ prevProps.hotspotData,
1411
+ );
1412
+ const nextHotspotLayerSignature = this.getHotspotLayerSignature(
1413
+ this.props.hotspotData,
1414
+ );
1415
+
1416
+ if (
1417
+ prevHotspotLayerSignature !== nextHotspotLayerSignature &&
1418
+ (this.state.selectedArea !== null ||
1419
+ this.state.lcYear !== null ||
1420
+ this.state.lccYear !== null)
1421
+ ) {
1422
+ this.setState({
1423
+ selectedArea: null,
1424
+ lcYear: null,
1425
+ lccYear: null,
1426
+ });
1427
+ return;
1428
+ }
1429
+
1430
+ if (
1431
+ prevProps.hotspotData !== this.props.hotspotData ||
1432
+ prevState.selectedArea !== this.state.selectedArea
1433
+ ) {
1075
1434
  this.getKLCNames(this.dataJSONNames, this.state.selectedArea);
1076
1435
  this.disableButton();
1077
1436
  }
1437
+ if (prevState.lcYear !== this.state.lcYear) {
1438
+ this.disableButton();
1439
+ }
1078
1440
  }
1079
1441
  }
1080
1442
  export default HotspotWidget;
@@ -7007,14 +7007,25 @@ class MenuWidget extends React.Component {
7007
7007
  layersReorder() {
7008
7008
  let activeLayers = document.querySelectorAll('.active-layer');
7009
7009
  let counter = activeLayers.length - 1;
7010
- let filterLayer;
7010
+ const hotspotData = this.props.hotspotData || {};
7011
+ const hotspotFilteredLayers =
7012
+ hotspotData.filteredLayers &&
7013
+ typeof hotspotData.filteredLayers === 'object'
7014
+ ? hotspotData.filteredLayers
7015
+ : null;
7016
+ const hotspotActiveLayers =
7017
+ hotspotData.activeLayers && typeof hotspotData.activeLayers === 'object'
7018
+ ? hotspotData.activeLayers
7019
+ : null;
7020
+
7011
7021
  activeLayers.forEach((item, index) => {
7022
+ let filterLayer;
7012
7023
  if (
7013
- this.props.hotspotData &&
7014
- this.props.hotspotData.filteredLayers &&
7015
- Object.keys(this.props.hotspotData.filteredLayers).length > 0
7024
+ hotspotFilteredLayers &&
7025
+ Object.keys(hotspotFilteredLayers).length > 0 &&
7026
+ hotspotActiveLayers
7016
7027
  ) {
7017
- Object.keys(this.props.hotspotData.activeLayers).forEach((key) => {
7028
+ Object.keys(hotspotActiveLayers).forEach((key) => {
7018
7029
  if (item.id.includes(key)) {
7019
7030
  if (key.includes('all_lcc')) {
7020
7031
  filterLayer = this.layers['lcc_filter'];
@@ -7022,12 +7033,11 @@ class MenuWidget extends React.Component {
7022
7033
  filterLayer = this.layers['lc_filter'];
7023
7034
  }
7024
7035
  }
7025
- this.map.reorder(filterLayer, index);
7026
7036
  });
7027
7037
  }
7028
7038
  let order = counter - index;
7029
7039
  item.setAttribute('layer-order', order);
7030
- if (filterLayer === undefined) {
7040
+ if (filterLayer === undefined || filterLayer === null) {
7031
7041
  this.layerReorder(this.layers[item.getAttribute('layer-id')], order);
7032
7042
  } else {
7033
7043
  this.layerReorder(filterLayer, order);
@@ -7056,6 +7066,7 @@ class MenuWidget extends React.Component {
7056
7066
  * @param {*} index
7057
7067
  */
7058
7068
  layerReorder(layer, index) {
7069
+ if (!layer || !this.map) return;
7059
7070
  this.map.reorder(layer, index);
7060
7071
  }
7061
7072
 
@@ -8006,6 +8017,7 @@ class MenuWidget extends React.Component {
8006
8017
  }
8007
8018
 
8008
8019
  deleteFilteredLayer(layer) {
8020
+ if (!layer || !this.props || !this.props.map) return;
8009
8021
  if (
8010
8022
  !(
8011
8023
  layer.includes('all_lcc') ||
@@ -8016,9 +8028,13 @@ class MenuWidget extends React.Component {
8016
8028
  )
8017
8029
  return;
8018
8030
  let filterLayer;
8019
- //let temp;
8020
- let updatedHotspotData = this.props.hotspotData;
8021
- let updatedFilteredLayers = this.props.hotspotData['filteredLayers'];
8031
+ let updatedHotspotData = this.props.hotspotData || {};
8032
+ let updatedFilteredLayers =
8033
+ updatedHotspotData &&
8034
+ updatedHotspotData['filteredLayers'] &&
8035
+ typeof updatedHotspotData['filteredLayers'] === 'object'
8036
+ ? updatedHotspotData['filteredLayers']
8037
+ : {};
8022
8038
  if (this.layers['lcc_filter'] && layer.includes('all_lcc')) {
8023
8039
  this.layers['lcc_filter'].visible = false;
8024
8040
  filterLayer = this.props.map.findLayerById('lcc_filter');
@@ -8044,7 +8060,9 @@ class MenuWidget extends React.Component {
8044
8060
  layer.includes('cop_klc')
8045
8061
  ) {
8046
8062
  this.layers['klc_filter'].visible = false;
8047
- this.layers[layer].visible = false;
8063
+ if (this.layers[layer]) {
8064
+ this.layers[layer].visible = false;
8065
+ }
8048
8066
  filterLayer = this.props.map.findLayerById('klc_filter');
8049
8067
  if (filterLayer !== undefined) {
8050
8068
  // temp = filterLayer;
@@ -8059,7 +8077,9 @@ class MenuWidget extends React.Component {
8059
8077
  layer.includes('protected_areas')
8060
8078
  ) {
8061
8079
  this.layers['pa_filter'].visible = false;
8062
- this.layers[layer].visible = false;
8080
+ if (this.layers[layer]) {
8081
+ this.layers[layer].visible = false;
8082
+ }
8063
8083
  filterLayer = this.props.map.findLayerById('pa_filter');
8064
8084
  if (filterLayer !== undefined) {
8065
8085
  // temp = filterLayer;
@@ -8070,8 +8090,10 @@ class MenuWidget extends React.Component {
8070
8090
  delete updatedFilteredLayers['pa_filter'];
8071
8091
  }
8072
8092
  this.props.mapLayersHandler(this.layers);
8073
- updatedHotspotData['filteredLayers'] = updatedFilteredLayers;
8074
- this.props.hotspotDataHandler(updatedHotspotData);
8093
+ if (this.props.hotspotDataHandler) {
8094
+ updatedHotspotData['filteredLayers'] = updatedFilteredLayers;
8095
+ this.props.hotspotDataHandler(updatedHotspotData);
8096
+ }
8075
8097
  }
8076
8098
 
8077
8099
  /**
@@ -234,6 +234,21 @@ const config = {
234
234
  '/products/lclcc-hot-spots/json-files/all_geo_klc.json/@@download/file',
235
235
  legendLinkUrl:
236
236
  'https://geospatial.jrc.ec.europa.eu/geoserver/hotspots/ows?service=WMS&request=GetLegendGraphic&format=image%2Fpng&width=20&height=20&layer=',
237
+ hotspotWmsEndpoint: 'https://geospatial.jrc.ec.europa.eu/geoserver/hsm/wms',
238
+ hotspotWmsVersion: '1.1.0',
239
+ hotspotWmsSrs: 'EPSG:3857',
240
+ hotspotWmsLayerNames: {
241
+ lcc: {
242
+ a: 'hsm:lcc_a_pols',
243
+ b: 'hsm:lcc_b_pols',
244
+ },
245
+ lc: {
246
+ a: 'hsm:present_lc_a_pols',
247
+ b: 'hsm:present_lc_b_pols',
248
+ },
249
+ klc: 'hsm:cop_klc',
250
+ pa: 'hsm:protected_areas',
251
+ },
237
252
  serviceUrl: 'https://geospatial.jrc.ec.europa.eu/geoserver/hotspots/wms',
238
253
  positronCompositeThumbnail:
239
254
  'https://gisco-services.ec.europa.eu/maps/wmts/OSMPositronComposite/EPSG3857/0/0/0.png',