@sedoo/unidoo 0.1.16 → 0.1.17
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/dist/unidoo/unidoo.common.js +7 -2
- package/dist/unidoo/unidoo.common.js.map +1 -1
- package/dist/unidoo/unidoo.umd.js +7 -2
- package/dist/unidoo/unidoo.umd.js.map +1 -1
- package/dist/unidoo/unidoo.umd.min.js +7 -2
- package/dist/unidoo/unidoo.umd.min.js.map +1 -1
- package/package.json +3 -2
- package/src/components/UnidooAlert.vue +101 -37
- package/src/components/UnidooMapGeoFeatures.vue +640 -0
- package/src/plugin.js +214 -176
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
/* eslint-disable no-new */
|
|
2
|
+
<template>
|
|
3
|
+
<v-app>
|
|
4
|
+
<v-card height="564px">
|
|
5
|
+
<v-toolbar
|
|
6
|
+
flat
|
|
7
|
+
dark
|
|
8
|
+
:color="colorTitle"
|
|
9
|
+
>
|
|
10
|
+
<v-card-title>{{ title }}</v-card-title>
|
|
11
|
+
</v-toolbar>
|
|
12
|
+
<div ref="map-root" style="width: 100%; height: 100%"></div>
|
|
13
|
+
<!-- Popup -->
|
|
14
|
+
<div v-show="content_element" id="popup" class="ol-popup">
|
|
15
|
+
<a href="#" id="popup-closer" class="ol-popup-closer" @click.prevent="closePopup"></a>
|
|
16
|
+
<div id="popup-content"></div>
|
|
17
|
+
</div>
|
|
18
|
+
<!-- Snackbar (Toast Notification) -->
|
|
19
|
+
<v-snackbar v-model="showToast" :bottom="true" :timeout="10000" :color="colorFeature">
|
|
20
|
+
Click on a feature drawn to view more details!
|
|
21
|
+
<v-btn color="pink" text @click="showToast = false">
|
|
22
|
+
Close
|
|
23
|
+
</v-btn>
|
|
24
|
+
</v-snackbar>
|
|
25
|
+
</v-card>
|
|
26
|
+
</v-app>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<script>
|
|
30
|
+
import View from 'ol/View'
|
|
31
|
+
import Map from 'ol/Map'
|
|
32
|
+
import TileLayer from 'ol/layer/Tile'
|
|
33
|
+
import OSM from 'ol/source/OSM'
|
|
34
|
+
import GeoJSON from 'ol/format/GeoJSON';
|
|
35
|
+
import VectorLayer from 'ol/layer/Vector'
|
|
36
|
+
import VectorSource from 'ol/source/Vector'
|
|
37
|
+
import {Fill, RegularShape, Stroke, Style} from 'ol/style';
|
|
38
|
+
// importing the OpenLayers stylesheet is required for having
|
|
39
|
+
// good looking buttons!
|
|
40
|
+
import 'ol/ol.css'
|
|
41
|
+
import {Select} from "ol/interaction";
|
|
42
|
+
import {click, pointerMove} from "ol/events/condition";
|
|
43
|
+
import {FullScreen} from "ol/control";
|
|
44
|
+
import {Overlay} from "ol";
|
|
45
|
+
import CircleStyle from "ol/style/Circle";
|
|
46
|
+
import {
|
|
47
|
+
DragRotateAndZoom,
|
|
48
|
+
defaults as defaultInteractions,
|
|
49
|
+
} from 'ol/interaction.js';
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
export default {
|
|
53
|
+
name: 'MapContainer',
|
|
54
|
+
components: {},
|
|
55
|
+
data() {
|
|
56
|
+
return {
|
|
57
|
+
map: null,
|
|
58
|
+
vectorLayer: null,
|
|
59
|
+
vectorSource: null,
|
|
60
|
+
openStyle: null,
|
|
61
|
+
selectedStyle: null,
|
|
62
|
+
loadingStations: false,
|
|
63
|
+
selectInteraction: null,
|
|
64
|
+
features: {},
|
|
65
|
+
feature: null,
|
|
66
|
+
popupOverlay: null,
|
|
67
|
+
featureProperties: {},
|
|
68
|
+
geoJsonObject: null,
|
|
69
|
+
content_element: null,
|
|
70
|
+
showToast: false,
|
|
71
|
+
bannedKeysArray: [],
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
props: {
|
|
75
|
+
title: {
|
|
76
|
+
type: String,
|
|
77
|
+
default: 'title',
|
|
78
|
+
},
|
|
79
|
+
colorTitle: {
|
|
80
|
+
type: String,
|
|
81
|
+
default: '#3f4a75',
|
|
82
|
+
},
|
|
83
|
+
colorFeature: {
|
|
84
|
+
type: String,
|
|
85
|
+
default: 'green',
|
|
86
|
+
},
|
|
87
|
+
colorOfSelectedFeature: {
|
|
88
|
+
type: String,
|
|
89
|
+
default: 'blue',
|
|
90
|
+
},
|
|
91
|
+
serviceUrl: {
|
|
92
|
+
type: String,
|
|
93
|
+
default: '',
|
|
94
|
+
},
|
|
95
|
+
bannedKeys: {
|
|
96
|
+
type: String,
|
|
97
|
+
default: () => {
|
|
98
|
+
return 'geometry,origin,interval,source,technicalId,externalLinkPrefix,externalLink,UTC_time,altitude';
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
shapeStylePoint: {
|
|
102
|
+
type: String,
|
|
103
|
+
default: 'circle',
|
|
104
|
+
},
|
|
105
|
+
disablePopup: {
|
|
106
|
+
type: String,
|
|
107
|
+
default: 'false',
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
created() {
|
|
111
|
+
// Check if user has visited before
|
|
112
|
+
if (typeof localStorage !== 'undefined') {
|
|
113
|
+
// Access and use localStorage here
|
|
114
|
+
if (!localStorage.getItem('visitedBefore') && this.disablePopup.toLowerCase() !== 'true') {
|
|
115
|
+
this.showToast = true;
|
|
116
|
+
localStorage.setItem('visitedBefore', 'true'); // Set flag indicating user has visited
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
console.log('localStorage is not available.');
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
mounted() {
|
|
123
|
+
this.initMap();
|
|
124
|
+
if (!this.disablePopup !== 'true') {
|
|
125
|
+
console.log('axios.then : initPopup()');
|
|
126
|
+
this.initPopup();
|
|
127
|
+
}
|
|
128
|
+
this.loadFeatures();
|
|
129
|
+
},
|
|
130
|
+
watch: {
|
|
131
|
+
serviceUrl: {
|
|
132
|
+
immediate: true, // also call the handler right after component initialization
|
|
133
|
+
handler(newVal, oldVal) {
|
|
134
|
+
if (newVal !== oldVal) {
|
|
135
|
+
console.log(`Service URL changed from ${oldVal} to ${newVal}`);
|
|
136
|
+
this.loadFeatures();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
methods: {
|
|
142
|
+
loadFeatures() {
|
|
143
|
+
|
|
144
|
+
// Close the popup at the start of loading new features.
|
|
145
|
+
this.closePopup();
|
|
146
|
+
|
|
147
|
+
this.axios.get(this.serviceUrl)
|
|
148
|
+
.then(res => {
|
|
149
|
+
const geoJsonObject = res.data
|
|
150
|
+
geoJsonObject.crs = {
|
|
151
|
+
type: 'name',
|
|
152
|
+
properties: {
|
|
153
|
+
name: 'EPSG:3857',
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
this.geoJsonObject = geoJsonObject;
|
|
157
|
+
this.updateMapFeatures(geoJsonObject);
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
})
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
initMap() {
|
|
164
|
+
|
|
165
|
+
//Initialisation de la Map
|
|
166
|
+
|
|
167
|
+
console.log('initMap()');
|
|
168
|
+
|
|
169
|
+
const styles = {
|
|
170
|
+
|
|
171
|
+
LineString: new Style({
|
|
172
|
+
stroke: new Stroke({
|
|
173
|
+
color: this.colorFeature,
|
|
174
|
+
width: 2,
|
|
175
|
+
}),
|
|
176
|
+
}),
|
|
177
|
+
/* For SAFIRE */
|
|
178
|
+
MultiLineString: new Style({
|
|
179
|
+
stroke: new Stroke({
|
|
180
|
+
color: this.colorFeature,
|
|
181
|
+
width: 1,
|
|
182
|
+
}),
|
|
183
|
+
}),
|
|
184
|
+
Polygon: new Style({
|
|
185
|
+
stroke: new Stroke({
|
|
186
|
+
color: this.colorFeature,
|
|
187
|
+
width: 3,
|
|
188
|
+
}),
|
|
189
|
+
fill: new Fill({
|
|
190
|
+
color: 'rgba(0, 0, 255, 0.1)',
|
|
191
|
+
}),
|
|
192
|
+
}),
|
|
193
|
+
Point: this.getPointStyle(false), // setting Point style dynamically when it is not selected (false)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const selectedStyle = {
|
|
197
|
+
|
|
198
|
+
LineString: new Style({
|
|
199
|
+
stroke: new Stroke({
|
|
200
|
+
color: this.colorOfSelectedFeature,
|
|
201
|
+
width: 2,
|
|
202
|
+
}),
|
|
203
|
+
}),
|
|
204
|
+
/* For SAFIRE */
|
|
205
|
+
MultiLineString: new Style({
|
|
206
|
+
stroke: new Stroke({
|
|
207
|
+
color: this.colorOfSelectedFeature,
|
|
208
|
+
width: 1,
|
|
209
|
+
}),
|
|
210
|
+
}),
|
|
211
|
+
Polygon: new Style({
|
|
212
|
+
stroke: new Stroke({
|
|
213
|
+
color: this.colorOfSelectedFeature,
|
|
214
|
+
width: 3,
|
|
215
|
+
}),
|
|
216
|
+
fill: new Fill({
|
|
217
|
+
color: 'rgba(0, 0, 255, 0.1)',
|
|
218
|
+
}),
|
|
219
|
+
}),
|
|
220
|
+
Point: this.getPointStyle(true), // setting Point style dynamically when it is selected (true)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
this.selectedStyle = selectedStyle;
|
|
224
|
+
|
|
225
|
+
const styleFunction = function (feature) {
|
|
226
|
+
return styles[feature.getGeometry().getType()];
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// Initialization of vectorSource should be empty at first.
|
|
230
|
+
this.vectorSource = new VectorSource({wrapX: false});
|
|
231
|
+
|
|
232
|
+
const vectorLayer = new VectorLayer({
|
|
233
|
+
source: this.vectorSource,
|
|
234
|
+
style: styleFunction,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// this is where we create the OpenLayers map
|
|
238
|
+
// eslint-disable-next-line no-new
|
|
239
|
+
this.map = new Map({
|
|
240
|
+
interactions: defaultInteractions().extend([new DragRotateAndZoom()]),
|
|
241
|
+
// the map will be created using the 'map-root' ref
|
|
242
|
+
target: this.$refs['map-root'],
|
|
243
|
+
layers: [
|
|
244
|
+
// adding a background tiled layer
|
|
245
|
+
new TileLayer({
|
|
246
|
+
source: new OSM() // tiles are served by OpenStreetMap
|
|
247
|
+
}),
|
|
248
|
+
vectorLayer
|
|
249
|
+
],
|
|
250
|
+
//overlays: [this.popupOverlay],
|
|
251
|
+
// the map view will initially show the whole world
|
|
252
|
+
view: new View({
|
|
253
|
+
zoom: 0,
|
|
254
|
+
center: [0, 0],
|
|
255
|
+
constrainResolution: true,
|
|
256
|
+
projection: 'EPSG:3857'
|
|
257
|
+
}),
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
if (this.vectorSource.getFeatures().length > 0) {
|
|
261
|
+
this.map.getView().fit(this.vectorSource.getExtent());
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
let fullscreen = new FullScreen();
|
|
265
|
+
this.map.addControl(fullscreen);
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
//add select interaction to change the color of a selected feature
|
|
269
|
+
this.selectInteraction = new Select({
|
|
270
|
+
condition: click,
|
|
271
|
+
style: this.selectedStyleFunction,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
this.map.addInteraction(this.selectInteraction);
|
|
275
|
+
|
|
276
|
+
//add hover interaction to show that a feature is selectable (change the color style)
|
|
277
|
+
this.hoverInteraction = new Select({
|
|
278
|
+
condition: pointerMove,
|
|
279
|
+
style: this.selectedStyleFunction,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
this.map.addInteraction(this.hoverInteraction);
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
async updateMapFeatures(geoJsonObject) {
|
|
288
|
+
// Clear existing features.
|
|
289
|
+
this.vectorSource.clear();
|
|
290
|
+
|
|
291
|
+
// Convert GeoJSON features and add them to the source.
|
|
292
|
+
let geoJson = new GeoJSON({
|
|
293
|
+
dataProjection: 'EPSG:4326',
|
|
294
|
+
featureProjection: 'EPSG:3857'
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
let features = geoJsonObject.features;
|
|
298
|
+
if (features) {
|
|
299
|
+
this.features = features;
|
|
300
|
+
for (const element of features) {
|
|
301
|
+
this.vectorSource.addFeature(geoJson.readFeature(element));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// The $nextTick method is used to wait for Vue’s next update cycle to perform operations after the DOM has been updated. By using 'await', we ensure that the code following this line does not execute until the DOM updates are completed.
|
|
306
|
+
// This is particularly useful here to make sure that any features added to the vector source are rendered on the map before trying to adjust the view to fit them.
|
|
307
|
+
// Thus, we prevent potential issues where the view adjustment might occur before the features are fully rendered, which could result in an incorrect view or visual bugs.
|
|
308
|
+
await this.$nextTick();
|
|
309
|
+
|
|
310
|
+
// Log the extent for debugging:
|
|
311
|
+
console.log(this.vectorSource.getExtent());
|
|
312
|
+
|
|
313
|
+
// Check if there are features to fit to:
|
|
314
|
+
if (this.vectorSource.getFeatures().length > 0) {
|
|
315
|
+
// Ensure the view is adjusted to show the new features.
|
|
316
|
+
this.map.getView().fit(this.vectorSource.getExtent());
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
selectedStyleFunction(feature) {
|
|
323
|
+
|
|
324
|
+
return this.selectedStyle[feature.getGeometry().getType()];
|
|
325
|
+
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
getPointStyle(isSelectedColor) {
|
|
329
|
+
let image;
|
|
330
|
+
let color = this.colorFeature;
|
|
331
|
+
let fillColor = 'rgba(0, 0, 255, 0.1)';
|
|
332
|
+
if (isSelectedColor) {
|
|
333
|
+
color = this.colorOfSelectedFeature;
|
|
334
|
+
fillColor = this.colorOfSelectedFeature;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (this.shapeStylePoint === 'circle') {
|
|
338
|
+
image = new CircleStyle({
|
|
339
|
+
radius: 5,
|
|
340
|
+
fill: new Fill({
|
|
341
|
+
color: fillColor,
|
|
342
|
+
}),
|
|
343
|
+
stroke: new Stroke({
|
|
344
|
+
color: color,
|
|
345
|
+
width: 1,
|
|
346
|
+
}),
|
|
347
|
+
});
|
|
348
|
+
} else if (this.shapeStylePoint === 'triangle') {
|
|
349
|
+
image = new RegularShape({
|
|
350
|
+
fill: new Fill({
|
|
351
|
+
color: fillColor,
|
|
352
|
+
}),
|
|
353
|
+
stroke: new Stroke({
|
|
354
|
+
color: color,
|
|
355
|
+
width: 1,
|
|
356
|
+
}),
|
|
357
|
+
points: 3,
|
|
358
|
+
radius: 10,
|
|
359
|
+
angle: 0,
|
|
360
|
+
});
|
|
361
|
+
} else if (this.shapeStylePoint === 'square') {
|
|
362
|
+
image = new RegularShape({
|
|
363
|
+
fill: new Fill({
|
|
364
|
+
color: fillColor,
|
|
365
|
+
}),
|
|
366
|
+
stroke: new Stroke({
|
|
367
|
+
color: color,
|
|
368
|
+
width: 1,
|
|
369
|
+
}),
|
|
370
|
+
points: 4,
|
|
371
|
+
radius: 10,
|
|
372
|
+
angle: Math.PI / 4,
|
|
373
|
+
});
|
|
374
|
+
} else if (this.shapeStylePoint === 'star') {
|
|
375
|
+
image = new RegularShape({
|
|
376
|
+
fill: new Fill({
|
|
377
|
+
color: fillColor,
|
|
378
|
+
}),
|
|
379
|
+
stroke: new Stroke({
|
|
380
|
+
color: color,
|
|
381
|
+
width: 1,
|
|
382
|
+
}),
|
|
383
|
+
points: 5,
|
|
384
|
+
radius: 10,
|
|
385
|
+
radius2: 4,
|
|
386
|
+
angle: 0,
|
|
387
|
+
});
|
|
388
|
+
} else if (this.shapeStylePoint === 'cross') {
|
|
389
|
+
image = new RegularShape({
|
|
390
|
+
fill: new Fill({
|
|
391
|
+
color: fillColor,
|
|
392
|
+
}),
|
|
393
|
+
stroke: new Stroke({
|
|
394
|
+
color: color,
|
|
395
|
+
width: 2,
|
|
396
|
+
}),
|
|
397
|
+
points: 4,
|
|
398
|
+
radius: 10,
|
|
399
|
+
radius2: 0,
|
|
400
|
+
angle: 0,
|
|
401
|
+
});
|
|
402
|
+
} else if (this.shapeStylePoint === 'x') {
|
|
403
|
+
image = new RegularShape({
|
|
404
|
+
fill: new Fill({
|
|
405
|
+
color: fillColor,
|
|
406
|
+
}),
|
|
407
|
+
stroke: new Stroke({
|
|
408
|
+
color: color,
|
|
409
|
+
width: 2,
|
|
410
|
+
}),
|
|
411
|
+
points: 4,
|
|
412
|
+
radius: 10,
|
|
413
|
+
radius2: 0,
|
|
414
|
+
angle: Math.PI / 4,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return new Style({
|
|
419
|
+
image: image
|
|
420
|
+
});
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
initPopup() {
|
|
424
|
+
|
|
425
|
+
//Initialisation de la Popup
|
|
426
|
+
|
|
427
|
+
console.log('initPopup()');
|
|
428
|
+
|
|
429
|
+
//init Array of excluded properties
|
|
430
|
+
this.bannedKeysArray = this.bannedKeys.split(',');
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Elements that make up the popup.
|
|
434
|
+
*/
|
|
435
|
+
const container = document.getElementById('popup');
|
|
436
|
+
this.content_element = document.getElementById('popup-content');
|
|
437
|
+
const closer = document.getElementById('popup-closer');
|
|
438
|
+
|
|
439
|
+
//Create a popup as an overlay
|
|
440
|
+
this.popupOverlay = new Overlay({
|
|
441
|
+
element: container,
|
|
442
|
+
positioning: "top-center",
|
|
443
|
+
stopEvent: true,
|
|
444
|
+
autoPan: { //the map automatically pans to make the overlay (popup) entirely visible.
|
|
445
|
+
animation: {
|
|
446
|
+
duration: 250,
|
|
447
|
+
},
|
|
448
|
+
offset: [0, -10]
|
|
449
|
+
},
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
// Change the cursor when hovering over a feature in the vector layer
|
|
453
|
+
this.map.on("pointermove", (evt) => {
|
|
454
|
+
if (!evt.dragging) {
|
|
455
|
+
this.map.getTargetElement().style.cursor = this.map.hasFeatureAtPixel(
|
|
456
|
+
this.map.getEventPixel(evt.originalEvent)
|
|
457
|
+
)
|
|
458
|
+
? "pointer"
|
|
459
|
+
: "";
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
// Add a click handler to get the clicked feature
|
|
465
|
+
this.map.on("click", (evt) => {
|
|
466
|
+
let features = [];
|
|
467
|
+
this.map.forEachFeatureAtPixel(
|
|
468
|
+
evt.pixel,
|
|
469
|
+
(feature, layer) => {
|
|
470
|
+
features.push(feature);
|
|
471
|
+
}
|
|
472
|
+
);
|
|
473
|
+
if (features.length) {
|
|
474
|
+
this.createPopup(features, evt.coordinate);
|
|
475
|
+
} else {
|
|
476
|
+
this.popupOverlay.setPosition(undefined);
|
|
477
|
+
closer.blur();
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Build the content of the popup and display it by adding the popupOverlay to the map
|
|
488
|
+
* reference code reused and modified : safire-components projects (git repo https://gitlab.in2p3.fr/sedoo)
|
|
489
|
+
* @param features
|
|
490
|
+
* @param position
|
|
491
|
+
*/
|
|
492
|
+
createPopup(features, position) {
|
|
493
|
+
let content = '';
|
|
494
|
+
let i = 0;
|
|
495
|
+
features.forEach(feature => {
|
|
496
|
+
if (feature.id_) { //if feature.id_ is defined (e.g for SAFIRE)
|
|
497
|
+
content += '<p><h4>' + feature.id_ + '</h4>';
|
|
498
|
+
} else { //if feature.id_ is not defined (e.g for IAGOS)
|
|
499
|
+
i++;
|
|
500
|
+
let featureTitle = this.getFeatureTitleBasedOnGeometryType(feature);
|
|
501
|
+
if (features.length > 1) {
|
|
502
|
+
content += '<details>';
|
|
503
|
+
content += '<summary><b>' + featureTitle + ' ' + i + '</b></summary>';
|
|
504
|
+
} else {
|
|
505
|
+
content += '<p><h4>' + featureTitle + '</h4>';
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
let keys = this.extractKeysFromValues(feature.values_);
|
|
509
|
+
keys.forEach(key => {
|
|
510
|
+
|
|
511
|
+
//if feature.values_[key] is an Array
|
|
512
|
+
if (Array.isArray(feature.values_[key])) {
|
|
513
|
+
content += '<details>';
|
|
514
|
+
content += '<summary>' + key + ':</summary>'
|
|
515
|
+
if (feature.values_[key].length) {
|
|
516
|
+
content += '<ul>'
|
|
517
|
+
feature.values_[key].forEach(value => {
|
|
518
|
+
content += '<li>' + value + '</li>';
|
|
519
|
+
});
|
|
520
|
+
content += '</ul>';
|
|
521
|
+
} else {
|
|
522
|
+
content += '<kbd style="margin-left: 2em;">Empty</kbd>';
|
|
523
|
+
}
|
|
524
|
+
content += '</details>';
|
|
525
|
+
} else {
|
|
526
|
+
//Display content for a non array value
|
|
527
|
+
content += '<p>' + key + ':<kbd style="margin-left: 2em;">' + feature.values_[key] + '</kbd></p>';
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
if (features.length > 1 && !feature.id_) {
|
|
531
|
+
content += '</details>';
|
|
532
|
+
} else {
|
|
533
|
+
content += '</p>';
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
this.content_element.innerHTML = content;
|
|
538
|
+
|
|
539
|
+
this.popupOverlay.setPosition(position);
|
|
540
|
+
this.map.addOverlay(this.popupOverlay);
|
|
541
|
+
|
|
542
|
+
},
|
|
543
|
+
|
|
544
|
+
getFeatureTitleBasedOnGeometryType(feature) {
|
|
545
|
+
switch (feature.getGeometry().getType()) {
|
|
546
|
+
case 'Point':
|
|
547
|
+
return 'Site';
|
|
548
|
+
case 'LineString':
|
|
549
|
+
case 'MultiLineString':
|
|
550
|
+
return 'Trajectory';
|
|
551
|
+
case 'Polygon':
|
|
552
|
+
return 'Area';
|
|
553
|
+
default:
|
|
554
|
+
return 'Feature';
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
|
|
558
|
+
extractKeysFromValues(values) {
|
|
559
|
+
let result = [];
|
|
560
|
+
if (!!values) {
|
|
561
|
+
Object.keys(values).forEach(value => {
|
|
562
|
+
if (!this.exactWordIsInArray(value, this.bannedKeysArray)) //this.bannedKeysArray : properties to not display in the popup
|
|
563
|
+
result.push(value);
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
return result;
|
|
567
|
+
},
|
|
568
|
+
|
|
569
|
+
exactWordIsInArray(word, array) {
|
|
570
|
+
let result = false;
|
|
571
|
+
if (word && array)
|
|
572
|
+
array.forEach((item) =>
|
|
573
|
+
item && item === word ? (result = true) : null
|
|
574
|
+
);
|
|
575
|
+
return result;
|
|
576
|
+
},
|
|
577
|
+
|
|
578
|
+
closePopup() {
|
|
579
|
+
this.map.removeOverlay(this.popupOverlay);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
}
|
|
585
|
+
</script>
|
|
586
|
+
<style scoped>
|
|
587
|
+
|
|
588
|
+
.ol-popup {
|
|
589
|
+
position: absolute;
|
|
590
|
+
background-color: white;
|
|
591
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
|
|
592
|
+
padding: 15px;
|
|
593
|
+
border-radius: 10px;
|
|
594
|
+
border: 1px solid #cccccc;
|
|
595
|
+
bottom: 12px;
|
|
596
|
+
left: -50px;
|
|
597
|
+
/*min-width: 280px;*/
|
|
598
|
+
/*cf. SAFIR */
|
|
599
|
+
min-width: 450px;
|
|
600
|
+
max-height: 250px;
|
|
601
|
+
/*overflow-y: scroll; adding this makes the arrow disappear*/
|
|
602
|
+
color: black;
|
|
603
|
+
-webkit-filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
|
|
604
|
+
filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
#popup-content {
|
|
608
|
+
max-height: 200px; /* Or whatever height you want */
|
|
609
|
+
overflow-y: auto; /* Use auto instead of scroll to only show scrollbar when necessary */
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
.ol-popup:after, .ol-popup:before {
|
|
614
|
+
top: 100%;
|
|
615
|
+
border: solid transparent;
|
|
616
|
+
content: " ";
|
|
617
|
+
height: 0;
|
|
618
|
+
width: 0;
|
|
619
|
+
position: absolute;
|
|
620
|
+
pointer-events: none;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.ol-popup:after {
|
|
624
|
+
border-top-color: white;
|
|
625
|
+
border-width: 10px;
|
|
626
|
+
left: 48px;
|
|
627
|
+
margin-left: -10px;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
.ol-popup-closer {
|
|
631
|
+
text-decoration: none;
|
|
632
|
+
position: absolute;
|
|
633
|
+
top: 2px;
|
|
634
|
+
right: 8px;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.ol-popup-closer:after {
|
|
638
|
+
content: "✖";
|
|
639
|
+
}
|
|
640
|
+
</style>
|