Skip to content

Commit

Permalink
BGDIINF_SB-2980: Add GeoJSON layer component for 3d
Browse files Browse the repository at this point in the history
BGDIINF_SB-2980: Add GeoJSON layer component for 3d
  • Loading branch information
vladyslav-tk committed Jul 14, 2023
1 parent e9fc97f commit 7a2cc91
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 37 deletions.
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
"test:e2e:ci:prod": "npm run test:e2e:ci",
"test:ci": "npm run test:unit && npm run test:e2e:ci",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier --write src",
"format-lint": "npm run format && npm run lint",
"format": "prettier --write src",
"format-lint": "npm run format && npm run lint",
"type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
"build": "npm run type-check && vite build",
"build:dev": "npm run build -- --mode development",
Expand Down Expand Up @@ -55,6 +55,7 @@
"liang-barsky": "^1.0.5",
"maplibre-gl": "^3.1.0",
"ol": "^7.4.0",
"ol-cesium": "^2.14.0",
"pako": "^2.1.0",
"print-js": "^1.6.0",
"proj4": "^2.9.0",
Expand Down
120 changes: 120 additions & 0 deletions src/modules/map/components/cesium/CesiumGeoJSONLayer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<template>
<div>
<slot />
</div>
</template>

<script>
import { PrimitiveCollection } from 'cesium'
import axios from 'axios'
import OlStyleForPropertyValue from '@/modules/map/components/openlayers/utils/styleFromLiterals'
import FeatureConverter from 'ol-cesium/src/olcs/FeatureConverter'
import GeoJSON from 'ol/format/GeoJSON'
import { Vector as VectorLayer } from 'ol/layer'
import { Vector as VectorSource } from 'ol/source'
import { updateCollectionOpacity } from '@/modules/map/components/cesium/utils/geoJsonUtils'
import addLayerToViewer from '@/modules/map/components/cesium/utils/addLayerToViewer-mixins'
import log from '@/utils/logging'
const STYLE_RESOLUTION = 100
/** Adds a GeoJSON layer to the Cesium viewer */
export default {
mixins: [addLayerToViewer],
props: {
layerId: {
type: String,
required: true,
},
geojsonUrl: {
type: String,
required: true,
},
styleUrl: {
type: String,
required: true,
},
opacity: {
type: Number,
default: 0.9,
},
},
methods: {
addLayer(layer) {
this.getViewer().scene.primitives.add(layer)
this.isPresentOnMap = true
},
removeLayer(layer) {
const viewer = this.getViewer()
layer.removeAll()
viewer.scene.primitives.remove(layer)
viewer.scene.requestRender()
this.isPresentOnMap = false
},
},
watch: {
opacity(newOpacity) {
updateCollectionOpacity(this.layer, newOpacity)
this.getViewer().scene.requestRender()
},
},
created() {
const scene = this.getViewer().scene
this.layer = new PrimitiveCollection()
const featureConverter = new FeatureConverter(scene)
const olLayer = new VectorLayer({
id: this.layerId,
opacity: this.opacity,
properties: { altitudeMode: 'relativeToGround' },
})
Promise.all([axios.get(this.geojsonUrl), axios.get(this.styleUrl)])
.then((responses) => {
const geojsonData = responses[0].data
const geojsonStyleLiterals = responses[1].data
const style = new OlStyleForPropertyValue(geojsonStyleLiterals)
olLayer.setSource(
new VectorSource({
features: new GeoJSON().readFeatures(geojsonData),
})
)
olLayer.setStyle(function (feature, res) {
return style.getFeatureStyle(feature, res)
})
const counterpart = featureConverter.olVectorLayerToCesium(
olLayer,
{
getProjection: () =>
geojsonData.crs ? geojsonData.crs.properties.name : null,
getResolution: () => STYLE_RESOLUTION,
},
{}
)
// need to wait for terrain loaded otherwise primitives will be placed wrong
if (this.layer) {
if (scene.globe.tilesLoaded) {
this.layer.add(counterpart.getRootPrimitive())
updateCollectionOpacity(this.layer, this.opacity)
this.getViewer().scene.requestRender()
} else {
const unlisten = scene.globe.tileLoadProgressEvent.addEventListener(
(queueLength) => {
if (scene.globe.tilesLoaded && queueLength === 0) {
this.layer.add(counterpart.getRootPrimitive())
updateCollectionOpacity(this.layer, this.opacity)
this.getViewer().scene.requestRender()
unlisten()
}
}
)
}
}
})
.catch((error) => {
log.error(
'Error while fetching GeoJSON data/style for layer ' + this.layerId,
error
)
})
},
}
</script>
9 changes: 9 additions & 0 deletions src/modules/map/components/cesium/CesiumInternalLayer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
:projection="LV95"
:z-index="zIndex"
/>
<CesiumGeoJSONLayer
v-if="layerConfig.type === LayerTypes.GEOJSON"
:layer-id="layerConfig.getID()"
:opacity="layerConfig.opacity"
:geojson-url="layerConfig.geoJsonUrl"
:style-url="layerConfig.styleUrl"
/>
<slot />
</div>
</template>
Expand All @@ -24,13 +31,15 @@ import LayerTypes from '@/api/layers/LayerTypes.enum'
import { LV95, WEBMERCATOR } from '@/utils/coordinateSystems'
import CesiumWMTSLayer from './CesiumWMTSLayer.vue'
import CesiumWMSLayer from './CesiumWMSLayer.vue'
import CesiumGeoJSONLayer from './CesiumGeoJSONLayer.vue'
/**
* Transforms a layer config (metadata, structures can be found in api/layers/** files) into the
* correct Cesium counterpart depending on the layer type.
*/
export default {
components: {
CesiumGeoJSONLayer,
CesiumWMTSLayer,
CesiumWMSLayer,
},
Expand Down
38 changes: 33 additions & 5 deletions src/modules/map/components/cesium/CesiumMap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@
:z-index="0"
/>
<!-- Adding all other layers -->
<!-- Layers split for correct zIndex ordering -->
<CesiumInternalLayer
v-for="(layer, index) in visibleLayers"
v-for="(layer, index) in visibleImageryLayers"
:key="layer.getID()"
:layer-config="layer"
:preview-year="previewYear"
:z-index="index + startingZIndexForVisibleLayers"
/>
<CesiumInternalLayer
v-for="(layer, index) in visiblePrimitiveLayers"
:key="layer.getID()"
:layer-config="layer"
:preview-year="previewYear"
:z-index="index"
/>
</div>
</div>
<cesium-compass v-show="isDesktopMode" ref="compass"></cesium-compass>
Expand Down Expand Up @@ -45,6 +53,8 @@ import GeoAdminWMTSLayer from '@/api/layers/GeoAdminWMTSLayer.class'
import LayerTimeConfig from '@/api/layers/LayerTimeConfig.class'
import { CURRENT_YEAR_WMTS_TIMESTAMP } from '@/api/layers/LayerTimeConfigEntry.class'
import '@geoblocks/cesium-compass'
import GeoAdminWMSLayer from '@/api/layers/GeoAdminWMSLayer.class'
import GeoAdminGeoJsonLayer from '@/api/layers/GeoAdminGeoJsonLayer.class'
export default {
components: { CesiumInternalLayer },
Expand Down Expand Up @@ -89,12 +99,23 @@ export default {
startingZIndexForVisibleLayers() {
return this.currentBackgroundLayer ? 1 : 0
},
visibleImageryLayers() {
return this.visibleLayers.filter(
(l) => l instanceof GeoAdminWMTSLayer || l instanceof GeoAdminWMSLayer
)
},
visiblePrimitiveLayers() {
return this.visibleLayers.filter((l) => l instanceof GeoAdminGeoJsonLayer)
},
},
beforeCreate() {
// Global variable required for Cesium and point to the URL where four static directories (see vite.config) are served
// https://cesium.com/learn/cesiumjs-learn/cesiumjs-quickstart/#install-with-npm
window['CESIUM_BASE_URL'] = '.'
// required for ol-cesium
window['Cesium'] = cesium
// A per server key list of overrides to use for throttling limits.
// Useful when streaming data from a known HTTP/2 or HTTP/3 server.
Object.assign(RequestScheduler.requestsByServer, {
Expand Down Expand Up @@ -165,7 +186,9 @@ export default {
this.viewerCreated = true
this.viewer.scene.postRender.addEventListener(limitCameraCenter(TILEGRID_EXTENT_EPSG_4326))
this.viewer.scene.postRender.addEventListener(limitCameraPitchRoll(CAMERA_MIN_PITCH, CAMERA_MAX_PITCH, 0.0, 0.0))
this.viewer.scene.postRender.addEventListener(
limitCameraPitchRoll(CAMERA_MIN_PITCH, CAMERA_MAX_PITCH, 0.0, 0.0)
)
this.flyToPosition()
},
Expand All @@ -179,9 +202,13 @@ export default {
flyToPosition() {
const x = this.camera ? this.camera.x : this.centerEpsg4326[0]
const y = this.camera ? this.camera.y : this.centerEpsg4326[1]
const z = this.camera ? this.camera.z : calculateHeight(this.resolution, this.viewer.canvas.clientWidth)
const z = this.camera
? this.camera.z
: calculateHeight(this.resolution, this.viewer.canvas.clientWidth)
const heading = this.camera ? CesiumMath.toRadians(this.camera.heading) : -this.rotation
const pitch = this.camera ? CesiumMath.toRadians(this.camera.pitch) : -CesiumMath.PI_OVER_TWO
const pitch = this.camera
? CesiumMath.toRadians(this.camera.pitch)
: -CesiumMath.PI_OVER_TWO
const roll = this.camera ? CesiumMath.toRadians(this.camera.roll) : 0
this.viewer.camera.flyTo({
destination: Cartesian3.fromDegrees(x, y, z),
Expand Down Expand Up @@ -219,7 +246,8 @@ export default {
height: 100%;
z-index: $zindex-map;
.cesium-viewer, .cesium-widget canvas {
.cesium-viewer,
.cesium-widget canvas {
position: absolute;
width: 100%;
height: 100%;
Expand Down
39 changes: 9 additions & 30 deletions src/modules/map/components/cesium/utils/addImageryLayer-mixins.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import addLayerToViewer from '@/modules/map/components/cesium/utils/addLayerToViewer-mixins'

/**
* Vue mixin that will handle the addition or removal of a Cesium Imagery layer. This is a
* centralized way of describing this logic.
Expand All @@ -16,26 +18,9 @@
* imagery layers in Cesium viewer.
*/
const addImageryLayerMixins = {
inject: ['getViewer'],
data() {
return {
isPresentOnMap: false,
}
},
mounted() {
if (this.layer && !this.isPresentOnMap) {
this.addLayer(this.zIndex, this.layer)
}
},
unmounted() {
if (this.layer && this.isPresentOnMap) {
this.removeLayer(this.layer)
}

delete this.layer
},
mixins: [addLayerToViewer],
methods: {
addLayer(zIndex, layer) {
addLayer(layer, zIndex) {
const viewer = this.getViewer()
viewer.scene.imageryLayers.add(layer, zIndex)
this.isPresentOnMap = true
Expand All @@ -48,7 +33,7 @@ const addImageryLayerMixins = {
watch: {
opacity(newOpacity) {
this.layer.alpha = newOpacity
this.getViewer().scene.render()
this.getViewer().scene.requestRender()
},
url(newUrl) {
const viewer = this.getViewer()
Expand All @@ -57,18 +42,12 @@ const addImageryLayerMixins = {
this.layer = this.createImagery(newUrl)
viewer.scene.imageryLayers.add(this.layer, index)
},
zIndex(zIndex) {
zIndex() {
if (this.layer) {
const imageryLayers = this.getViewer().scene.imageryLayers
const index = imageryLayers.indexOf(this.layer)
const indexDiff = Math.abs(zIndex - index)
for (let i = indexDiff; i !== 0; i--) {
if (index > zIndex) {
imageryLayers.lower(this.layer)
} else {
imageryLayers.raise(this.layer)
}
}
imageryLayers.lowerToBottom(this.layer)
// raise one time to place layer above base layer
imageryLayers.raise(this.layer)
}
},
},
Expand Down
30 changes: 30 additions & 0 deletions src/modules/map/components/cesium/utils/addLayerToViewer-mixins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Vue mixin that will handle the addition or removal of a Cesium layer.
*
* Each component that uses this mixin must create a layer (`this.layer`) and next methods:
*
* - `addLayer` that gets `layer` and `zIndex` (optional) as properties
* - `removeLayer` that gets `layer` as property
*/
const addLayerToViewer = {
inject: ['getViewer'],
data() {
return {
isPresentOnMap: false,
}
},
mounted() {
if (this.layer && !this.isPresentOnMap) {
this.addLayer(this.layer, this.zIndex)
}
},
unmounted() {
if (this.layer && this.isPresentOnMap) {
this.removeLayer(this.layer)
}

delete this.layer
},
}

export default addLayerToViewer
Loading

0 comments on commit 7a2cc91

Please sign in to comment.