Skip to content

Commit 3f85533

Browse files
committed
Transformation of all OpenLayers component into Composition API
gathering/regrouping of different "responsabilities" of the huge OpenLayersMap into multiple files that can be then re-used (partially) by Cesium (i.e. mouse interaction) ZIndex calculation has also been made "bilingual" but this needs to be put to the test of being applied to the Cesium component.
1 parent 53e6538 commit 3f85533

29 files changed

+1669
-1835
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import LayerTypes from '@/api/layers/LayerTypes.enum'
2+
import { ClickInfo, ClickType } from '@/store/modules/map.store'
3+
import { computed } from 'vue'
4+
import { useStore } from 'vuex'
5+
6+
const msBeforeTriggeringLocationPopup = 700
7+
8+
export function useMouseOnMap() {
9+
let isPointerDown = false
10+
let isStillOnStartingPosition = false
11+
let hasPointerDownTriggeredLocationPopup = false
12+
let contextMenuTimeout = null
13+
14+
const store = useStore()
15+
const visibleGeoJsonLayers = computed(() =>
16+
store.getters.visibleLayers.filter((layer) => layer.type === LayerTypes.GEOJSON)
17+
)
18+
const visibleKMLLayers = computed(() =>
19+
store.getters.visibleLayers.filter((layer) => layer.type === LayerTypes.KML)
20+
)
21+
22+
/**
23+
* @param {[Number, Number]} screenPosition
24+
* @param {[Number, Number]} coordinate
25+
*/
26+
function onLeftClickDown(screenPosition, coordinate) {
27+
isPointerDown = true
28+
isStillOnStartingPosition = true
29+
// if the user stays with a mouse left (or touch) down for a certain amount of time, we want
30+
// to show the LocationPopup instead of running an identification of features
31+
contextMenuTimeout = setTimeout(() => {
32+
if (isStillOnStartingPosition) {
33+
onRightClick(screenPosition, coordinate)
34+
hasPointerDownTriggeredLocationPopup = true
35+
}
36+
}, msBeforeTriggeringLocationPopup)
37+
}
38+
39+
function onLeftClickUp(screenPosition, coordinate) {
40+
clearTimeout(contextMenuTimeout)
41+
// if we've already "handled" this click event, we do nothing more
42+
if (!hasPointerDownTriggeredLocationPopup && isStillOnStartingPosition) {
43+
const features = []
44+
// if there is a GeoJSON layer currently visible, we will find it and search for features under the mouse cursor
45+
visibleGeoJsonLayers.value.forEach((geoJSonLayer) => {
46+
console.log(
47+
'GeoJSON features',
48+
geoJSonLayer
49+
// booleanIntersects(geoJSonLayer, coordinate)
50+
)
51+
// features.push(...this.handleClickOnGeoJsonLayer(event, geoJSonLayer))
52+
})
53+
visibleKMLLayers.value.forEach(() => {
54+
// features.push(...this.handleClickOnKMLLayer(event, KMLLayer))
55+
})
56+
store.dispatch(
57+
'click',
58+
new ClickInfo(coordinate, screenPosition, features, ClickType.LEFT_SINGLECLICK)
59+
)
60+
}
61+
// reset of all flags
62+
isPointerDown = false
63+
isStillOnStartingPosition = false
64+
hasPointerDownTriggeredLocationPopup = false
65+
}
66+
67+
function onMouseMove() {
68+
if (isPointerDown) {
69+
isStillOnStartingPosition = false
70+
}
71+
}
72+
73+
function onRightClick(screenPosition, coordinate) {
74+
store.dispatch(
75+
'click',
76+
new ClickInfo(coordinate, screenPosition, [], ClickType.CONTEXTMENU)
77+
)
78+
}
79+
80+
return {
81+
onLeftClickDown,
82+
onLeftClickUp,
83+
onRightClick,
84+
onMouseMove,
85+
}
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import LayerTypes from '@/api/layers/LayerTypes.enum'
2+
import { computed } from 'vue'
3+
import { useStore } from 'vuex'
4+
5+
/** Composable that gives utility function to calculate/get layers' and features' z-index */
6+
export function useLayerZIndexCalculation() {
7+
const store = useStore()
8+
const is3dActive = computed(() => store.state.cesium.active)
9+
const backgroundLayers = computed(() => {
10+
if (is3dActive.value) {
11+
return store.getters.backgroundLayersFor3D
12+
}
13+
return [store.state.layers.currentBackgroundLayer]
14+
})
15+
const selectedFeatures = computed(() => store.state.features.selectedFeatures)
16+
const pinnedLocation = computed(() => store.state.map.pinnedLocation)
17+
const previewLocation = computed(() => store.state.map.previewedPinnedLocation)
18+
const crossHair = computed(() => store.state.position.crossHair)
19+
20+
const startingZIndexForVisibleLayers = computed(() => {
21+
if (backgroundLayers.value && backgroundLayers.value.length > 0) {
22+
return backgroundLayers.value.length - 1
23+
}
24+
return 0
25+
})
26+
const visibleLayersHoldingAZIndex = computed(() => {
27+
const visibleLayersWithZIndex = [...backgroundLayers.value]
28+
if (is3dActive.value) {
29+
// in 3D, GeoJSON and KML layers are not given a ZIndex as Cesium handles them differently
30+
// (as primitive layers, on top of all other layers)
31+
visibleLayersWithZIndex.push(
32+
...store.getters.visibleLayers.filter(
33+
(visibleLayer) =>
34+
[LayerTypes.KML, LayerTypes.GEOJSON].indexOf(visibleLayer.type) === -1
35+
)
36+
)
37+
} else {
38+
visibleLayersWithZIndex.push(...store.getters.visibleLayers)
39+
}
40+
return visibleLayersWithZIndex
41+
})
42+
43+
// from here: things that will be exported
44+
45+
const startingZIndexForThingsOnTopOfLayers = computed(
46+
() => visibleLayersHoldingAZIndex.value.length
47+
)
48+
const zIndexHighlightedFeatures = computed(() => startingZIndexForThingsOnTopOfLayers.value)
49+
const zIndexDroppedPin = computed(() => {
50+
let zIndex = zIndexHighlightedFeatures.value
51+
if (selectedFeatures.value.length > 0) {
52+
zIndex++
53+
}
54+
return zIndex
55+
})
56+
const zIndexPreviewPosition = computed(() => {
57+
let zIndex = zIndexDroppedPin.value
58+
if (pinnedLocation.value) {
59+
zIndex++
60+
}
61+
return zIndex
62+
})
63+
const zIndexCrossHair = computed(() => {
64+
let zIndex = zIndexPreviewPosition.value
65+
if (previewLocation.value) {
66+
zIndex++
67+
}
68+
return zIndex
69+
})
70+
const nextAvailableZIndex = computed(() => {
71+
let zIndex = zIndexCrossHair.value
72+
if (crossHair.value) {
73+
zIndex++
74+
}
75+
return zIndex
76+
})
77+
78+
/**
79+
* Gives the z-index of a layer, taking into account if the map is shown in 3D or not. This
80+
* works for BG layer too.
81+
*
82+
* @param {AbstractLayer} layer A background or visible layer
83+
* @returns {Number} The Z-Index for this layer
84+
*/
85+
function getZIndexForLayer(layer) {
86+
if (!layer) {
87+
return -1
88+
}
89+
// checking first of this is a BG layer
90+
const matchingBackgroundLayer = backgroundLayers.value.find(
91+
(bgLayer) => bgLayer.getID() === layer.getID()
92+
)
93+
if (matchingBackgroundLayer) {
94+
return backgroundLayers.value.indexOf(matchingBackgroundLayer)
95+
}
96+
// if not found in the BG, we check the visible layers
97+
const matchingLayerInVisibleLayers = visibleLayersHoldingAZIndex.value.find(
98+
(visibleLayer) => visibleLayer.getID() === layer.getID()
99+
)
100+
if (!matchingLayerInVisibleLayers) {
101+
return -1
102+
}
103+
const indexOfLayerInVisibleLayers = visibleLayersHoldingAZIndex.value.indexOf(
104+
matchingLayerInVisibleLayers
105+
)
106+
// checking if there are some group of layers before, meaning more than one z-index for this entry
107+
const subLayersPresentUnderThisLayer = visibleLayersHoldingAZIndex.value
108+
// only keeping previous layers (if layer is first, an empty array will be returned by slice(0, 0))
109+
.slice(0, Math.max(indexOfLayerInVisibleLayers - 1, 0))
110+
// only keeping groups of layers
111+
.filter((previousLayer) => previousLayer.type === LayerTypes.GROUP)
112+
// counting how many layers they have inside each group
113+
.map((previousGroup) => previousGroup.layers.length)
114+
// sum
115+
.reduce((a, b) => a + b, 0)
116+
return (
117+
startingZIndexForVisibleLayers.value +
118+
subLayersPresentUnderThisLayer +
119+
indexOfLayerInVisibleLayers
120+
)
121+
}
122+
123+
return {
124+
zIndexHighlightedFeatures,
125+
zIndexDroppedPin,
126+
zIndexPreviewPosition,
127+
zIndexCrossHair,
128+
nextAvailableZIndex,
129+
getZIndexForLayer,
130+
}
131+
}
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,59 @@
1-
<template>
2-
<div>
3-
<slot />
4-
</div>
5-
</template>
1+
<script setup>
2+
/**
3+
* Component managing the rendering of a red transparent circle to show currently how accurate is
4+
* the geolocation
5+
*/
66
7-
<script>
7+
import useAddLayerToMap from '@/modules/map/components/openlayers/utils/add-layers-to-map.composable'
88
import Feature from 'ol/Feature'
99
import { Circle } from 'ol/geom'
1010
import { Vector as VectorLayer } from 'ol/layer'
1111
import { Vector as VectorSource } from 'ol/source'
1212
import { Fill, Stroke, Style } from 'ol/style'
13-
import addLayerToMapMixin from './utils/addLayerToMap-mixins'
13+
import { computed, inject, toRef, watch } from 'vue'
14+
import { useStore } from 'vuex'
1415
15-
const accuracyCircleStyle = new Style({
16-
fill: new Fill({
17-
color: [255, 0, 0, 0.1],
18-
}),
19-
stroke: new Stroke({
20-
color: [255, 0, 0, 0.9],
21-
width: 3,
16+
const props = defineProps({
17+
zIndex: {
18+
type: Number,
19+
default: -1,
20+
},
21+
})
22+
// if we do not wrap props around refs, we lose reactivity
23+
const zIndex = toRef(props, 'zIndex')
24+
25+
// mapping relevant store values
26+
const store = useStore()
27+
const position = computed(() => store.state.layers.previewYear)
28+
const accuracy = computed(() => store.state.position.projection)
29+
30+
const accuracyCircle = new Circle(position.value, accuracy.value)
31+
const accuracyCircleFeature = new Feature({
32+
geometry: accuracyCircle,
33+
})
34+
accuracyCircleFeature.setStyle(
35+
new Style({
36+
fill: new Fill({
37+
color: [255, 0, 0, 0.1],
38+
}),
39+
stroke: new Stroke({
40+
color: [255, 0, 0, 0.9],
41+
width: 3,
42+
}),
43+
})
44+
)
45+
const layer = new VectorLayer({
46+
id: `geolocation-accuracy-layer`,
47+
source: new VectorSource({
48+
features: [accuracyCircleFeature],
2249
}),
2350
})
2451
25-
/**
26-
* Component managing the rendering of a red transparent circle to show currently how accurate is
27-
* the geolocation
28-
*/
29-
export default {
30-
mixins: [addLayerToMapMixin],
31-
inject: ['getMap'],
32-
props: {
33-
position: {
34-
type: Array,
35-
required: true,
36-
},
37-
accuracy: {
38-
type: Number,
39-
required: true,
40-
},
41-
zIndex: {
42-
type: Number,
43-
default: -1,
44-
},
45-
},
46-
watch: {
47-
position(newPosition) {
48-
this.accuracyCircle.setCenter(newPosition)
49-
},
50-
accuracy(newAccuracy) {
51-
this.accuracyCircle.setRadius(newAccuracy)
52-
},
53-
},
54-
created() {
55-
this.accuracyCircle = new Circle(this.position, this.accuracy)
56-
this.accuracyCircleFeature = new Feature({
57-
geometry: this.accuracyCircle,
58-
})
59-
this.accuracyCircleFeature.setStyle(accuracyCircleStyle)
60-
this.layer = new VectorLayer({
61-
id: `geolocation-accuracy-layer`,
62-
source: new VectorSource({
63-
features: [this.accuracyCircleFeature],
64-
}),
65-
})
66-
},
67-
}
52+
// grabbing the map from the main OpenLayersMap component and use the composable that adds this layer to the map
53+
const olMap = inject('olMap', null)
54+
useAddLayerToMap(layer, olMap, zIndex)
55+
56+
// reacting to changes accordingly
57+
watch(position, (newPosition) => accuracyCircle.setCenter(newPosition))
58+
watch(accuracy, (newAccuracy) => accuracyCircle.setRadius(newAccuracy))
6859
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script setup>
2+
import { useLayerZIndexCalculation } from '@/modules/map/components/common/z-index.composable'
3+
import OpenLayersInternalLayer from '@/modules/map/components/openlayers/OpenLayersInternalLayer.vue'
4+
import { computed } from 'vue'
5+
import { useStore } from 'vuex'
6+
7+
const store = useStore()
8+
const currentBackgroundLayer = computed(() => store.state.layers.currentBackgroundLayer)
9+
10+
const { getZIndexForLayer } = useLayerZIndexCalculation()
11+
</script>
12+
13+
<template>
14+
<OpenLayersInternalLayer
15+
v-if="currentBackgroundLayer"
16+
:layer-config="currentBackgroundLayer"
17+
:z-index="getZIndexForLayer(currentBackgroundLayer)"
18+
/>
19+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<script setup>
2+
import { useLayerZIndexCalculation } from '@/modules/map/components/common/z-index.composable'
3+
import OpenLayersMarker from '@/modules/map/components/openlayers/OpenLayersMarker.vue'
4+
import { OpenLayersMarkerStyles } from '@/modules/map/components/openlayers/utils/markerStyle'
5+
import { CrossHairs } from '@/store/modules/position.store'
6+
import { computed } from 'vue'
7+
import { useStore } from 'vuex'
8+
9+
const store = useStore()
10+
const crossHair = computed(() => store.state.position.crossHair)
11+
const crossHairPosition = computed(() => store.state.position.crossHairPosition)
12+
const crossHairStyle = computed(() => {
13+
switch (crossHair.value) {
14+
case CrossHairs.point:
15+
return OpenLayersMarkerStyles.POINT
16+
case CrossHairs.cross:
17+
return OpenLayersMarkerStyles.CROSS
18+
case CrossHairs.bowl:
19+
return OpenLayersMarkerStyles.BOWL
20+
case CrossHairs.marker:
21+
return OpenLayersMarkerStyles.BALLOON
22+
case CrossHairs.circle:
23+
return OpenLayersMarkerStyles.CIRCLE
24+
}
25+
return null
26+
})
27+
const { zIndexCrossHair } = useLayerZIndexCalculation()
28+
</script>
29+
30+
<template>
31+
<OpenLayersMarker
32+
v-if="crossHair"
33+
:position="crossHairPosition"
34+
:marker-style="crossHairStyle"
35+
:z-index="zIndexCrossHair"
36+
/>
37+
</template>

0 commit comments

Comments
 (0)