@eeacms/volto-arcgis-block 0.1.387 → 0.1.389

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,17 @@ 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.389](https://github.com/eea/volto-arcgis-block/compare/0.1.388...0.1.389) - 18 September 2025
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - Merge pull request #1018 from eea/CLMS-291307-ADAPT-DATA-VIEWER-TIME-SLIDER-FOR-CDSE-TIME-SERIES-DATASETS [Unai Bolivar - [`da9c70f`](https://github.com/eea/volto-arcgis-block/commit/da9c70f56b030432f95b07487775be947d6c668c)]
12
+ - (bug): time slider widget turns off logo for cdse tiles [Unai Bolivar - [`555b466`](https://github.com/eea/volto-arcgis-block/commit/555b466b3fe54ca44a20c97102846651d825f7ef)]
13
+ ### [0.1.388](https://github.com/eea/volto-arcgis-block/compare/0.1.387...0.1.388) - 17 September 2025
14
+
15
+ #### :hammer_and_wrench: Others
16
+
17
+ - (bug): added time slider widget funciotnality for CDSE time series datasets [Unai Bolivar - [`759b719`](https://github.com/eea/volto-arcgis-block/commit/759b7193cbd046985197a6c9514d41b926f127d3)]
7
18
  ### [0.1.387](https://github.com/eea/volto-arcgis-block/compare/0.1.386...0.1.387) - 12 September 2025
8
19
 
9
20
  ### [0.1.386](https://github.com/eea/volto-arcgis-block/compare/0.1.385...0.1.386) - 12 September 2025
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-arcgis-block",
3
- "version": "0.1.387",
3
+ "version": "0.1.389",
4
4
  "description": "volto-arcgis-block: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: CodeSyntax",
@@ -3850,7 +3850,6 @@ class MenuWidget extends React.Component {
3850
3850
  } else {
3851
3851
  this.url = serviceLayer.ViewService;
3852
3852
  }
3853
- // const isCDSE = !!this.url && this.url.toLowerCase().includes('/ogc/');
3854
3853
  const isCDSE =
3855
3854
  !!this.url &&
3856
3855
  ['/ogc/', '/cdse/'].some((s) => this.url.toLowerCase().includes(s));
@@ -84,6 +84,46 @@ class TimesliderWidget extends React.Component {
84
84
  });
85
85
  }
86
86
 
87
+ async getCDSEWFSTemporalData(url, layer) {
88
+ if (!url) return {};
89
+ const match = /\/ogc\/(?:wmts|wms)\/([^/?]+)/i.exec(url);
90
+ const datasetDownloadInformation =
91
+ layer?.DatasetDownloadInformation ||
92
+ layer?.DatasetDownloadInformation ||
93
+ {};
94
+ if (!datasetDownloadInformation) return {};
95
+ const byocCollectionId =
96
+ datasetDownloadInformation?.items[0].byoc_collection || null;
97
+ if (!byocCollectionId) return {};
98
+ if (!match) return {};
99
+ const fetchUrl = `https://sh.dataspace.copernicus.eu/ogc/wfs/${match[1]}?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAMES=byoc-${byocCollectionId}&COUNT=100&BBOX=-21039383,-22375217,21039383,22375217&OUTPUTFORMAT=application/json`;
100
+ try {
101
+ const res = await fetch(fetchUrl);
102
+ const data = await res.json();
103
+ let times = {};
104
+ let layerName = '';
105
+ if (layer?.type === 'feature') {
106
+ layerName = layer?.id;
107
+ } else if (layer?.type === 'wms') {
108
+ layerName = layer?.sublayers?.items?.[0]?.name;
109
+ } else if (layer?.type === 'wmts') {
110
+ layerName = layer?.activeLayer?.id;
111
+ } else {
112
+ layerName = layer?.name || layer?.title || layer?.id || 'layer';
113
+ }
114
+ const features = Array.isArray(data?.features) ? data.features : [];
115
+ const dates = features
116
+ .map((f) => f?.properties?.date || null)
117
+ .filter((v) => v);
118
+ if (dates.length > 0) {
119
+ times[layerName] = {
120
+ array: dates,
121
+ };
122
+ }
123
+ return times;
124
+ } catch (e) {}
125
+ }
126
+
87
127
  parseTimeWMS(xml) {
88
128
  let layers = Array.from(xml.querySelectorAll('Layer')).filter(
89
129
  (v) => v.querySelectorAll('Layer').length === 0,
@@ -287,7 +327,14 @@ class TimesliderWidget extends React.Component {
287
327
 
288
328
  this.props.view
289
329
  .whenLayerView(this.layer, this.TimesliderWidget)
290
- .then((lv) => {
330
+ .then(async (lv) => {
331
+ this.url = lv?.layer?.viewService ?? this.layer?.url ?? null;
332
+ const urlNorm =
333
+ typeof this.url === 'string'
334
+ ? this.url.replace(/%2F/gi, '/').toLowerCase()
335
+ : '';
336
+ const isCDSE =
337
+ urlNorm.includes('/ogc/') || urlNorm.includes('/cdse/');
291
338
  if (this.layer.type === 'feature') {
292
339
  this.TimesliderWidget.fullTimeExtent = this.layer.timeInfo.fullTimeExtent;
293
340
  this.TimesliderWidget.stops = {
@@ -315,79 +362,50 @@ class TimesliderWidget extends React.Component {
315
362
  } else if (this.layer.type === 'wmts') {
316
363
  serviceType = 'wmts';
317
364
  }
365
+ let times = {};
366
+ let periodicity;
367
+ if (isCDSE) {
368
+ times = await this.getCDSEWFSTemporalData(
369
+ this.layer.url,
370
+ this.layer,
371
+ );
372
+ if (times) {
373
+ // Dates array
374
+ this.TimesliderWidget.fullTimeExtent = new TimeExtent({
375
+ start: new Date(
376
+ times[this.layerName].array[
377
+ times[this.layerName].array.length - 1
378
+ ],
379
+ ),
380
+ end: new Date(times[this.layerName].array[0]),
381
+ });
382
+ this.TimesliderWidget.stops = {
383
+ dates: times[this.layerName].array.map((e) => new Date(e)),
384
+ };
318
385
 
319
- this.getCapabilities(this.layer.url, serviceType).then((xml) => {
320
- let times = {};
321
- let periodicity;
322
- if (this.layer.type === 'wms') {
323
- times = this.parseTimeWMS(xml);
324
- } else if (this.layer.type === 'wmts') {
325
- times = this.parseTimeWMTS(xml);
326
- }
327
- // Capabilities have time enabled
328
- if (times[this.layerName].hasOwnProperty('dimension') === false) {
329
- // Start-End-Period
330
- if (times[this.layerName].hasOwnProperty('period')) {
331
- this.TimesliderWidget.fullTimeExtent = new TimeExtent({
332
- start: new Date(times[this.layerName].start),
333
- end: new Date(times[this.layerName].end),
334
- });
335
-
336
- const period = this.parserPeriod(
337
- times[this.layerName].period,
338
- );
339
-
340
- this.TimesliderWidget.stops = {
341
- interval: {
342
- value:
343
- period.years * 365 * 24 * 60 +
344
- period.months * 31 * 24 * 60 +
345
- period.weeks * 7 * 24 * 60 +
346
- period.days * 24 * 60 +
347
- period.hours * 60 +
348
- period.minutes +
349
- period.seconds / 60,
350
- unit: 'minutes',
351
- },
352
- };
353
-
354
- periodicity = times[this.layerName].period;
355
- } else if (times[this.layerName].hasOwnProperty('array')) {
356
- // Dates array
357
- this.TimesliderWidget.fullTimeExtent = new TimeExtent({
358
- start: new Date(times[this.layerName].array[0]),
359
- end: new Date(
360
- times[this.layerName].array[
361
- times[this.layerName].array.length - 1
362
- ],
363
- ),
364
- });
365
- this.TimesliderWidget.stops = {
366
- dates: times[this.layerName].array.map((e) => new Date(e)),
386
+ if (this.layer.type === 'wmts') {
387
+ this.layer.customLayerParameters = {
388
+ SHOWLOGO: false,
367
389
  };
390
+ const time = times[this.layerName].array.map(
391
+ (d) => new Date(d),
392
+ );
368
393
 
369
- if (this.layer.type === 'wmts') {
370
- this.layer.customLayerParameters = {};
371
- const time = times[this.layerName].array.map(
372
- (d) => new Date(d),
373
- );
374
-
375
- for (let i in time) {
376
- timeDict[time[i]] = times[this.layerName].array[i];
377
- }
394
+ for (let i in time) {
395
+ timeDict[time[i]] = times[this.layerName].array[i];
378
396
  }
397
+ }
379
398
 
380
- periodicity = Math.floor(
381
- (Date.parse(times[this.layerName].array[1]) -
382
- Date.parse(times[this.layerName].array[0])) /
383
- 86400000,
384
- );
385
- if (periodicity === 0) {
386
- periodicity =
387
- (new Date(times[this.layerName].array[1]).getHours() -
388
- new Date(times[this.layerName].array[0]).getHours()) /
389
- 24;
390
- }
399
+ periodicity = Math.floor(
400
+ (Date.parse(times[this.layerName].array[1]) -
401
+ Date.parse(times[this.layerName].array[0])) /
402
+ 86400000,
403
+ );
404
+ if (periodicity === 0) {
405
+ periodicity =
406
+ (new Date(times[this.layerName].array[1]).getHours() -
407
+ new Date(times[this.layerName].array[0]).getHours()) /
408
+ 24;
391
409
  }
392
410
 
393
411
  this.setState({ periodicity: periodicity });
@@ -409,18 +427,10 @@ class TimesliderWidget extends React.Component {
409
427
  if (!this.container.current ? true : false) {
410
428
  this.TimesliderWidget.stop();
411
429
  }
412
- /*let start = new Date(timeExtent.start).getTime();
413
- let end = new Date(timeExtent.end).getTime();
414
- this.props.time.elem.setAttribute('time-start', start);
415
- this.props.time.elem.setAttribute('time-end', end);
416
- if (this.props.download) {
417
- this.props.time.dataset.setAttribute('time-start', start);
418
- this.props.time.dataset.setAttribute('time-end', end);
419
- }
420
- this.props.time.dataset.setAttribute('time-start', start);
421
- this.props.time.dataset.setAttribute('time-end', end);*/
422
430
  if (this.layer.type === 'wmts') {
423
- this.layer.customLayerParameters = {};
431
+ this.layer.customLayerParameters = {
432
+ SHOWLOGO: false,
433
+ };
424
434
  this.layer.customLayerParameters['TIME'] =
425
435
  timeDict[this.TimesliderWidget.timeExtent.end];
426
436
  } else {
@@ -448,7 +458,144 @@ class TimesliderWidget extends React.Component {
448
458
  else {
449
459
  this.TimesliderWidget.disabled = true;
450
460
  }
451
- });
461
+ } else {
462
+ //place getCapabilties in here
463
+ this.getCapabilities(this.layer.url, serviceType).then((xml) => {
464
+ if (this.layer.type === 'wms') {
465
+ times = this.parseTimeWMS(xml);
466
+ } else if (this.layer.type === 'wmts') {
467
+ times = this.parseTimeWMTS(xml);
468
+ }
469
+ // Capabilities have time enabled
470
+ if (
471
+ times[this.layerName].hasOwnProperty('dimension') === false
472
+ ) {
473
+ // Start-End-Period
474
+ if (times[this.layerName].hasOwnProperty('period')) {
475
+ this.TimesliderWidget.fullTimeExtent = new TimeExtent({
476
+ start: new Date(times[this.layerName].start),
477
+ end: new Date(times[this.layerName].end),
478
+ });
479
+
480
+ const period = this.parserPeriod(
481
+ times[this.layerName].period,
482
+ );
483
+
484
+ this.TimesliderWidget.stops = {
485
+ interval: {
486
+ value:
487
+ period.years * 365 * 24 * 60 +
488
+ period.months * 31 * 24 * 60 +
489
+ period.weeks * 7 * 24 * 60 +
490
+ period.days * 24 * 60 +
491
+ period.hours * 60 +
492
+ period.minutes +
493
+ period.seconds / 60,
494
+ unit: 'minutes',
495
+ },
496
+ };
497
+
498
+ periodicity = times[this.layerName].period;
499
+ } else if (times[this.layerName].hasOwnProperty('array')) {
500
+ // Dates array
501
+ this.TimesliderWidget.fullTimeExtent = new TimeExtent({
502
+ start: new Date(times[this.layerName].array[0]),
503
+ end: new Date(
504
+ times[this.layerName].array[
505
+ times[this.layerName].array.length - 1
506
+ ],
507
+ ),
508
+ });
509
+ this.TimesliderWidget.stops = {
510
+ dates: times[this.layerName].array.map(
511
+ (e) => new Date(e),
512
+ ),
513
+ };
514
+
515
+ if (this.layer.type === 'wmts') {
516
+ this.layer.customLayerParameters = {};
517
+ const time = times[this.layerName].array.map(
518
+ (d) => new Date(d),
519
+ );
520
+
521
+ for (let i in time) {
522
+ timeDict[time[i]] = times[this.layerName].array[i];
523
+ }
524
+ }
525
+
526
+ periodicity = Math.floor(
527
+ (Date.parse(times[this.layerName].array[1]) -
528
+ Date.parse(times[this.layerName].array[0])) /
529
+ 86400000,
530
+ );
531
+ if (periodicity === 0) {
532
+ periodicity =
533
+ (new Date(times[this.layerName].array[1]).getHours() -
534
+ new Date(times[this.layerName].array[0]).getHours()) /
535
+ 24;
536
+ }
537
+ }
538
+
539
+ this.setState({ periodicity: periodicity });
540
+ if (this.TimesliderWidget.effectiveStops.length === 11) {
541
+ let period =
542
+ (this.TimesliderWidget.fullTimeExtent.end -
543
+ this.TimesliderWidget.fullTimeExtent.start) /
544
+ 590000000;
545
+ if (period > this.TimesliderWidget.stops.interval.value) {
546
+ this.TimesliderWidget.stops = {
547
+ interval: {
548
+ value: period,
549
+ unit: 'minutes',
550
+ },
551
+ };
552
+ }
553
+ }
554
+ this.TimesliderWidget.watch('timeExtent', (timeExtent) => {
555
+ if (!this.container.current ? true : false) {
556
+ this.TimesliderWidget.stop();
557
+ }
558
+ // let start = new Date(timeExtent.start).getTime();
559
+ // let end = new Date(timeExtent.end).getTime();
560
+ // this.props.time.elem.setAttribute('time-start', start);
561
+ // this.props.time.elem.setAttribute('time-end', end);
562
+ // if (this.props.download) {
563
+ // this.props.time.dataset.setAttribute('time-start', start);
564
+ // this.props.time.dataset.setAttribute('time-end', end);
565
+ // }
566
+ // this.props.time.dataset.setAttribute('time-start', start);
567
+ // this.props.time.dataset.setAttribute('time-end', end);
568
+ if (this.layer.type === 'wmts') {
569
+ this.layer.customLayerParameters = {};
570
+ this.layer.customLayerParameters['TIME'] =
571
+ timeDict[this.TimesliderWidget.timeExtent.end];
572
+ } else {
573
+ this.layer.customLayerParameters = {};
574
+ if (times[this.layerName].hasOwnProperty('array')) {
575
+ this.layer.customLayerParameters['TIME'] =
576
+ timeDict[this.TimesliderWidget.timeExtent.end];
577
+ } else {
578
+ const newDateTimeObject = new Date(
579
+ this.TimesliderWidget.timeExtent.start.toISOString(),
580
+ );
581
+ newDateTimeObject.setMinutes(
582
+ this.TimesliderWidget.timeExtent.start.getMinutes() +
583
+ this.TimesliderWidget.stops['interval'].value,
584
+ );
585
+ this.layer.customLayerParameters['TIME'] =
586
+ this.TimesliderWidget.timeExtent.start.toISOString() +
587
+ '/' +
588
+ newDateTimeObject.toISOString(); //OK
589
+ }
590
+ }
591
+ this.layer.refresh();
592
+ });
593
+ } // if there is dimension time
594
+ else {
595
+ this.TimesliderWidget.disabled = true;
596
+ }
597
+ });
598
+ }
452
599
  } // is feature or WMS/WMTS
453
600
  });
454
601
  });