Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BGDIINF_SB-2980: add GeoJSON and KML layer components for 3d #426

Merged
merged 9 commits into from
Aug 17, 2023
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.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"liang-barsky": "^1.0.5",
"maplibre-gl": "^3.3.0",
"ol": "^7.5.1",
"ol-cesium": "^2.15.0",
"pako": "^2.1.0",
"print-js": "^1.6.0",
"proj4": "^2.9.0",
Expand Down
62 changes: 62 additions & 0 deletions src/modules/map/components/cesium/CesiumGeoJSONLayer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<template>
<div>
<slot />
</div>
</template>

<script>
import axios from 'axios'
import OlStyleForPropertyValue from '@/modules/map/components/openlayers/utils/styleFromLiterals'
import GeoJSON from 'ol/format/GeoJSON'
import { Vector as VectorSource } from 'ol/source'
import log from '@/utils/logging'
import addPrimitiveLayerMixins from './utils/addPrimitiveLayer-mixins'

/** Adds a GeoJSON layer to the Cesium viewer */
export default {
mixins: [addPrimitiveLayerMixins],
props: {
layerId: {
type: String,
required: true,
},
geojsonUrl: {
type: String,
required: true,
},
styleUrl: {
type: String,
required: true,
},
opacity: {
type: Number,
default: 0.9,
},
},
methods: {
loadLayer() {
return 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)
this.olLayer.setSource(
new VectorSource({
features: new GeoJSON().readFeatures(geojsonData),
})
)
this.olLayer.setStyle(function (feature, res) {
return style.getFeatureStyle(feature, res)
})
return geojsonData.crs ? geojsonData.crs.properties.name : null
})
.catch((error) => {
log.error(
'Error while fetching GeoJSON data/style for layer ' + this.layerId,
error
)
})
},
},
}
</script>
18 changes: 18 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,20 @@
: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"
/>
<CesiumKMLLayer
v-if="layerConfig.type === LayerTypes.KML && layerConfig.addToMap"
:layer-id="layerConfig.getID()"
:opacity="layerConfig.opacity"
:url="layerConfig.getURL()"
:z-index="zIndex"
/>
<slot />
</div>
</template>
Expand All @@ -24,13 +38,17 @@ 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'
import CesiumKMLLayer from './CesiumKMLLayer.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: {
CesiumKMLLayer,
CesiumGeoJSONLayer,
CesiumWMTSLayer,
CesiumWMSLayer,
},
Expand Down
71 changes: 71 additions & 0 deletions src/modules/map/components/cesium/CesiumKMLLayer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<template>
<div>
<slot />
</div>
</template>

<script>
import log from '@/utils/logging'
import { getKmlFromUrl } from '@/api/files.api'
import KML from 'ol/format/KML'
import { WEBMERCATOR } from '@/utils/coordinateSystems'
import { EditableFeature } from '@/api/features.api'
import VectorSource from 'ol/source/Vector'
import { mapState } from 'vuex'
import addPrimitiveLayerMixins from './utils/addPrimitiveLayer-mixins'

/** Renders a KML file to the Cesium viewer */
export default {
mixins: [addPrimitiveLayerMixins],
props: {
layerId: {
type: String,
required: true,
},
url: {
type: String,
required: true,
},
opacity: {
type: Number,
default: 1.0,
},
zIndex: {
type: Number,
default: -1,
},
},
computed: {
...mapState({
availableIconSets: (state) => state.drawing.iconSets,
}),
},
methods: {
async loadLayer() {
try {
const kml = await getKmlFromUrl(this.url)
const features = new KML().readFeatures(kml, {
// Reproject all features to webmercator, as this is the projection used for the view
featureProjection: WEBMERCATOR.epsg,
})
if (features) {
this.olLayer.setSource(new VectorSource({ wrapX: true }))
features.forEach((olFeature) => {
EditableFeature.deserialize(olFeature, this.availableIconSets)
})
// remove all old features first
this.olLayer.getSource().clear()
// add the deserialized features
this.olLayer.getSource().addFeatures(features)
return WEBMERCATOR.epsg
} else {
log.error(`No KML features available to add`, features)
}
} catch (error) {
log.error(`Failed to load kml from ${this.url}`, error)
}
pakb marked this conversation as resolved.
Show resolved Hide resolved
return undefined
},
},
}
</script>
27 changes: 26 additions & 1 deletion 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 All @@ -33,6 +41,7 @@ import {
} from '@/config'
import { UIModes } from '@/store/modules/ui.store'
import '@geoblocks/cesium-compass'
import * as cesium from 'cesium'
import {
Cartesian3,
Cartographic,
Expand All @@ -56,6 +65,9 @@ import { calculateHeight, limitCameraCenter, limitCameraPitchRoll } from './util
import { ClickInfo, ClickType } from '@/store/modules/map.store'
import { WEBMERCATOR, WGS84 } from '@/utils/coordinateSystems'
import proj4 from 'proj4'
import GeoAdminWMSLayer from '@/api/layers/GeoAdminWMSLayer.class'
import GeoAdminGeoJsonLayer from '@/api/layers/GeoAdminGeoJsonLayer.class'
import KMLLayer from '@/api/layers/KMLLayer.class'

export default {
components: { CesiumInternalLayer },
Expand Down Expand Up @@ -100,12 +112,25 @@ 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 || (l.addToMap && l instanceof KMLLayer)
)
},
},
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

const withoutSchemeAndTrailingSlash = (url) => {
const urlWithoutScheme = url.replace('https://', '')
return urlWithoutScheme.endsWith('/')
Expand Down
7 changes: 7 additions & 0 deletions src/modules/map/components/cesium/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,10 @@ export const CAMERA_MIN_PITCH = -Math.PI / 2
* @type {number}
*/
export const CAMERA_MAX_PITCH = Math.PI / 4

/**
* Distance on which depth test will be disabled for primitive to avoid cutting by terrain
*
* @type {number}
*/
export const PRIMITIVE_DISABLE_DEPTH_TEST_DISTANCE = 1.2742018 * 10 ** 7 // Diameter of Earth
25 changes: 5 additions & 20 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 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
Loading