@eeacms/volto-arcgis-block 0.1.35 → 0.1.38

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,8 +4,38 @@ 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.38](https://github.com/eea/volto-arcgis-block/compare/0.1.37...0.1.38)
8
+
9
+ - Bugs n improvements [`#114`](https://github.com/eea/volto-arcgis-block/pull/114)
10
+ - change clms-utils version [`029f7af`](https://github.com/eea/volto-arcgis-block/commit/029f7afae5aa03dd732ea8b6e01f59eceec5c281)
11
+ - ESLint fix [`dcdd9e7`](https://github.com/eea/volto-arcgis-block/commit/dcdd9e7b9fa206def9ff3f8173632c548ae83c59)
12
+ - ESLint fix [`6e0cba5`](https://github.com/eea/volto-arcgis-block/commit/6e0cba5ecc450deec0deded854f7ad46c6eb7cf8)
13
+ - Info widget table [`65da92c`](https://github.com/eea/volto-arcgis-block/commit/65da92c048bd363a7070c9e63f3c199f5be61721)
14
+
15
+ #### [0.1.37](https://github.com/eea/volto-arcgis-block/compare/0.1.36...0.1.37)
16
+
17
+ > 15 March 2022
18
+
19
+ - Develop [`#113`](https://github.com/eea/volto-arcgis-block/pull/113)
20
+ - Download flow [`#112`](https://github.com/eea/volto-arcgis-block/pull/112)
21
+
22
+ #### [0.1.36](https://github.com/eea/volto-arcgis-block/compare/0.1.35...0.1.36)
23
+
24
+ > 11 March 2022
25
+
26
+ - Develop [`#111`](https://github.com/eea/volto-arcgis-block/pull/111)
27
+ - Bugs n improvements [`#110`](https://github.com/eea/volto-arcgis-block/pull/110)
28
+ - Use cases [`#109`](https://github.com/eea/volto-arcgis-block/pull/109)
29
+ - MinZoom [`5c49b0b`](https://github.com/eea/volto-arcgis-block/commit/5c49b0b4252e5119142011bb710b1d73e3a563af)
30
+ - Info widget bug fix [`21fd11b`](https://github.com/eea/volto-arcgis-block/commit/21fd11be9e147b35652188fef70468bad35dba12)
31
+ - ESLint fix [`77e4aa8`](https://github.com/eea/volto-arcgis-block/commit/77e4aa8de5a28e8eb6f953f69e581bbdcafd6a63)
32
+ - Use case repeated titles [`c4f1374`](https://github.com/eea/volto-arcgis-block/commit/c4f1374974e39de3525bd5adfa7ac9d04e409b18)
33
+
7
34
  #### [0.1.35](https://github.com/eea/volto-arcgis-block/compare/0.1.34...0.1.35)
8
35
 
36
+ > 7 March 2022
37
+
38
+ - Develop [`#108`](https://github.com/eea/volto-arcgis-block/pull/108)
9
39
  - Bugs n improvements [`#107`](https://github.com/eea/volto-arcgis-block/pull/107)
10
40
  - ESLint fix [`5a7ecf9`](https://github.com/eea/volto-arcgis-block/commit/5a7ecf98e35803dd61d01d4faaa89af950b630e8)
11
41
  - Summary tooltip [`066b710`](https://github.com/eea/volto-arcgis-block/commit/066b710f179147557f36004dffc29b7c10820f99)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-arcgis-block",
3
- "version": "0.1.35",
3
+ "version": "0.1.38",
4
4
  "description": "volto-arcgis-block: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: CodeSyntax",
@@ -40,7 +40,7 @@
40
40
  "@fortawesome/fontawesome-svg-core": "1.2.35",
41
41
  "@fortawesome/free-solid-svg-icons": "5.15.3",
42
42
  "@fortawesome/react-fontawesome": "0.1.14",
43
- "@eeacms/volto-clms-utils": "0.1.1",
43
+ "@eeacms/volto-clms-utils": "*",
44
44
  "highcharts": "^9.3.2",
45
45
  "highcharts-react-official": "^3.1.0"
46
46
  },
@@ -81,7 +81,7 @@ class AreaWidget extends React.Component {
81
81
  'none';
82
82
  this.container.current
83
83
  .querySelector('.esri-widget--button')
84
- .classList.replace('esri-icon-right-arrow', 'esri-icon-cursor-marquee');
84
+ .classList.replace('esri-icon-close', 'esri-icon-cursor-marquee');
85
85
  // By invoking the setState, we notify the state we want to reach
86
86
  // and ensure that the component is rendered again
87
87
  this.setState({ showMapMenu: false });
@@ -95,7 +95,7 @@ class AreaWidget extends React.Component {
95
95
  'block';
96
96
  this.container.current
97
97
  .querySelector('.esri-widget--button')
98
- .classList.replace('esri-icon-cursor-marquee', 'esri-icon-right-arrow');
98
+ .classList.replace('esri-icon-cursor-marquee', 'esri-icon-close');
99
99
  // By invoking the setState, we notify the state we want to reach
100
100
  // and ensure that the component is rendered again
101
101
  this.setState({ showMapMenu: true });
@@ -65,7 +65,7 @@ class BasemapWidget extends React.Component {
65
65
  this.basemapGallery.domNode.style.display = 'none';
66
66
  this.container.current
67
67
  .querySelector('.esri-widget--button')
68
- .classList.replace('esri-icon-right-arrow', 'esri-icon-basemap');
68
+ .classList.replace('esri-icon-close', 'esri-icon-basemap');
69
69
  // By invoking the setState, we notify the state we want to reach
70
70
  // and ensure that the component is rendered again
71
71
  this.setState({ showMapMenu: false });
@@ -75,7 +75,7 @@ class BasemapWidget extends React.Component {
75
75
  this.basemapGallery.domNode.style.display = 'block';
76
76
  this.container.current
77
77
  .querySelector('.esri-widget--button')
78
- .classList.replace('esri-icon-basemap', 'esri-icon-right-arrow');
78
+ .classList.replace('esri-icon-basemap', 'esri-icon-close');
79
79
  // By invoking the setState, we notify the state we want to reach
80
80
  // and ensure that the component is rendered again
81
81
  this.setState({ showMapMenu: true });
@@ -50,7 +50,7 @@ class InfoWidget extends React.Component {
50
50
  'none';
51
51
  this.container.current
52
52
  .querySelector('.esri-widget--button')
53
- .classList.replace('esri-icon-right-arrow', 'esri-icon-description');
53
+ .classList.replace('esri-icon-close', 'esri-icon-description');
54
54
  // By invoking the setState, we notify the state we want to reach
55
55
  // and ensure that the component is rendered again
56
56
  this.setState({
@@ -64,7 +64,7 @@ class InfoWidget extends React.Component {
64
64
  this.props.mapViewer.setActiveWidget(this);
65
65
  this.container.current
66
66
  .querySelector('.esri-widget--button')
67
- .classList.replace('esri-icon-description', 'esri-icon-right-arrow');
67
+ .classList.replace('esri-icon-description', 'esri-icon-close');
68
68
  this.container.current.querySelector('.info-panel').style.display =
69
69
  'block';
70
70
  // By invoking the setState, we notify the state we want to reach
@@ -110,6 +110,7 @@ class InfoWidget extends React.Component {
110
110
  type: 'wmts',
111
111
  title: title,
112
112
  });
113
+ promises.push(Promise.reject());
113
114
  } else {
114
115
  layerTypes.push({
115
116
  isTimeSeries: true,
@@ -132,6 +133,7 @@ class InfoWidget extends React.Component {
132
133
  type: 'wmts',
133
134
  title: title,
134
135
  });
136
+ promises.push(Promise.reject());
135
137
  } else {
136
138
  layerTypes.push({
137
139
  isTimeSeries: false,
@@ -147,131 +149,144 @@ class InfoWidget extends React.Component {
147
149
  let data = response.value;
148
150
  let layer = layerTypes[index];
149
151
  let properties = [];
150
- if (layer.isTimeSeries) {
151
- switch (layer.type) {
152
- case 'wms':
153
- if (data.type === 'FeatureCollection') {
154
- if (data.features.length) {
155
- let obj = data.features.map((a) => {
156
- return a.properties;
157
- });
158
- properties = this.transformWmsData(obj);
159
- }
160
- } else if (data.doctype && data.doctype.name === 'html') {
161
- let th = data.querySelectorAll('tbody th');
162
- let tr = data.querySelectorAll(
163
- 'tbody tr:not(:first-of-type)',
164
- );
165
- if (th.length) {
166
- let obj = Array.from(tr).map((a) => {
167
- let x = [];
168
- a.querySelectorAll('td').forEach((td, index) => {
169
- x[th[index].textContent] = td.textContent;
152
+ if (response.status === 'rejected') {
153
+ this.infoData[index] = {
154
+ title: layer.title,
155
+ data: properties,
156
+ };
157
+ } else {
158
+ if (layer.isTimeSeries) {
159
+ switch (layer.type) {
160
+ case 'wms':
161
+ if (data.type === 'FeatureCollection') {
162
+ if (data.features.length) {
163
+ let obj = data.features.map((a) => {
164
+ return a.properties;
170
165
  });
171
- return x;
172
- });
173
- properties = this.transformWmsData(obj);
174
- }
175
- } else if (
176
- data.getElementsByTagName('FIELDS').length &&
177
- typeof data !== 'undefined'
178
- ) {
179
- let fields = data.getElementsByTagName('FIELDS');
180
- if (fields.length) {
181
- let obj = Array.from(fields).map((a) => {
182
- let x = [];
183
- Object.entries(a.attributes).forEach((b) => {
184
- x[b[1].name] = b[1].value;
166
+ properties = this.transformWmsData(obj);
167
+ }
168
+ } else if (
169
+ data.doctype &&
170
+ data.doctype.name === 'html'
171
+ ) {
172
+ let th = data.querySelectorAll('tbody th');
173
+ let tr = data.querySelectorAll(
174
+ 'tbody tr:not(:first-of-type)',
175
+ );
176
+ if (th.length) {
177
+ let obj = Array.from(tr).map((a) => {
178
+ let x = [];
179
+ a.querySelectorAll('td').forEach((td, index) => {
180
+ x[th[index].textContent] = td.textContent;
181
+ });
182
+ return x;
185
183
  });
186
- return x;
187
- });
188
- properties = this.transformWmsData(obj);
189
- }
190
- }
191
- this.infoData[index] = {
192
- title: layer.title,
193
- data: properties,
194
- time: true,
195
- };
196
- break;
197
- case 'wmts':
198
- this.infoData[index] = {
199
- title: layer.title,
200
- data: properties,
201
- time: true,
202
- };
203
- break;
204
- case 'featureLayer':
205
- this.infoData[index] = {
206
- title: layer.title,
207
- data: data,
208
- time: true,
209
- };
210
- break;
211
- default:
212
- break;
213
- }
214
- } else {
215
- switch (layer.type) {
216
- case 'wms':
217
- if (data.type === 'FeatureCollection') {
218
- if (data.features.length) {
219
- properties = data.features[0].properties;
220
- properties = Object.entries(properties);
221
- }
222
- } else if (data.doctype && data.doctype.name === 'html') {
223
- let th = data.querySelectorAll('tbody th');
224
- let td = data.querySelectorAll('tbody td');
225
- if (th.length) {
226
- let fields = [];
227
- th.forEach((item, index) => {
228
- fields.push([
229
- item.textContent,
230
- td[index].textContent,
231
- ]);
232
- });
233
- properties = fields;
184
+ properties = this.transformWmsData(obj);
185
+ }
186
+ } else if (
187
+ data.getElementsByTagName('FIELDS').length &&
188
+ typeof data !== 'undefined'
189
+ ) {
190
+ let fields = data.getElementsByTagName('FIELDS');
191
+ if (fields.length) {
192
+ let obj = Array.from(fields).map((a) => {
193
+ let x = [];
194
+ Object.entries(a.attributes).forEach((b) => {
195
+ x[b[1].name] = b[1].value;
196
+ });
197
+ return x;
198
+ });
199
+ properties = this.transformWmsData(obj);
200
+ }
234
201
  }
235
- } else if (
236
- data.getElementsByTagName('FIELDS').length &&
237
- typeof data !== 'undefined'
238
- ) {
239
- let fields = data.getElementsByTagName('FIELDS');
240
- if (fields.length) {
241
- properties = Object.entries(fields[0].attributes).map(
242
- (a) => {
202
+ this.infoData[index] = {
203
+ title: layer.title,
204
+ data: properties,
205
+ time: true,
206
+ };
207
+ break;
208
+ case 'wmts':
209
+ this.infoData[index] = {
210
+ title: layer.title,
211
+ data: properties,
212
+ time: true,
213
+ };
214
+ break;
215
+ case 'featureLayer':
216
+ this.infoData[index] = {
217
+ title: layer.title,
218
+ data: data,
219
+ time: true,
220
+ };
221
+ break;
222
+ default:
223
+ break;
224
+ }
225
+ } else {
226
+ switch (layer.type) {
227
+ case 'wms':
228
+ if (data.type === 'FeatureCollection') {
229
+ if (data.features.length) {
230
+ properties = data.features[0].properties;
231
+ properties = Object.entries(properties);
232
+ }
233
+ } else if (
234
+ data.doctype &&
235
+ data.doctype.name === 'html'
236
+ ) {
237
+ let th = data.querySelectorAll('tbody th');
238
+ let td = data.querySelectorAll('tbody td');
239
+ if (th.length) {
240
+ let fields = [];
241
+ th.forEach((item, index) => {
242
+ fields.push([
243
+ item.textContent,
244
+ td[index].textContent,
245
+ ]);
246
+ });
247
+ properties = fields;
248
+ }
249
+ } else if (
250
+ data.getElementsByTagName('FIELDS').length &&
251
+ typeof data !== 'undefined'
252
+ ) {
253
+ let fields = data.getElementsByTagName('FIELDS');
254
+ if (fields.length) {
255
+ properties = Object.entries(
256
+ fields[0].attributes,
257
+ ).map((a) => {
243
258
  return [a[1].name, a[1].value];
244
- },
245
- );
259
+ });
260
+ }
246
261
  }
247
- }
248
- this.infoData[index] = {
249
- title: layer.title,
250
- data: properties,
251
- };
252
- break;
253
- case 'wmts':
254
- this.infoData[index] = {
255
- title: layer.title,
256
- data: properties,
257
- };
258
- break;
259
- case 'featureLayer':
260
- if (data.results.length) {
261
- var graphic = data.results.filter((result) => {
262
- return result.graphic.layer === layers[index];
263
- })[0].graphic;
264
- if (graphic) {
265
- properties = graphic.attributes;
262
+ this.infoData[index] = {
263
+ title: layer.title,
264
+ data: properties,
265
+ };
266
+ break;
267
+ case 'wmts':
268
+ this.infoData[index] = {
269
+ title: layer.title,
270
+ data: properties,
271
+ };
272
+ break;
273
+ case 'featureLayer':
274
+ if (data.results.length) {
275
+ var graphic = data.results.filter((result) => {
276
+ return result.graphic.layer === layers[index];
277
+ })[0].graphic;
278
+ if (graphic) {
279
+ properties = graphic.attributes;
280
+ }
266
281
  }
267
- }
268
- this.infoData[index] = {
269
- title: layer.title,
270
- data: Object.entries(properties),
271
- };
272
- break;
273
- default:
274
- break;
282
+ this.infoData[index] = {
283
+ title: layer.title,
284
+ data: Object.entries(properties),
285
+ };
286
+ break;
287
+ default:
288
+ break;
289
+ }
275
290
  }
276
291
  }
277
292
  });
@@ -418,13 +433,17 @@ class InfoWidget extends React.Component {
418
433
  }
419
434
 
420
435
  transformWmsData(obj) {
421
- let values = { timeFields: {}, data: {}, variables: {} };
436
+ let values = { timeFields: {}, data: {}, variables: {}, tableData: {} };
422
437
  let startField = Object.keys(obj[0]).find(
423
438
  (a, i) =>
424
439
  a.toUpperCase().includes('DATE') &&
425
440
  !isNaN(parseInt(Object.values(obj[0])[i])),
426
441
  );
427
442
  values.timeFields['start'] = startField;
443
+ values.tableData['fields'] = Object.keys(obj[0]);
444
+ values.tableData['values'] = obj.map((a) => {
445
+ return Object.entries(a);
446
+ });
428
447
  let fields = Object.keys(obj[0]).filter((a, i) => {
429
448
  return (
430
449
  !isNaN(parseInt(Object.values(obj[0])[i])) &&
@@ -495,10 +514,13 @@ class InfoWidget extends React.Component {
495
514
  }
496
515
 
497
516
  identify(layer, evt) {
498
- let values = { timeFields: {}, data: {}, variables: {} };
517
+ let values = { timeFields: {}, data: {}, variables: {}, tableData: {} };
499
518
  //Complete time data
500
519
  values.timeFields['start'] = layer.timeInfo.startField;
501
520
  values.timeFields['end'] = layer.timeInfo.endField;
521
+ values.tableData['fields'] = layer.fields.map((a) => {
522
+ return a.name;
523
+ });
502
524
  let timeQuery = layer.createQuery();
503
525
  timeQuery.outFields = [layer.timeInfo.startField];
504
526
  let fields = layer.fields
@@ -537,7 +559,7 @@ class InfoWidget extends React.Component {
537
559
  query.units = 'meters';
538
560
  query.spatialRelationship = 'intersects'; // this is the default
539
561
  query.returnGeometry = true;
540
- query.outFields = [fields.toString()]; // Information to be returned
562
+ query.outFields = [values.tableData.fields.toString()]; // Information to be returned
541
563
  values.data['outFields'] = [fields.toString()];
542
564
 
543
565
  let p2 = layer.queryFeatures(query).then((response) => {
@@ -564,8 +586,16 @@ class InfoWidget extends React.Component {
564
586
  e.geometry.latitude === point.latitude &&
565
587
  e.geometry.longitude === point.longitude,
566
588
  );
589
+ values.tableData['values'] = values.data['values'] = info.map((e) => {
590
+ return Object.entries(e.attributes);
591
+ });
567
592
  values.data['values'] = info.map((e) => {
568
- return e.attributes;
593
+ let attributes = e.attributes;
594
+ return Object.fromEntries(
595
+ Object.entries(attributes).filter(([key]) =>
596
+ values.data.outFields[0].split(',').includes(key),
597
+ ),
598
+ );
569
599
  });
570
600
  });
571
601
 
@@ -680,6 +710,27 @@ class InfoWidget extends React.Component {
680
710
  );
681
711
  }
682
712
 
713
+ loadTimeInfoTable(index) {
714
+ let properties = this.infoData[index].data.tableData.values;
715
+ let table = properties.map((item) => {
716
+ let rows = item.map((row) => {
717
+ return (
718
+ <tr key={row}>
719
+ {Object.values(row).map((val) => (
720
+ <td key={val}>{val}</td>
721
+ ))}
722
+ </tr>
723
+ );
724
+ });
725
+ return (
726
+ <table className="info-table">
727
+ <tbody>{rows}</tbody>
728
+ </table>
729
+ );
730
+ });
731
+ return <>{table}</>;
732
+ }
733
+
683
734
  loadVariableSelector(index) {
684
735
  let response = this.infoData[index].data;
685
736
  let variables = response.variables.options;
@@ -898,6 +949,7 @@ class InfoWidget extends React.Component {
898
949
  options={this.loadInfoChart(this.state.layerIndex)}
899
950
  />
900
951
  {this.loadStatisticsSelector(this.state.layerIndex)}
952
+ {this.loadTimeInfoTable(this.state.layerIndex)}
901
953
  </>
902
954
  )}
903
955
  {this.state.popup &&
@@ -38,7 +38,7 @@ class LegendWidget extends React.Component {
38
38
  'none';
39
39
  this.container.current
40
40
  .querySelector('.esri-widget--button')
41
- .classList.replace('esri-icon-right-arrow', 'esri-icon-legend');
41
+ .classList.replace('esri-icon-close', 'esri-icon-legend');
42
42
  // By invoking the setState, we notify the state we want to reach
43
43
  // and ensure that the component is rendered again
44
44
  this.setState({ showMapMenu: false });
@@ -46,7 +46,7 @@ class LegendWidget extends React.Component {
46
46
  this.props.mapViewer.setActiveWidget(this);
47
47
  this.container.current
48
48
  .querySelector('.esri-widget--button')
49
- .classList.replace('esri-icon-legend', 'esri-icon-right-arrow');
49
+ .classList.replace('esri-icon-legend', 'esri-icon-close');
50
50
  this.container.current.querySelector('.legend-panel').style.display =
51
51
  'block';
52
52
  // By invoking the setState, we notify the state we want to reach
@@ -14,6 +14,7 @@ import { MapViewerConfig } from '../../actions';
14
14
  import { compose } from 'redux';
15
15
  import { connect } from 'react-redux';
16
16
  import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
17
+ import useCartState from '@eeacms/volto-clms-utils/cart/useCartState';
17
18
 
18
19
  //import "isomorphic-fetch"; <-- Necessary to use fetch?
19
20
  var Map, MapView, Zoom;
@@ -75,6 +76,9 @@ class MapViewer extends React.Component {
75
76
  map: this.map,
76
77
  center: this.mapCfg.center,
77
78
  zoom: this.mapCfg.zoom,
79
+ constraints: {
80
+ minZoom: this.mapCfg.minZoom,
81
+ },
78
82
  ui: {
79
83
  components: ['attribution'],
80
84
  },
@@ -142,16 +146,9 @@ class MapViewer extends React.Component {
142
146
 
143
147
  renderArea() {
144
148
  if (this.props.mapviewer_config.Download) return;
145
- if (this.view)
146
- return (
147
- <AreaWidget
148
- view={this.view}
149
- map={this.map}
150
- mapViewer={this}
151
- download={this.props.mapviewer_config.Download}
152
- updateArea={this.updateArea}
153
- />
154
- );
149
+ if (this.view) {
150
+ return <CheckLogin reference={this} />;
151
+ }
155
152
  }
156
153
 
157
154
  renderScale() {
@@ -203,6 +200,23 @@ class MapViewer extends React.Component {
203
200
  }
204
201
  }
205
202
 
203
+ export const CheckLogin = ({ reference }) => {
204
+ let { isLoggedIn } = useCartState();
205
+ return (
206
+ <>
207
+ {isLoggedIn && (
208
+ <AreaWidget
209
+ view={reference.view}
210
+ map={reference.map}
211
+ mapViewer={reference}
212
+ download={reference.props.mapviewer_config.Download}
213
+ updateArea={reference.updateArea}
214
+ />
215
+ )}
216
+ </>
217
+ );
218
+ };
219
+
206
220
  export default compose(
207
221
  connect(
208
222
  (state, props) => ({
@@ -38,7 +38,7 @@ class MeasurementWidget extends React.Component {
38
38
  'none';
39
39
  this.container.current
40
40
  .querySelector('.esri-widget--button')
41
- .classList.replace('esri-icon-right-arrow', 'esri-icon-measure');
41
+ .classList.replace('esri-icon-close', 'esri-icon-measure');
42
42
  // By invoking the setState, we notify the state we want to reach
43
43
  // and ensure that the component is rendered again
44
44
  this.setState({ showMapMenu: false });
@@ -56,7 +56,7 @@ class MeasurementWidget extends React.Component {
56
56
  'block';
57
57
  this.container.current
58
58
  .querySelector('.esri-widget--button')
59
- .classList.replace('esri-icon-measure', 'esri-icon-right-arrow');
59
+ .classList.replace('esri-icon-measure', 'esri-icon-close');
60
60
  // By invoking the setState, we notify the state we want to reach
61
61
  // and ensure that the component is rendered again
62
62
  this.setState({ showMapMenu: true });
@@ -111,7 +111,7 @@ class MeasurementWidget extends React.Component {
111
111
 
112
112
  showCoordinates(pt) {
113
113
  this.setState({
114
- latlong: pt.latitude.toFixed(3) + ' ' + pt.longitude.toFixed(3),
114
+ latlong: pt.latitude.toFixed(4) + ' ' + pt.longitude.toFixed(4),
115
115
  });
116
116
  }
117
117
 
@@ -212,8 +212,9 @@ export const AddCartItem = ({
212
212
  <ul>
213
213
  <li>
214
214
  <p>
215
- Download full dataset (Note: Dowload process will take
216
- longer).
215
+ Add full dataset (Note: Download process will take longer
216
+ and for some very large datasets, a Bounding box for
217
+ Europe is selected by default).
217
218
  </p>
218
219
  </li>
219
220
  <li>
@@ -359,7 +360,7 @@ class MenuWidget extends React.Component {
359
360
  this.container.current.querySelector('#paneles').style.display = 'none';
360
361
  this.container.current
361
362
  .querySelector('.esri-widget--button')
362
- .classList.replace('esri-icon-left-arrow', 'esri-icon-drag-horizontal');
363
+ .classList.replace('esri-icon-close', 'esri-icon-drag-horizontal');
363
364
  if (document.contains(document.querySelector('.timeslider-container')))
364
365
  document.querySelector('.timeslider-container').style.display = 'none';
365
366
 
@@ -372,7 +373,7 @@ class MenuWidget extends React.Component {
372
373
  this.container.current.querySelector('#paneles').style.display = 'block';
373
374
  this.container.current
374
375
  .querySelector('.esri-widget--button')
375
- .classList.replace('esri-icon-drag-horizontal', 'esri-icon-left-arrow');
376
+ .classList.replace('esri-icon-drag-horizontal', 'esri-icon-close');
376
377
  if (document.contains(document.querySelector('.timeslider-container')))
377
378
  document.querySelector('.timeslider-container').style.display = 'block';
378
379
 
@@ -43,7 +43,7 @@ class PrintWidget extends React.Component {
43
43
  this.print.domNode.style.display = 'none';
44
44
  this.container.current
45
45
  .querySelector('.esri-widget--button')
46
- .classList.replace('esri-icon-right-arrow', 'esri-icon-printer');
46
+ .classList.replace('esri-icon-close', 'esri-icon-printer');
47
47
  // By invoking the setState, we notify the state we want to reach
48
48
  // and ensure that the component is rendered again
49
49
  this.setState({ showMapMenu: false });
@@ -52,7 +52,7 @@ class PrintWidget extends React.Component {
52
52
  this.print.domNode.style.display = 'block';
53
53
  this.container.current
54
54
  .querySelector('.esri-widget--button')
55
- .classList.replace('esri-icon-printer', 'esri-icon-right-arrow');
55
+ .classList.replace('esri-icon-printer', 'esri-icon-close');
56
56
  // By invoking the setState, we notify the state we want to reach
57
57
  // and ensure that the component is rendered again
58
58
  this.setState({ showMapMenu: true });
@@ -3,6 +3,7 @@ const config = {
3
3
  div: 'mapDiv',
4
4
  center: [15, 50],
5
5
  zoom: 3,
6
+ minZoom: 3,
6
7
  },
7
8
  Components: [
8
9
  {
@@ -638,6 +638,7 @@
638
638
  display: block;
639
639
  margin-bottom: 1rem;
640
640
  font-weight: bold;
641
+ word-break: break-word;
641
642
  }
642
643
 
643
644
  .info-panel select {
@@ -646,6 +647,7 @@
646
647
 
647
648
  .info-table {
648
649
  width: 100%;
650
+ margin-bottom: 1rem;
649
651
  table-layout: fixed;
650
652
  }
651
653
 
@@ -665,6 +667,10 @@
665
667
  background-color: #ebebeb;
666
668
  }
667
669
 
670
+ .info-statistics-panel {
671
+ margin-bottom: 1rem;
672
+ }
673
+
668
674
  /* Time slider*/
669
675
  .esri-ui-bottom-right.esri-ui-corner {
670
676
  width: 100%;
@@ -1,4 +1,5 @@
1
1
  import React, { createRef } from 'react';
2
+ import { Loader } from 'semantic-ui-react';
2
3
 
3
4
  let layerControl,
4
5
  navigationControl,
@@ -17,6 +18,7 @@ class InfoWidget extends React.Component {
17
18
  layerSpatial = props.layerSpatial;
18
19
  layerHighlight = props.layerHighlight;
19
20
  this.container = createRef();
21
+ this.loadOnce = true;
20
22
  }
21
23
 
22
24
  /**
@@ -63,7 +65,7 @@ class InfoWidget extends React.Component {
63
65
  <div className="use-case-detail-info">
64
66
  <span>{UseCase.Use_case_topics}</span>
65
67
  <span>{UseCase.Use_case_submitting_production_year}</span>
66
- <span>{UseCase.Spatial_coverage}</span>
68
+ <span>{UseCase.Origin_name}</span>
67
69
  <span>{responsibleOrganizationOrPerson}</span>
68
70
  </div>
69
71
  <div className="use-case-detail-description">
@@ -87,42 +89,56 @@ class InfoWidget extends React.Component {
87
89
  * @returns useCasesRegion
88
90
  */
89
91
  getDataBrief(data) {
92
+ let titles = [];
90
93
  let children = data.map((val) => {
94
+ let hideTitle = false;
91
95
  let responsibleOrganizationOrPerson = val.Responsible_organisation
92
96
  ? val.Responsible_organisation
93
97
  : val.Contact_person_name_
94
98
  ? val.Contact_person_name_
95
99
  : '';
100
+ if (titles.includes(val.Use_case_title)) {
101
+ hideTitle = true;
102
+ } else {
103
+ titles.push(val.Use_case_title);
104
+ }
96
105
  return (
97
- <>
106
+ <div
107
+ key={val.OBJECTID}
108
+ className="use-case-element"
109
+ aria-hidden="true"
110
+ onClick={() => {
111
+ layerControl.getGeometry(val.Spatial_coverage, layerHighlight);
112
+ layerControl.showLayer(layerHighlight.id);
113
+ mapViewer.setState((prevState) => ({
114
+ useCaseLevel: 4,
115
+ selectedUseCase: val,
116
+ previousState: prevState.useCaseLevel,
117
+ }));
118
+ }}
119
+ id={`use_case_${val.OBJECTID}`}
120
+ >
98
121
  <div
99
- key={val.Use_case_title}
100
- className="use-case-element"
101
- aria-hidden="true"
102
- onClick={() => {
103
- layerControl.getGeometry(val.Spatial_coverage, layerHighlight);
104
- layerControl.showLayer(layerHighlight.id);
105
- mapViewer.setState((prevState) => ({
106
- useCaseLevel: 4,
107
- selectedUseCase: val,
108
- previousState: prevState.useCaseLevel,
109
- }));
110
- }}
111
- id={`use_case_${val.OBJECTID}`}
122
+ className="use-case-element-title"
123
+ style={{ display: hideTitle && 'none' }}
112
124
  >
113
- <div className="use-case-element-title">{val.Use_case_title}</div>
114
- <div className="use-case-element-description">
115
- <span>{val.Use_case_topics}</span>
116
- <span>{val.Use_case_submitting_production_year}</span>
117
- <span className="use-case-coverage">{val.Spatial_coverage}</span>
118
- <span>{responsibleOrganizationOrPerson}</span>
119
- </div>
125
+ {val.Use_case_title}
120
126
  </div>
121
- </>
127
+ <div className="use-case-element-description">
128
+ <span>{val.Use_case_topics}</span>
129
+ <span>{val.Use_case_submitting_production_year}</span>
130
+ <span
131
+ className="use-case-coverage"
132
+ data-country-code={val.Spatial_coverage}
133
+ >
134
+ {val.Origin_name}
135
+ </span>
136
+ <span>{responsibleOrganizationOrPerson}</span>
137
+ </div>
138
+ </div>
122
139
  );
123
140
  });
124
-
125
- return <>{children}</>;
141
+ return children;
126
142
  }
127
143
 
128
144
  /**
@@ -229,24 +245,22 @@ class InfoWidget extends React.Component {
229
245
  let children = this.getDataBrief(data);
230
246
 
231
247
  return (
232
- <>
248
+ <div
249
+ key={Copernicus_Land_Monitoring_Service_products_used}
250
+ className="use-cases-dropdown"
251
+ >
233
252
  <div
234
- key={Copernicus_Land_Monitoring_Service_products_used}
235
- className="use-cases-dropdown"
253
+ className="ccl-expandable__button"
254
+ aria-expanded="false"
255
+ onClick={this.toggleDropdownContent.bind(this)}
256
+ onKeyDown={this.toggleDropdownContent.bind(this)}
257
+ tabIndex="0"
258
+ role="button"
236
259
  >
237
- <div
238
- className="ccl-expandable__button"
239
- aria-expanded="false"
240
- onClick={this.toggleDropdownContent.bind(this)}
241
- onKeyDown={this.toggleDropdownContent.bind(this)}
242
- tabIndex="0"
243
- role="button"
244
- >
245
- {Copernicus_Land_Monitoring_Service_products_used}
246
- </div>
247
- <div className="use-cases-element-container">{children}</div>
260
+ {Copernicus_Land_Monitoring_Service_products_used}
248
261
  </div>
249
- </>
262
+ <div className="use-cases-element-container">{children}</div>
263
+ </div>
250
264
  );
251
265
  }
252
266
 
@@ -270,10 +284,18 @@ class InfoWidget extends React.Component {
270
284
  DOMElements.push(
271
285
  this.getDataSummary(processedData[product_use_name], product_use_name),
272
286
  );
273
-
274
287
  return <>{DOMElements}</>;
275
288
  }
276
289
 
290
+ getCountryNames(countries) {
291
+ let url =
292
+ this.props.layerHighlight.url +
293
+ '/0/query?where=CNTR_ID+in+%28' +
294
+ countries +
295
+ '%29&text=&objectIds=&time=&timeRelation=esriTimeRelationOverlaps&geometry=&geometryType=esriGeometryEnvelope&inSR=&spatialRel=esriSpatialRelIntersects&distance=&units=esriSRUnit_Foot&relationParam=&outFields=CNTR_ID%2C+NAME_ENGL&returnGeometry=false&returnTrueCurves=false&maxAllowableOffset=&geometryPrecision=&outSR=&havingClause=&returnIdsOnly=false&returnCountOnly=false&orderByFields=&groupByFieldsForStatistics=&outStatistics=&returnZ=false&returnM=false&gdbVersion=&historicMoment=&returnDistinctValues=false&resultOffset=&resultRecordCount=&returnExtentOnly=false&sqlFormat=none&datumTransformation=&parameterValues=&rangeValues=&quantizationParameters=&featureEncoding=esriDefault&f=pjson';
296
+ return fetch(url);
297
+ }
298
+
277
299
  /**
278
300
  * Returns all different service product names
279
301
  * @param {Array} features
@@ -302,21 +324,33 @@ class InfoWidget extends React.Component {
302
324
  * Shows summarized information of a whole set of use cases.
303
325
  */
304
326
  showSummary() {
305
- if (view !== undefined && this.features === undefined) {
327
+ if (view !== undefined && this.features === undefined && this.loadOnce) {
328
+ this.loadOnce = false;
306
329
  (async () => {
307
330
  let features = await layerSpatial
308
331
  .queryFeatures()
309
332
  .then((featureSet) => featureSet.features);
310
- features = layerControl.orderFeatures(features);
311
-
312
- this.features = features;
333
+ let countryCodes = features
334
+ .map((a) => {
335
+ return a.attributes;
336
+ })
337
+ .map((b) => {
338
+ return "'" + b.Spatial_coverage + "'";
339
+ })
340
+ .filter((v, i, a) => a.indexOf(v) === i);
341
+ this.getCountryNames(countryCodes)
342
+ .then((response) => response.json())
343
+ .then((data) => {
344
+ features = layerControl.orderFeatures(features, data.features);
345
+ this.features = features;
313
346
 
314
- mapViewer.setState((prevState) => ({
315
- useCaseLevel: 1,
316
- region: '',
317
- selectedUseCase: '',
318
- previousState: prevState.useCaseLevel,
319
- }));
347
+ mapViewer.setState((prevState) => ({
348
+ useCaseLevel: 1,
349
+ region: '',
350
+ selectedUseCase: '',
351
+ previousState: prevState.useCaseLevel,
352
+ }));
353
+ });
320
354
  })();
321
355
  } else if (this.features !== undefined) {
322
356
  if (mapViewer.state.useCaseLevel !== 1) {
@@ -388,6 +422,9 @@ class InfoWidget extends React.Component {
388
422
  <>
389
423
  <div className="use-cases-products-block cont-w-50">
390
424
  {this.useCasesInformationPanel()}
425
+ {!this.features && (
426
+ <Loader active inline="centered" indeterminate size="small" />
427
+ )}
391
428
  </div>
392
429
  </>
393
430
  );
@@ -196,7 +196,7 @@ class LayerControl {
196
196
  document
197
197
  .querySelectorAll('.use-case-element-description .use-case-coverage')
198
198
  .forEach((element) => {
199
- if (element.innerText === country_code) {
199
+ if (element.dataset.countryCode === country_code) {
200
200
  element.closest('.use-case-element').classList.add('selected');
201
201
  }
202
202
  });
@@ -218,21 +218,66 @@ class LayerControl {
218
218
  * @param {Object} features
219
219
  * @returns features ordered
220
220
  */
221
- orderFeatures(features) {
221
+ orderFeatures(features, countries) {
222
+ features.map((feature) => {
223
+ let country = countries
224
+ .map((a) => {
225
+ return a.attributes;
226
+ })
227
+ .find((b) => {
228
+ return b.CNTR_ID === feature.attributes.Spatial_coverage;
229
+ });
230
+ if (country) {
231
+ feature.attributes.Origin_name = country.NAME_ENGL;
232
+ }
233
+ return feature;
234
+ });
235
+
222
236
  features.sort(function (a, b) {
223
237
  if (
224
238
  a.attributes.Copernicus_Land_Monitoring_Service_products_used <
225
239
  b.attributes.Copernicus_Land_Monitoring_Service_products_used
226
240
  )
227
241
  return -1;
228
-
229
- if (
242
+ else if (
230
243
  a.attributes.Copernicus_Land_Monitoring_Service_products_used >
231
244
  b.attributes.Copernicus_Land_Monitoring_Service_products_used
232
245
  )
233
246
  return 1;
234
-
235
- return 0;
247
+ else {
248
+ if (a.attributes.Use_case_title < b.attributes.Use_case_title)
249
+ return -1;
250
+ else if (a.attributes.Use_case_title > b.attributes.Use_case_title)
251
+ return 1;
252
+ else {
253
+ if (
254
+ a.attributes.Use_case_submitting_production_year >
255
+ b.attributes.Use_case_submitting_production_year
256
+ ) {
257
+ return -1;
258
+ } else if (
259
+ a.attributes.Use_case_submitting_production_year <
260
+ b.attributes.Use_case_submitting_production_year
261
+ ) {
262
+ return 1;
263
+ } else {
264
+ var sortOrder = ['EEA', 'EU'];
265
+ if (
266
+ !sortOrder.includes(a.attributes.Origin_name) &&
267
+ !sortOrder.includes(b.attributes.Origin_name)
268
+ ) {
269
+ return a.attributes.Origin_name.localeCompare(
270
+ b.attributes.Origin_name,
271
+ );
272
+ } else {
273
+ return (
274
+ -sortOrder.indexOf(a.attributes.Origin_name) -
275
+ -sortOrder.indexOf(b.attributes.Origin_name)
276
+ );
277
+ }
278
+ }
279
+ }
280
+ }
236
281
  });
237
282
  return features;
238
283
  }
@@ -35,14 +35,14 @@ class LegendWidget extends React.Component {
35
35
  'none';
36
36
  this.container.current
37
37
  .querySelector('.esri-widget--button')
38
- .classList.replace('esri-icon-right-arrow', 'esri-icon-legend');
38
+ .classList.replace('esri-icon-close', 'esri-icon-legend');
39
39
  // By invoking the setState, we notify the state we want to reach
40
40
  // and ensure that the component is rendered again
41
41
  this.mapViewer.setState({ showMapMenu: false });
42
42
  } else {
43
43
  this.container.current
44
44
  .querySelector('.esri-widget--button')
45
- .classList.replace('esri-icon-legend', 'esri-icon-right-arrow');
45
+ .classList.replace('esri-icon-legend', 'esri-icon-close');
46
46
  this.container.current.querySelector('.legend-panel').style.display =
47
47
  'block';
48
48
 
@@ -177,6 +177,7 @@
177
177
  margin-bottom: 0;
178
178
  color: #656565;
179
179
  font-size: 0.875rem;
180
+ word-break: break-word;
180
181
  }
181
182
 
182
183
  .use-case-element-description span:not(:last-of-type) {