Skip to content

Commit

Permalink
Add streamline mapbox layer
Browse files Browse the repository at this point in the history
  • Loading branch information
ceesvoesenek committed Feb 15, 2024
1 parent d0ecb63 commit 8690fb9
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/components/map/MapComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
:drag-pan="true"
:scroll-zoom="true"
:transformRequest="transformRequest"
:useWebGL2="true"
@mb-created="setMapInstance"
>
<slot></slot>
Expand Down
19 changes: 15 additions & 4 deletions src/components/spatialdisplay/SpatialDisplayComponent.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
<template>
<MapComponent>
<animated-mapbox-layer
<AnimatedMapboxLayer
v-if="layerKind === LayerKind.Static"
:layer="layerOptions"
@doubleclick="onCoordinateClick"
>
</animated-mapbox-layer>
/>
<AnimatedStreamlineMapboxLayer
v-if="layerKind === LayerKind.Streamline"
:layerOptions="layerOptions"
:streamlineOptions="layerCapabilities?.animatedVectors"
@doubleclick="onCoordinateClick"
/>
<div class="colourbar-container" v-if="legend">
<ColourBar
:colourMap="legend"
Expand All @@ -21,7 +27,7 @@
:max-value="maxElevation"
:ticks="elevationTicks"
:unit="elevationUnit"
></ElevationSlider>
/>
<LocationsLayerComponent
v-if="filterIds"
:filterIds="filterIds"
Expand Down Expand Up @@ -51,6 +57,7 @@
<script setup lang="ts">
import MapComponent from '@/components/map/MapComponent.vue'
import LayerKindControl from '@/components/spatialdisplay/LayerKindControl.vue'
import AnimatedStreamlineMapboxLayer from '@/components/wms/AnimatedStreamlineMapboxLayer.vue'
import { ref, computed, onBeforeMount, watch, watchEffect } from 'vue'
import {
Expand Down Expand Up @@ -146,6 +153,10 @@ const legend = computed(() => {
const canUseStreamlines = computed(
() => props.layerCapabilities?.animatedVectors !== undefined,
)
watch(canUseStreamlines, (canUse) => {
// Fall back to static layer if streamlines are not available.
if (!canUse) layerKind.value = LayerKind.Static
})
watchEffect(() => {
if (!legend.value) return
Expand Down
131 changes: 131 additions & 0 deletions src/components/wms/AnimatedStreamlineMapboxLayer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<template>
<div></div>
</template>

<script setup lang="ts">
import { type Layer } from '@deltares/fews-wms-requests'
import { useMap } from '@studiometa/vue-mapbox-gl'
import { onMounted, onUnmounted, watch } from 'vue'
import {
StreamlineStyle,
WMSStreamlineLayer,
type WMSStreamlineLayerOptions,
} from '@/lib/streamlines'
import { configManager } from '@/services/application-config'
import { type MapboxLayerOptions } from './AnimatedMapboxLayer.vue'
import type { MapLayerMouseEvent, MapLayerTouchEvent } from 'mapbox-gl'
type StreamlineLayerOptionsFews = Layer['animatedVectors']
interface Props {
layerOptions?: MapboxLayerOptions
streamlineOptions?: StreamlineLayerOptionsFews
}
const props = defineProps<Props>()
const emit = defineEmits(['doubleclick'])
const { map } = useMap()
const layerId = 'streamlines'
let layer: WMSStreamlineLayer | null = null
let isInitialised = false
onMounted(addLayer)
onUnmounted(removeLayer)
// Recreate the streamline visualiser when the a different layer is selected.
watch(
() => props.layerOptions?.name,
async () => {
removeLayer()
addLayer()
},
)
// Update the velocity field when the time, elevation or color scale range is
// changed.
watch(
() => props.layerOptions?.time,
(time) => {
if (!isInitialised || !layer || !time) return
layer.setTime(time)
},
)
watch(
() => props.layerOptions?.elevation,
(elevation) => {
if (!isInitialised || !layer) return
layer.setElevation(elevation ?? null)
},
)
watch(
() => props.layerOptions?.colorScaleRange,
(colorScaleRange) => {
if (!isInitialised || !layer) return
layer.setColorScaleRange(
getColorScaleRangeFromString(colorScaleRange) ?? null,
)
},
)
function addLayer(): void {
if (!props.layerOptions || !props.streamlineOptions) return
const options = mergeOptions(props.layerOptions, props.streamlineOptions)
// Create and add layer.
isInitialised = false
layer = new WMSStreamlineLayer(layerId, options)
map.value.addLayer(layer, 'boundary_country_outline')
map.value.on('dblclick', onDoubleClick)
// Make sure we are at the appropriate time, elevation and color scale range
// after the visualiser has been initialised.
layer.once('load', async () => {
if (!layer || !props.layerOptions) return
await layer.initialise(
options,
props.layerOptions.time,
props.layerOptions.elevation ?? undefined,
getColorScaleRangeFromString(props.layerOptions.colorScaleRange),
)
isInitialised = true
})
}
function removeLayer(): void {
map.value.removeLayer(layerId)
map.value.off('dblclick', onDoubleClick)
isInitialised = false
}
function onDoubleClick(event: MapLayerMouseEvent | MapLayerTouchEvent) {
emit('doubleclick', event)
}
function mergeOptions(
layerOptions: MapboxLayerOptions,
streamlineOptions: StreamlineLayerOptionsFews,
): WMSStreamlineLayerOptions {
const baseUrl = configManager.get('VITE_FEWS_WEBSERVICES_URL')
const baseUrlWms = `${baseUrl}/wms`
return {
baseUrl: baseUrlWms,
layer: layerOptions.name,
streamlineStyle: StreamlineStyle.ColoredParticles,
numParticles: streamlineOptions?.numberOfParticles ?? 1000,
particleSize: streamlineOptions?.particleSize ?? 3,
speedFactor: streamlineOptions?.speedFactor ?? 0.2,
fadeAmountPerSecond: streamlineOptions?.fadeAmount ?? 0.1,
particleColor: `#${streamlineOptions?.particleColor}`,
}
}
function getColorScaleRangeFromString(
colorScaleRangeString?: string,
): [number, number] | undefined {
if (!colorScaleRangeString) return undefined
return colorScaleRangeString.split(',', 2).map(parseFloat) as [number, number]
}
</script>

0 comments on commit 8690fb9

Please sign in to comment.