Skip to content

Commit e9fc97f

Browse files
authored
Merge pull request #429 from geoadmin/feat-BGDIINF_SB-3007-camera_to_store
BGDIINF_SB-3007: Send cesium camera changes to the store
2 parents 21ea4cc + c4913f1 commit e9fc97f

File tree

8 files changed

+187
-146
lines changed

8 files changed

+187
-146
lines changed

src/modules/map/components/cesium/CesiumMap.vue

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,18 @@ import {
2727
Color,
2828
Cartesian3,
2929
RequestScheduler,
30+
Math as CesiumMath,
3031
} from 'cesium'
3132
import { UIModes } from '@/store/modules/ui.store'
32-
import { mapGetters, mapState } from 'vuex'
33+
import { mapGetters, mapState, mapActions } from 'vuex'
3334
import {
3435
TERRAIN_URL,
3536
CAMERA_MIN_ZOOM_DISTANCE,
3637
CAMERA_MAX_ZOOM_DISTANCE,
3738
CAMERA_MIN_PITCH,
3839
CAMERA_MAX_PITCH,
3940
} from './constants'
40-
import { limitCameraCenter, limitCameraPitchRoll } from './utils/cameraUtils'
41+
import { limitCameraCenter, limitCameraPitchRoll, calculateHeight } from './utils/cameraUtils'
4142
import { IS_TESTING_WITH_CYPRESS, WMTS_BASE_URL, TILEGRID_EXTENT_EPSG_4326 } from '@/config'
4243
import CesiumInternalLayer from './CesiumInternalLayer.vue'
4344
import GeoAdminWMTSLayer from '@/api/layers/GeoAdminWMTSLayer.class'
@@ -77,8 +78,7 @@ export default {
7778
...mapState({
7879
zoom: (state) => state.position.zoom,
7980
rotation: (state) => state.position.rotation,
80-
center: (state) => state.position.center,
81-
is3DActive: (state) => state.ui.showIn3d,
81+
camera: (state) => state.position.camera,
8282
uiMode: (state) => state.ui.mode,
8383
previewYear: (state) => state.layers.previewYear,
8484
}),
@@ -145,6 +145,8 @@ export default {
145145
scene.pickTranslucentDepth = true
146146
scene.backgroundColor = Color.TRANSPARENT
147147
148+
this.viewer.camera.moveEnd.addEventListener(this.onCameraMoveEnd)
149+
148150
const globe = scene.globe
149151
globe.baseColor = Color.TRANSPARENT
150152
globe.depthTestAgainstTerrain = true
@@ -167,20 +169,42 @@ export default {
167169
168170
this.flyToPosition()
169171
},
172+
unmounted() {
173+
this.setCameraPosition(null)
174+
this.viewer.destroy()
175+
delete this.viewer
176+
},
170177
methods: {
178+
...mapActions(['setCameraPosition']),
171179
flyToPosition() {
172-
const cameraHeight =
173-
(this.resolution * this.viewer.canvas.clientWidth) /
174-
(2 * Math.tan(this.viewer.camera.frustum.fov / 2))
180+
const x = this.camera ? this.camera.x : this.centerEpsg4326[0]
181+
const y = this.camera ? this.camera.y : this.centerEpsg4326[1]
182+
const z = this.camera ? this.camera.z : calculateHeight(this.resolution, this.viewer.canvas.clientWidth)
183+
const heading = this.camera ? CesiumMath.toRadians(this.camera.heading) : -this.rotation
184+
const pitch = this.camera ? CesiumMath.toRadians(this.camera.pitch) : -CesiumMath.PI_OVER_TWO
185+
const roll = this.camera ? CesiumMath.toRadians(this.camera.roll) : 0
175186
this.viewer.camera.flyTo({
176-
destination: Cartesian3.fromDegrees(
177-
this.centerEpsg4326[0],
178-
this.centerEpsg4326[1],
179-
cameraHeight
180-
),
187+
destination: Cartesian3.fromDegrees(x, y, z),
188+
orientation: {
189+
heading,
190+
pitch,
191+
roll,
192+
},
181193
duration: 0,
182194
})
183195
},
196+
onCameraMoveEnd() {
197+
const camera = this.viewer.camera
198+
const position = camera.positionCartographic
199+
this.setCameraPosition({
200+
x: CesiumMath.toDegrees(position.longitude).toFixed(6),
201+
y: CesiumMath.toDegrees(position.latitude).toFixed(6),
202+
z: position.height.toFixed(0),
203+
heading: CesiumMath.toDegrees(camera.heading).toFixed(0),
204+
pitch: CesiumMath.toDegrees(camera.pitch).toFixed(0),
205+
roll: CesiumMath.toDegrees(camera.roll).toFixed(0),
206+
})
207+
},
184208
},
185209
}
186210
</script>

src/modules/map/components/cesium/utils/cameraUtils.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212

1313
/**
1414
* Limits the camera pitch and roll.
15+
*
1516
* @param {number} minPitch
1617
* @param {number} maxPitch
1718
* @param {number} minRoll
@@ -34,6 +35,7 @@ export function limitCameraPitchRoll(minPitch, maxPitch, minRoll, maxRoll) {
3435

3536
/**
3637
* Limits the camera center point to a rectangle.
38+
*
3739
* @param {Number[]} rectangle
3840
* @returns
3941
*/
@@ -95,3 +97,21 @@ export function limitCameraCenter(extent) {
9597
}
9698
}
9799
}
100+
101+
/**
102+
* @param {Number} resolution
103+
* @param {Number} width Screen width in pixels
104+
* @returns {number}
105+
*/
106+
export function calculateHeight(resolution, width) {
107+
return (resolution * width) / (2 * Math.tan(Math.PI / 6))
108+
}
109+
110+
/**
111+
* @param {Number} height
112+
* @param {Number} width Screen width in pixels
113+
* @returns {number}
114+
*/
115+
export function calculateResolution(height, width) {
116+
return (2 * Math.tan(Math.PI / 6) * height) / width
117+
}
Lines changed: 33 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,58 @@
11
import AbstractParamConfig from '@/router/storeSync/abstractParamConfig.class'
22

3-
function readValueFromObjectOrReturnNull(object, paramName, type) {
4-
if (object && paramName in object) {
5-
return type(object[paramName])
6-
}
7-
return null
8-
}
9-
103
/**
11-
* @param query
4+
* Reads the camera position from the single URL param. Returns null if the camera position is not
5+
* defined or not complete
6+
*
7+
* @param urlParamValue
128
* @returns {CameraPosition | null}
139
*/
14-
function parseCameraFromQuery(query) {
15-
const camera = readValueFromObjectOrReturnNull(query, 'camera', String)
16-
if (camera) {
17-
let cameraValues = camera.split(',')
18-
// the split must have 6 components (x, y, z, pitch, yaw and roll)
10+
export function readCameraFromUrlParam(urlParamValue) {
11+
if (urlParamValue) {
12+
let cameraValues = urlParamValue.split(',')
13+
// the split must have 6 components (x, y, z, pitch, heading and roll)
1914
if (cameraValues.length === 6) {
2015
// parsing to number all values (default to 0 if the value is empty)
2116
cameraValues = cameraValues.map((value) => (value === '' ? 0 : Number(value)))
22-
const [x, y, z, pitch, yaw, roll] = cameraValues
17+
const [x, y, z, pitch, heading, roll] = cameraValues
2318
return {
2419
x,
2520
y,
2621
z,
2722
pitch,
28-
yaw,
23+
heading,
2924
roll,
3025
}
3126
}
3227
}
3328
return null
3429
}
3530

31+
function dispatchCameraFromUrlIntoStore(store, urlParamValue) {
32+
const promisesForAllDispatch = []
33+
const camera = readCameraFromUrlParam(urlParamValue)
34+
if (camera) {
35+
promisesForAllDispatch.push(store.dispatch('setCameraPosition', camera))
36+
}
37+
return Promise.all(promisesForAllDispatch)
38+
}
39+
40+
function generateCameraUrlParamFromStoreValues(store) {
41+
if (store.state.ui.showIn3d && store.state.position.camera !== null) {
42+
const { x, y, z, pitch, heading, roll } = store.state.position.camera
43+
const valuesAsString = [x, y, z, pitch, heading, roll].map((value) =>
44+
value === 0 ? '' : `${value}`
45+
)
46+
return valuesAsString.join(',')
47+
}
48+
return null
49+
}
50+
3651
/**
3752
* Definition of a set of URL params to store the position camera for the 3D viewer
3853
*
3954
* It will generate a unique camera URL param that will be a concat of all relevant camera values
40-
* (x,y,z,yaw,pitch,roll), this param will only be added to the URL when 3D is active
55+
* (x,y,z,heading,pitch,roll), this param will only be added to the URL when 3D is active
4156
*
4257
* This param parsing is based on the value of the 3D flag in the store, and not the one in the URL.
4358
*/
@@ -46,83 +61,10 @@ export default class CameraParamConfig extends AbstractParamConfig {
4661
super(
4762
'camera',
4863
'setCameraPosition',
49-
() => {},
50-
() => {},
64+
dispatchCameraFromUrlIntoStore,
65+
generateCameraUrlParamFromStoreValues,
5166
false,
5267
String
5368
)
5469
}
55-
56-
/**
57-
* Reads the camera position from the single URL param
58-
*
59-
* @param query
60-
* @returns {CameraPosition | null}
61-
* @override
62-
*/
63-
readValueFromQuery(query) {
64-
return parseCameraFromQuery(query)
65-
}
66-
67-
/**
68-
* Adds the camera URL param if 3D is active, or removes the camera URL param when not active
69-
*
70-
* @param query
71-
* @param store
72-
* @override
73-
*/
74-
populateQueryWithStoreValue(query, store) {
75-
if (store.state.ui.showIn3d) {
76-
const { x, y, z, pitch, yaw, roll } = store.state.position.camera
77-
const valuesAsString = [x, y, z, pitch, yaw, roll].map((value) =>
78-
value === 0 ? '' : `${value}`
79-
)
80-
query['camera'] = valuesAsString.join(',')
81-
} else {
82-
delete query['camera']
83-
}
84-
}
85-
86-
/**
87-
* Dispatches to the store the camera position from the URL, if 3D is active
88-
*
89-
* @param {Vuex.Store} store
90-
* @param query
91-
* @returns {Promise<Awaited[]>}
92-
* @override
93-
*/
94-
populateStoreWithQueryValue(store, query) {
95-
const promisesSetValuesInStore = []
96-
if (store.state.ui.showIn3d) {
97-
const cameraInQuery = parseCameraFromQuery(query)
98-
if (cameraInQuery) {
99-
promisesSetValuesInStore.push(store.dispatch('setCameraPosition', cameraInQuery))
100-
}
101-
}
102-
return Promise.all(promisesSetValuesInStore)
103-
}
104-
105-
/**
106-
* Checks if the camera in the URL is different from the one in the store, this check happens
107-
* only when 3D is active
108-
*
109-
* @param query
110-
* @param store
111-
* @returns {boolean}
112-
* @override
113-
*/
114-
valuesAreDifferentBetweenQueryAndStore(query, store) {
115-
if (store.state.ui.showIn3d) {
116-
const queryCamera = parseCameraFromQuery(query)
117-
if (!queryCamera) {
118-
return true
119-
}
120-
const camera = store.state.position.camera
121-
let isEqual = true
122-
Object.entries(camera).forEach(([key, value]) => {
123-
isEqual &= value === queryCamera[key]
124-
})
125-
return !isEqual
126-
}
127-
}
12870
}

0 commit comments

Comments
 (0)