@eeacms/volto-arcgis-block 0.1.23 → 0.1.27
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 +52 -0
- package/Jenkinsfile +4 -3
- package/package.json +4 -2
- package/src/components/MapViewer/AreaWidget.jsx +12 -5
- package/src/components/MapViewer/BasemapWidget.jsx +1 -1
- package/src/components/MapViewer/InfoWidget.jsx +469 -0
- package/src/components/MapViewer/MapViewer.jsx +22 -1
- package/src/components/MapViewer/MenuWidget.jsx +241 -65
- package/src/components/MapViewer/TimesliderWidget.jsx +57 -6
- package/src/components/MapViewer/css/ArcgisMap.css +53 -0
- package/src/components/UseCasesMapViewer/InfoWidget.jsx +8 -4
- package/src/components/UseCasesMapViewer/LayerControl.jsx +12 -6
- package/src/components/UseCasesMapViewer/LegendWidget.jsx +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,8 +4,60 @@ 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.27](https://github.com/eea/volto-arcgis-block/compare/0.1.26...0.1.27)
|
|
8
|
+
|
|
9
|
+
- Bugs n improvements [`#91`](https://github.com/eea/volto-arcgis-block/pull/91)
|
|
10
|
+
- ESLint fix [`27ee762`](https://github.com/eea/volto-arcgis-block/commit/27ee762c776b8635cb414edf5f20e3e40187801b)
|
|
11
|
+
- Granularity [`9e8d87a`](https://github.com/eea/volto-arcgis-block/commit/9e8d87a9b5baebec5d5300195bd3ba2f9d5e1e2c)
|
|
12
|
+
|
|
13
|
+
#### [0.1.26](https://github.com/eea/volto-arcgis-block/compare/0.1.25...0.1.26)
|
|
14
|
+
|
|
15
|
+
> 17 January 2022
|
|
16
|
+
|
|
17
|
+
- Develop [`#89`](https://github.com/eea/volto-arcgis-block/pull/89)
|
|
18
|
+
- Bug fixing [`#88`](https://github.com/eea/volto-arcgis-block/pull/88)
|
|
19
|
+
- Bugs n improvements [`#87`](https://github.com/eea/volto-arcgis-block/pull/87)
|
|
20
|
+
- Develop [`#86`](https://github.com/eea/volto-arcgis-block/pull/86)
|
|
21
|
+
- Time slider drag/drop [`#85`](https://github.com/eea/volto-arcgis-block/pull/85)
|
|
22
|
+
- Use cases [`#84`](https://github.com/eea/volto-arcgis-block/pull/84)
|
|
23
|
+
- Develop [`#81`](https://github.com/eea/volto-arcgis-block/pull/81)
|
|
24
|
+
- Downloadable dataset [`c48eea9`](https://github.com/eea/volto-arcgis-block/commit/c48eea93b0f71134f9aa8fee1f5d6b4cf7e1368b)
|
|
25
|
+
- add flattenToAppURL to send the correct url to the MapViewerConfig action [`ca5f684`](https://github.com/eea/volto-arcgis-block/commit/ca5f684ae683e0df249d3fab3534f579583f03a8)
|
|
26
|
+
- ESLint fix [`7f43112`](https://github.com/eea/volto-arcgis-block/commit/7f4311224483fb599e7cecfe65096166ed5f2026)
|
|
27
|
+
- Use case highlight [`2279a5a`](https://github.com/eea/volto-arcgis-block/commit/2279a5ad669172545588e9030e3cf49ca4e30dfa)
|
|
28
|
+
|
|
29
|
+
#### [0.1.25](https://github.com/eea/volto-arcgis-block/compare/0.1.24...0.1.25)
|
|
30
|
+
|
|
31
|
+
> 5 January 2022
|
|
32
|
+
|
|
33
|
+
- Develop [`#83`](https://github.com/eea/volto-arcgis-block/pull/83)
|
|
34
|
+
- Bugs n improvements [`#82`](https://github.com/eea/volto-arcgis-block/pull/82)
|
|
35
|
+
- ESLint fix [`c129348`](https://github.com/eea/volto-arcgis-block/commit/c129348ee3886e0419029dd2bfe835c28878444c)
|
|
36
|
+
- Filter by product and dataset [`c89711a`](https://github.com/eea/volto-arcgis-block/commit/c89711acf4f46c8426c7e34add2baf4debe67697)
|
|
37
|
+
- ESLint fix [`406842a`](https://github.com/eea/volto-arcgis-block/commit/406842a54f1964bf2b4b0e709719028a5a47a903)
|
|
38
|
+
- Timeslider bug fixing [`c7a8345`](https://github.com/eea/volto-arcgis-block/commit/c7a834538a54a8c48e2e34259d3d733a1194007a)
|
|
39
|
+
|
|
40
|
+
#### [0.1.24](https://github.com/eea/volto-arcgis-block/compare/0.1.23...0.1.24)
|
|
41
|
+
|
|
42
|
+
> 21 December 2021
|
|
43
|
+
|
|
44
|
+
- Develop [`#77`](https://github.com/eea/volto-arcgis-block/pull/77)
|
|
45
|
+
- Bugs n improvements [`#80`](https://github.com/eea/volto-arcgis-block/pull/80)
|
|
46
|
+
- The area array to a json object and added a unique_id to the cart [`#79`](https://github.com/eea/volto-arcgis-block/pull/79)
|
|
47
|
+
- Remove Global from legend [`#76`](https://github.com/eea/volto-arcgis-block/pull/76)
|
|
48
|
+
- ESLint fix [`974d62b`](https://github.com/eea/volto-arcgis-block/commit/974d62bd5b66025e12fa85dffcd0d1a5ae04cba4)
|
|
49
|
+
- ESLint fix [`5c4b0d7`](https://github.com/eea/volto-arcgis-block/commit/5c4b0d76de50edd85809c35449f2fb364a9e9312)
|
|
50
|
+
- ESLint fix [`d57d144`](https://github.com/eea/volto-arcgis-block/commit/d57d144fd0e3dbbbbc95195f289d50e6c4b03375)
|
|
51
|
+
- ESLint fix [`9eb0a0c`](https://github.com/eea/volto-arcgis-block/commit/9eb0a0c17d018ac1b25bde422ac149b46b146ff4)
|
|
52
|
+
- ESLint fix [`5fd3772`](https://github.com/eea/volto-arcgis-block/commit/5fd3772f197a3a380067962a5638dbf901a4624e)
|
|
53
|
+
- Pixel info [`1ccef39`](https://github.com/eea/volto-arcgis-block/commit/1ccef39a9adcc3ee94655c77a5a166b613600a08)
|
|
54
|
+
- NUTS bug fix [`37fe7f3`](https://github.com/eea/volto-arcgis-block/commit/37fe7f3fad2c005e3b7c234b3b0564d5df92fa8c)
|
|
55
|
+
|
|
7
56
|
#### [0.1.23](https://github.com/eea/volto-arcgis-block/compare/0.1.22...0.1.23)
|
|
8
57
|
|
|
58
|
+
> 17 December 2021
|
|
59
|
+
|
|
60
|
+
- Develop [`#75`](https://github.com/eea/volto-arcgis-block/pull/75)
|
|
9
61
|
- Use cases region highlight [`#71`](https://github.com/eea/volto-arcgis-block/pull/71)
|
|
10
62
|
- Bugs n improvements [`#74`](https://github.com/eea/volto-arcgis-block/pull/74)
|
|
11
63
|
- Add NUTS 2 in the Map viewer [`#72`](https://github.com/eea/volto-arcgis-block/pull/72)
|
package/Jenkinsfile
CHANGED
|
@@ -4,7 +4,7 @@ pipeline {
|
|
|
4
4
|
environment {
|
|
5
5
|
GIT_NAME = "volto-arcgis-block"
|
|
6
6
|
NAMESPACE = "@eeacms"
|
|
7
|
-
SONARQUBE_TAGS = "volto.eea.europa.eu,clms.land.copernicus.eu"
|
|
7
|
+
SONARQUBE_TAGS = "volto.eea.europa.eu,clms.land.copernicus.eu,water.europa.eu-freshwater"
|
|
8
8
|
DEPENDENCIES = ""
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -124,7 +124,7 @@ pipeline {
|
|
|
124
124
|
node(label: 'docker') {
|
|
125
125
|
script {
|
|
126
126
|
try {
|
|
127
|
-
sh '''docker pull plone; docker run -d --name="$BUILD_TAG-plone" -e SITE="Plone" -e PROFILES="profile-plone.restapi:blocks" plone fg'''
|
|
127
|
+
sh '''docker pull plone; docker run -d --rm --name="$BUILD_TAG-plone" -e SITE="Plone" -e PROFILES="profile-plone.restapi:blocks" plone fg'''
|
|
128
128
|
sh '''docker pull plone/volto-addon-ci; docker run -i --name="$BUILD_TAG-cypress" --link $BUILD_TAG-plone:plone -e NAMESPACE="$NAMESPACE" -e GIT_NAME=$GIT_NAME -e GIT_BRANCH="$BRANCH_NAME" -e GIT_CHANGE_ID="$CHANGE_ID" -e DEPENDENCIES="$DEPENDENCIES" -e NODE_ENV=test plone/volto-addon-ci cypress'''
|
|
129
129
|
} finally {
|
|
130
130
|
try {
|
|
@@ -142,7 +142,8 @@ pipeline {
|
|
|
142
142
|
reportName: 'CypressCoverage',
|
|
143
143
|
reportTitles: 'Integration Tests Code Coverage'])
|
|
144
144
|
}
|
|
145
|
-
|
|
145
|
+
sh '''touch empty_file; for ok_test in $(grep -E 'file=.*failures="0"' $(grep 'testsuites .*failures="0"' $(find cypress-results -name *.xml) empty_file | awk -F: '{print $1}') empty_file | sed 's/.* file="\\(.*\\)" time.*/\\1/' | sed 's#^cypress/integration/##g' | sed 's#^../../../node_modules/@eeacms/##g'); do rm -f cypress-reports/videos/$ok_test.mp4; rm -f cypress-reports/$ok_test.mp4; done'''
|
|
146
|
+
archiveArtifacts artifacts: 'cypress-reports/**/*.mp4', fingerprint: true, allowEmptyArchive: true
|
|
146
147
|
stash name: "cypress-coverage", includes: "cypress-coverage/**", allowEmpty: true
|
|
147
148
|
}
|
|
148
149
|
finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eeacms/volto-arcgis-block",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
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,9 @@
|
|
|
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": "0.1.1",
|
|
44
|
+
"highcharts": "^9.3.2",
|
|
45
|
+
"highcharts-react-official": "^3.1.0"
|
|
44
46
|
},
|
|
45
47
|
"devDependencies": {
|
|
46
48
|
"@cypress/code-coverage": "^3.9.5",
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import React, { createRef } from 'react';
|
|
2
|
-
//import "@arcgis/core/assets/esri/css/main.css";
|
|
3
|
-
//import "./css/ArcgisMap.css";
|
|
4
|
-
//import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
5
2
|
import { loadModules } from 'esri-loader';
|
|
6
3
|
|
|
7
4
|
var Graphic,
|
|
@@ -174,7 +171,10 @@ class AreaWidget extends React.Component {
|
|
|
174
171
|
}),
|
|
175
172
|
symbol: fillSymbol,
|
|
176
173
|
});
|
|
177
|
-
this.props.updateArea({
|
|
174
|
+
this.props.updateArea({
|
|
175
|
+
origin: { x: origin.longitude, y: origin.latitude },
|
|
176
|
+
end: { x: p.longitude, y: p.latitude },
|
|
177
|
+
});
|
|
178
178
|
this.props.view.graphics.add(extentGraphic);
|
|
179
179
|
}
|
|
180
180
|
});
|
|
@@ -202,7 +202,14 @@ class AreaWidget extends React.Component {
|
|
|
202
202
|
});
|
|
203
203
|
this.props.map.add(this.nutsGroupLayer);
|
|
204
204
|
this.props.view.on('click', (event) => {
|
|
205
|
-
if (
|
|
205
|
+
if (
|
|
206
|
+
(this.props.mapViewer.activeWidget === this || this.props.download) &&
|
|
207
|
+
(this.props.mapViewer.activeWidget
|
|
208
|
+
? !this.props.mapViewer.activeWidget.container.current.classList.contains(
|
|
209
|
+
'info-container',
|
|
210
|
+
)
|
|
211
|
+
: true)
|
|
212
|
+
) {
|
|
206
213
|
this.props.view.hitTest(event).then((response) => {
|
|
207
214
|
if (response.results.length > 0) {
|
|
208
215
|
let graphic = response.results.filter((result) => {
|
|
@@ -15,7 +15,7 @@ class BasemapWidget extends React.Component {
|
|
|
15
15
|
//not be showing the basemap panel
|
|
16
16
|
this.state = { showMapMenu: false };
|
|
17
17
|
this.menuClass =
|
|
18
|
-
'esri-icon-basemap esri-widget--button esri-widget esri-interactive
|
|
18
|
+
'esri-icon-basemap esri-widget--button esri-widget esri-interactive';
|
|
19
19
|
this.loadFirst = true;
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import React, { createRef } from 'react';
|
|
2
|
+
import { loadModules } from 'esri-loader';
|
|
3
|
+
import Highcharts from 'highcharts';
|
|
4
|
+
import HighchartsReact from 'highcharts-react-official';
|
|
5
|
+
var GeometryEngine, Graphic;
|
|
6
|
+
|
|
7
|
+
class InfoWidget extends React.Component {
|
|
8
|
+
/**
|
|
9
|
+
* Creator of the InfoWidget widget class
|
|
10
|
+
* @param {*} props
|
|
11
|
+
*/
|
|
12
|
+
constructor(props) {
|
|
13
|
+
super(props);
|
|
14
|
+
//We create a reference to a DOM element to be mounted
|
|
15
|
+
this.container = createRef();
|
|
16
|
+
//Initially, we set the state of the component to
|
|
17
|
+
//not be showing the basemap panel
|
|
18
|
+
this.state = { showMapMenu: false, timeLayers: {} };
|
|
19
|
+
this.map = this.props.map;
|
|
20
|
+
this.menuClass =
|
|
21
|
+
'esri-icon-description esri-widget--button esri-widget esri-interactive';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
loader() {
|
|
25
|
+
return loadModules(['esri/geometry/geometryEngine', 'esri/Graphic']).then(
|
|
26
|
+
([_GeometryEngine, _Graphic]) => {
|
|
27
|
+
[GeometryEngine, Graphic] = [_GeometryEngine, _Graphic];
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Method that will be invoked when the
|
|
34
|
+
* button is clicked. It controls the open
|
|
35
|
+
* and close actions of the component
|
|
36
|
+
*/
|
|
37
|
+
openMenu() {
|
|
38
|
+
if (this.state.showMapMenu) {
|
|
39
|
+
this.props.mapViewer.setActiveWidget();
|
|
40
|
+
this.container.current.querySelector('.info-panel').style.display =
|
|
41
|
+
'none';
|
|
42
|
+
this.container.current
|
|
43
|
+
.querySelector('.esri-widget--button')
|
|
44
|
+
.classList.replace('esri-icon-right-arrow', 'esri-icon-description');
|
|
45
|
+
// By invoking the setState, we notify the state we want to reach
|
|
46
|
+
// and ensure that the component is rendered again
|
|
47
|
+
this.setState({ showMapMenu: false, pixelInfo: false, timeLayers: {} });
|
|
48
|
+
this.props.view.popup.autoOpenEnabled = true;
|
|
49
|
+
this.removeMarker();
|
|
50
|
+
} else {
|
|
51
|
+
this.props.mapViewer.setActiveWidget(this);
|
|
52
|
+
this.container.current
|
|
53
|
+
.querySelector('.esri-widget--button')
|
|
54
|
+
.classList.replace('esri-icon-description', 'esri-icon-right-arrow');
|
|
55
|
+
this.container.current.querySelector('.info-panel').style.display =
|
|
56
|
+
'block';
|
|
57
|
+
// By invoking the setState, we notify the state we want to reach
|
|
58
|
+
// and ensure that the component is rendered again
|
|
59
|
+
this.setState({ showMapMenu: true });
|
|
60
|
+
this.props.mapViewer.view.popup.close();
|
|
61
|
+
this.props.view.popup.autoOpenEnabled = false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* This method is executed after the rener method is executed
|
|
66
|
+
*/
|
|
67
|
+
async componentDidMount() {
|
|
68
|
+
await this.loader();
|
|
69
|
+
this.props.view.ui.add(this.container.current, 'top-right');
|
|
70
|
+
this.props.view.on('click', (e) => {
|
|
71
|
+
if (this.props.mapViewer.activeWidget === this) {
|
|
72
|
+
let option = document.querySelector('#info_layer').value;
|
|
73
|
+
let selected = this.props.activeLayers[
|
|
74
|
+
this.props.activeLayers.findIndex((a) => a.name === option)
|
|
75
|
+
];
|
|
76
|
+
let layer = selected.layer;
|
|
77
|
+
let title = selected.title;
|
|
78
|
+
let name = selected.name;
|
|
79
|
+
this.identify(layer, e).then((response) => {
|
|
80
|
+
let variables = response.variables.options;
|
|
81
|
+
let variable = response.variables.selected;
|
|
82
|
+
if (
|
|
83
|
+
this.state.variables
|
|
84
|
+
? variables.includes(this.state.variables.selected)
|
|
85
|
+
: false
|
|
86
|
+
) {
|
|
87
|
+
variable = this.state.variables.selected;
|
|
88
|
+
}
|
|
89
|
+
let data = {
|
|
90
|
+
x: response.timeFields.values
|
|
91
|
+
.map((a) => {
|
|
92
|
+
return a[response.timeFields.start];
|
|
93
|
+
})
|
|
94
|
+
.sort((a, b) => {
|
|
95
|
+
return new Date(a).getTime() - new Date(b).getTime();
|
|
96
|
+
}),
|
|
97
|
+
y: response.data.values.map((a) => {
|
|
98
|
+
return Math.round(a[variable] * 100) / 100;
|
|
99
|
+
}),
|
|
100
|
+
};
|
|
101
|
+
let chartData = this.createChart(title, variable, data);
|
|
102
|
+
this.addMarker(e);
|
|
103
|
+
this.setState({
|
|
104
|
+
pixelInfo: true,
|
|
105
|
+
data: response,
|
|
106
|
+
options: chartData,
|
|
107
|
+
variables: { options: variables, selected: variable },
|
|
108
|
+
timeLayers: { selected: name },
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
addMarker(evt) {
|
|
116
|
+
this.removeMarker();
|
|
117
|
+
let lat = evt.mapPoint.latitude;
|
|
118
|
+
let long = evt.mapPoint.longitude;
|
|
119
|
+
var point = {
|
|
120
|
+
type: 'point',
|
|
121
|
+
longitude: long,
|
|
122
|
+
latitude: lat,
|
|
123
|
+
};
|
|
124
|
+
var markerSymbol = {
|
|
125
|
+
type: 'simple-marker',
|
|
126
|
+
color: [255, 255, 255, 0.75],
|
|
127
|
+
outline: {
|
|
128
|
+
color: 'black',
|
|
129
|
+
width: '2px',
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
var pointGraphic = new Graphic({
|
|
133
|
+
geometry: point,
|
|
134
|
+
symbol: markerSymbol,
|
|
135
|
+
attributes: { long, lat, id: 'pixel-info' },
|
|
136
|
+
});
|
|
137
|
+
this.props.view.graphics.add(pointGraphic);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
removeMarker() {
|
|
141
|
+
if (
|
|
142
|
+
this.props.view.graphics.items.find((a) => {
|
|
143
|
+
return a.attributes ? a.attributes.id === 'pixel-info' : false;
|
|
144
|
+
})
|
|
145
|
+
) {
|
|
146
|
+
let marker = this.props.view.graphics.items.find((a) => {
|
|
147
|
+
return a.attributes && a.attributes.id === 'pixel-info';
|
|
148
|
+
});
|
|
149
|
+
this.props.view.graphics.remove(marker);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
identify(layer, evt) {
|
|
154
|
+
let values = { timeFields: {}, data: {}, variables: {} };
|
|
155
|
+
//Complete time data
|
|
156
|
+
values.timeFields['start'] = layer.timeInfo.startField;
|
|
157
|
+
values.timeFields['end'] = layer.timeInfo.endField;
|
|
158
|
+
let timeQuery = layer.createQuery();
|
|
159
|
+
timeQuery.outFields = [layer.timeInfo.startField];
|
|
160
|
+
let fields = layer.fields
|
|
161
|
+
.filter((a) => {
|
|
162
|
+
return (
|
|
163
|
+
a.type !== 'date' &&
|
|
164
|
+
a.type !== 'geometry' &&
|
|
165
|
+
a.type !== 'string' &&
|
|
166
|
+
a.type !== 'oid'
|
|
167
|
+
);
|
|
168
|
+
})
|
|
169
|
+
.map((b) => {
|
|
170
|
+
return b.name;
|
|
171
|
+
});
|
|
172
|
+
let field = layer.displayField in fields ? layer.displayField : fields[0];
|
|
173
|
+
values.variables = { options: fields, selected: field };
|
|
174
|
+
if (layer.timeInfo.fullTimeExtent.endField) {
|
|
175
|
+
timeQuery.outFields.push(layer.timeInfo.endField);
|
|
176
|
+
}
|
|
177
|
+
timeQuery.returnDistinctValues = true;
|
|
178
|
+
timeQuery.returnGeometry = false;
|
|
179
|
+
let p1 = layer.queryFeatures(timeQuery).then((r) => {
|
|
180
|
+
let timevals = [];
|
|
181
|
+
r.features.forEach((e) => timevals.push(e.attributes));
|
|
182
|
+
values.timeFields['values'] = timevals;
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
//Query for data
|
|
186
|
+
let query = layer.createQuery();
|
|
187
|
+
query.geometry = this.props.view.toMap(evt); // the point location of the pointer
|
|
188
|
+
query.distance = 1000;
|
|
189
|
+
query.units = 'meters';
|
|
190
|
+
query.spatialRelationship = 'intersects'; // this is the default
|
|
191
|
+
query.returnGeometry = true;
|
|
192
|
+
query.outFields = [fields.toString()]; // Information to be returned
|
|
193
|
+
values.data['outFields'] = [fields.toString()];
|
|
194
|
+
|
|
195
|
+
let p2 = layer.queryFeatures(query).then((response) => {
|
|
196
|
+
//First of all, we get all the points with its distance to click point
|
|
197
|
+
let points = response.features.map((e) => {
|
|
198
|
+
return {
|
|
199
|
+
latitude: e.geometry.latitude,
|
|
200
|
+
longitude: e.geometry.longitude,
|
|
201
|
+
distance: GeometryEngine.distance(
|
|
202
|
+
this.props.view.toMap(evt),
|
|
203
|
+
e.geometry,
|
|
204
|
+
'meters',
|
|
205
|
+
),
|
|
206
|
+
};
|
|
207
|
+
});
|
|
208
|
+
let min_distance = Math.min.apply(
|
|
209
|
+
null,
|
|
210
|
+
points.map((e) => e.distance),
|
|
211
|
+
); //minimum distance
|
|
212
|
+
let point = points.find((e) => e.distance <= min_distance); //minimum distance point
|
|
213
|
+
//get de info of the minimum distance point at all the times possible
|
|
214
|
+
let info = response.features.filter(
|
|
215
|
+
(e) =>
|
|
216
|
+
e.geometry.latitude === point.latitude &&
|
|
217
|
+
e.geometry.longitude === point.longitude,
|
|
218
|
+
);
|
|
219
|
+
values.data['values'] = info.map((e) => {
|
|
220
|
+
return e.attributes;
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return Promise.all([p1, p2]).then(() => {
|
|
225
|
+
return values;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
createChart(title, variable, chartData) {
|
|
230
|
+
let chartOptions = {
|
|
231
|
+
chart: {
|
|
232
|
+
height: 208,
|
|
233
|
+
},
|
|
234
|
+
title: {
|
|
235
|
+
text: title,
|
|
236
|
+
style: {},
|
|
237
|
+
},
|
|
238
|
+
subtitle: {
|
|
239
|
+
text: '',
|
|
240
|
+
style: {
|
|
241
|
+
display: 'none',
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
plotOptions: {
|
|
245
|
+
line: {
|
|
246
|
+
pointPlacement: 'between',
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
xAxis: {
|
|
250
|
+
title: {
|
|
251
|
+
text: null,
|
|
252
|
+
},
|
|
253
|
+
labels: {
|
|
254
|
+
format: '{value:%d/%m/%Y}',
|
|
255
|
+
},
|
|
256
|
+
type: 'datetime',
|
|
257
|
+
categories: chartData.x,
|
|
258
|
+
tickmarkPlacement: 'between',
|
|
259
|
+
startOnTick: true,
|
|
260
|
+
endOnTick: true,
|
|
261
|
+
},
|
|
262
|
+
legend: {
|
|
263
|
+
enabled: false,
|
|
264
|
+
},
|
|
265
|
+
tooltip: {
|
|
266
|
+
shared: true,
|
|
267
|
+
useHTML: true,
|
|
268
|
+
headerFormat: '{point.x:%d/%m/%Y}<br><b>{point.point.symbolName}</b>',
|
|
269
|
+
},
|
|
270
|
+
series: [
|
|
271
|
+
{
|
|
272
|
+
marker: {
|
|
273
|
+
enabled: false,
|
|
274
|
+
},
|
|
275
|
+
name: variable,
|
|
276
|
+
data: chartData.y,
|
|
277
|
+
shadow: false,
|
|
278
|
+
zIndex: 1,
|
|
279
|
+
color: '#a0b128',
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
exporting: {
|
|
283
|
+
buttons: {
|
|
284
|
+
contextButton: {
|
|
285
|
+
enabled: false,
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
credits: {
|
|
290
|
+
enabled: false,
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
return chartOptions;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
getActiveLayers() {
|
|
297
|
+
let layers = {};
|
|
298
|
+
if (!this.state.timeLayers.hasOwnProperty('selected')) {
|
|
299
|
+
layers.selected = this.props.activeLayers
|
|
300
|
+
.map((a) => {
|
|
301
|
+
return a.name;
|
|
302
|
+
})
|
|
303
|
+
.reverse()[0];
|
|
304
|
+
layers.layer = this.props.activeLayers[
|
|
305
|
+
this.props.activeLayers.findIndex((a) => a.name === layers.selected)
|
|
306
|
+
].layer;
|
|
307
|
+
} else if (
|
|
308
|
+
!this.props.activeLayers
|
|
309
|
+
.map((a) => {
|
|
310
|
+
return a.name;
|
|
311
|
+
})
|
|
312
|
+
.includes(this.state.timeLayers.selected)
|
|
313
|
+
) {
|
|
314
|
+
layers.selected = this.props.activeLayers
|
|
315
|
+
.map((a) => {
|
|
316
|
+
return a.name;
|
|
317
|
+
})
|
|
318
|
+
.reverse()[0];
|
|
319
|
+
layers.layer = this.props.activeLayers[
|
|
320
|
+
this.props.activeLayers.findIndex((a) => a.name === layers.selected)
|
|
321
|
+
].layer;
|
|
322
|
+
}
|
|
323
|
+
this.setState({
|
|
324
|
+
timeLayers: layers,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
selectLayer(option) {
|
|
329
|
+
let selected = this.props.activeLayers[
|
|
330
|
+
this.props.activeLayers.findIndex((a) => a.name === option)
|
|
331
|
+
];
|
|
332
|
+
let names = this.state.timeLayers.names;
|
|
333
|
+
let name = option;
|
|
334
|
+
let layer = selected.layer;
|
|
335
|
+
this.removeMarker();
|
|
336
|
+
this.setState({
|
|
337
|
+
timeLayers: { names: names, selected: name, layer: layer },
|
|
338
|
+
pixelInfo: false,
|
|
339
|
+
data: {},
|
|
340
|
+
variables: {},
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
selectVariable(option) {
|
|
345
|
+
let variables = this.state.variables.options;
|
|
346
|
+
let variable = option;
|
|
347
|
+
let selected = this.props.activeLayers[
|
|
348
|
+
this.props.activeLayers.findIndex(
|
|
349
|
+
(a) => a.name === this.state.timeLayers.selected,
|
|
350
|
+
)
|
|
351
|
+
];
|
|
352
|
+
let title = selected.title;
|
|
353
|
+
let data = {
|
|
354
|
+
x: this.state.data.timeFields.values
|
|
355
|
+
.map((a) => {
|
|
356
|
+
return a[this.state.data.timeFields.start];
|
|
357
|
+
})
|
|
358
|
+
.sort((a, b) => {
|
|
359
|
+
return new Date(a).getTime() - new Date(b).getTime();
|
|
360
|
+
}),
|
|
361
|
+
y: this.state.data.data.values.map((a) => {
|
|
362
|
+
return Math.round(a[variable] * 100) / 100;
|
|
363
|
+
}),
|
|
364
|
+
};
|
|
365
|
+
let chartData = this.createChart(title, variable, data);
|
|
366
|
+
this.setState({
|
|
367
|
+
options: chartData,
|
|
368
|
+
variables: { options: variables, selected: variable },
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* This method renders the component
|
|
374
|
+
* @returns jsx
|
|
375
|
+
*/
|
|
376
|
+
render() {
|
|
377
|
+
let isEmpty = true;
|
|
378
|
+
let pixelInfo = true;
|
|
379
|
+
if (this.state.pixelInfo && this.state.data) {
|
|
380
|
+
isEmpty =
|
|
381
|
+
this.state.data.data.values.map((a) => {
|
|
382
|
+
return a[this.state.variables.selected];
|
|
383
|
+
}).length === 0;
|
|
384
|
+
}
|
|
385
|
+
if (
|
|
386
|
+
(this.props.activeLayers &&
|
|
387
|
+
!this.props.activeLayers
|
|
388
|
+
.map((a) => {
|
|
389
|
+
return a.name;
|
|
390
|
+
})
|
|
391
|
+
.includes(this.state.timeLayers.selected)) ||
|
|
392
|
+
(this.props.activeLayers &&
|
|
393
|
+
document.querySelector('#info_layer').value !==
|
|
394
|
+
this.state.timeLayers.selected)
|
|
395
|
+
) {
|
|
396
|
+
pixelInfo = false;
|
|
397
|
+
this.removeMarker();
|
|
398
|
+
}
|
|
399
|
+
return (
|
|
400
|
+
<>
|
|
401
|
+
<div ref={this.container} className="info-container">
|
|
402
|
+
<div
|
|
403
|
+
className={this.menuClass}
|
|
404
|
+
id="info_button"
|
|
405
|
+
title="Info"
|
|
406
|
+
onClick={this.openMenu.bind(this)}
|
|
407
|
+
onKeyDown={this.openMenu.bind(this)}
|
|
408
|
+
tabIndex="0"
|
|
409
|
+
role="button"
|
|
410
|
+
></div>
|
|
411
|
+
<div className="info-panel">
|
|
412
|
+
{this.state.showMapMenu && (
|
|
413
|
+
<label>
|
|
414
|
+
Layer
|
|
415
|
+
<select
|
|
416
|
+
className="esri-select"
|
|
417
|
+
id="info_layer"
|
|
418
|
+
value={this.state.timeLayers.selected}
|
|
419
|
+
onBlur={(e) => e.preventDefault()}
|
|
420
|
+
onChange={(e) => this.selectLayer(e.target.value)}
|
|
421
|
+
>
|
|
422
|
+
{this.props.activeLayers.map((option) => (
|
|
423
|
+
<option key={option.name} value={option.name}>
|
|
424
|
+
{option.title}
|
|
425
|
+
</option>
|
|
426
|
+
))}
|
|
427
|
+
</select>
|
|
428
|
+
</label>
|
|
429
|
+
)}
|
|
430
|
+
{this.state.pixelInfo && pixelInfo ? (
|
|
431
|
+
<>
|
|
432
|
+
<label>
|
|
433
|
+
Variable
|
|
434
|
+
<select
|
|
435
|
+
className="esri-select"
|
|
436
|
+
id="info_variable"
|
|
437
|
+
value={this.state.variables.selected}
|
|
438
|
+
onBlur={(e) => e.preventDefault()}
|
|
439
|
+
onChange={(e) => this.selectVariable(e.target.value)}
|
|
440
|
+
>
|
|
441
|
+
{this.state.variables.options.map((option) => (
|
|
442
|
+
<option key={option} value={option}>
|
|
443
|
+
{option}
|
|
444
|
+
</option>
|
|
445
|
+
))}
|
|
446
|
+
</select>
|
|
447
|
+
</label>
|
|
448
|
+
{isEmpty ? (
|
|
449
|
+
<span className="info-panel-empty">No data</span>
|
|
450
|
+
) : (
|
|
451
|
+
<HighchartsReact
|
|
452
|
+
highcharts={Highcharts}
|
|
453
|
+
options={this.state.options}
|
|
454
|
+
/>
|
|
455
|
+
)}
|
|
456
|
+
</>
|
|
457
|
+
) : (
|
|
458
|
+
<span className="info-panel-empty">
|
|
459
|
+
Click on the map to get pixel info
|
|
460
|
+
</span>
|
|
461
|
+
)}
|
|
462
|
+
</div>
|
|
463
|
+
</div>
|
|
464
|
+
</>
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
export default InfoWidget;
|
|
@@ -8,10 +8,12 @@ import PrintWidget from './PrintWidget';
|
|
|
8
8
|
import AreaWidget from './AreaWidget';
|
|
9
9
|
import ScaleWidget from './ScaleWidget';
|
|
10
10
|
import LegendWidget from './LegendWidget';
|
|
11
|
+
import InfoWidget from './InfoWidget';
|
|
11
12
|
import MenuWidget from './MenuWidget';
|
|
12
13
|
import { MapViewerConfig } from '../../actions';
|
|
13
14
|
import { compose } from 'redux';
|
|
14
15
|
import { connect } from 'react-redux';
|
|
16
|
+
import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
|
|
15
17
|
|
|
16
18
|
//import "isomorphic-fetch"; <-- Necessary to use fetch?
|
|
17
19
|
var Map, MapView, Zoom;
|
|
@@ -44,6 +46,10 @@ class MapViewer extends React.Component {
|
|
|
44
46
|
this.mapViewer.setState({ area: shared_value });
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
updateActiveLayers(shared_value) {
|
|
50
|
+
this.mapViewer.setState({ activeLayers: shared_value });
|
|
51
|
+
}
|
|
52
|
+
|
|
47
53
|
loader() {
|
|
48
54
|
return loadModules([
|
|
49
55
|
'esri/WebMap',
|
|
@@ -87,7 +93,7 @@ class MapViewer extends React.Component {
|
|
|
87
93
|
// After launching the MapViewerConfig action
|
|
88
94
|
// we will have stored the json response here:
|
|
89
95
|
// this.props.mapviewer_config
|
|
90
|
-
this.props.MapViewerConfig(this.props.url);
|
|
96
|
+
this.props.MapViewerConfig(flattenToAppURL(this.props.url));
|
|
91
97
|
|
|
92
98
|
//Once we have created the MapView, we need to ensure that the map div
|
|
93
99
|
//is refreshed in order to show the map on it. To do so, we need to
|
|
@@ -156,6 +162,19 @@ class MapViewer extends React.Component {
|
|
|
156
162
|
if (this.view) return <ScaleWidget view={this.view} mapViewer={this} />;
|
|
157
163
|
}
|
|
158
164
|
|
|
165
|
+
renderInfo() {
|
|
166
|
+
if (this.view)
|
|
167
|
+
return (
|
|
168
|
+
<InfoWidget
|
|
169
|
+
view={this.view}
|
|
170
|
+
map={this.map}
|
|
171
|
+
mapViewer={this}
|
|
172
|
+
updateActiveLayers={this.updateActiveLayers}
|
|
173
|
+
activeLayers={this.state.activeLayers}
|
|
174
|
+
/>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
159
178
|
renderMenu() {
|
|
160
179
|
if (this.view)
|
|
161
180
|
return (
|
|
@@ -167,6 +186,7 @@ class MapViewer extends React.Component {
|
|
|
167
186
|
mapViewer={this}
|
|
168
187
|
updateArea={this.updateArea}
|
|
169
188
|
area={this.state.area}
|
|
189
|
+
updateActiveLayers={this.updateActiveLayers}
|
|
170
190
|
/>
|
|
171
191
|
); //call conf
|
|
172
192
|
}
|
|
@@ -188,6 +208,7 @@ class MapViewer extends React.Component {
|
|
|
188
208
|
{this.renderPrint()}
|
|
189
209
|
{this.renderArea()}
|
|
190
210
|
{this.renderScale()}
|
|
211
|
+
{this.renderInfo()}
|
|
191
212
|
{this.renderMenu()}
|
|
192
213
|
</div>
|
|
193
214
|
</div>
|