Skip to content

Commit

Permalink
Merge pull request #2349 from eurodatacube/add-datepicker
Browse files Browse the repository at this point in the history
Integrate Ships-detection indicator on-the-fly integration
  • Loading branch information
lubojr authored Apr 8, 2024
2 parents c82066e + 7ba96c1 commit 9e06c04
Show file tree
Hide file tree
Showing 10 changed files with 362 additions and 11 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/public/eodash-data/stories/E13c_ship_detections.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
placeholder
13 changes: 7 additions & 6 deletions app/src/components/map/CustomAreaButtons.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ import Feature from 'ol/Feature';
import {
mapState,
} from 'vuex';
// import { getArea } from 'ol/extent';
import Text from 'ol/style/Text';
import { Polygon } from 'ol/geom';
Expand Down Expand Up @@ -244,12 +243,14 @@ export default {
* @param {*} geom OpenLayer geometry
* @returns {Boolean}
*/
isGeometryTooLarge(geom) { // eslint-disable-line
// for now commenting out previous logic
isGeometryTooLarge(geom) {
if (this.mergedConfigsData.length && this.mergedConfigsData[0]?.maxDrawnAreaSide) {
const extent = geom.getExtent();
return extent && (
(extent[3] - extent[1] > this.mergedConfigsData[0]?.maxDrawnAreaSide)
|| (extent[2] - extent[0] > this.mergedConfigsData[0]?.maxDrawnAreaSide));
}
return false;
// const extent = geom.getExtent();
// to do: use more exact turf calculations?
// return extent && (getArea(extent) > 50000000000);
},
onDrawFinished(event) {
const { map } = getMapInstance(this.mapId);
Expand Down
57 changes: 57 additions & 0 deletions app/src/components/map/CustomFeaturesFetchButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<template>
<div class="fetchBtnControls mb-2">
<v-tooltip left>
<template v-slot:activator="{ on }">
<v-btn
:color="$vuetify.theme.currentTheme.background"
class="pa-0 elevation-2 round fetchBtn"
style="min-width: 0"
:loading="isLoadingCustomFeatures"
v-on="on"
@click="$emit('fetchCustomAreaFeatures');"
>
<v-icon>mdi-map-search-outline</v-icon>
</v-btn>
</template>
<span>Get detections</span>
</v-tooltip>
</div>
</template>

<script>
export default {
data() {
return {
isLoadingCustomFeatures: false,
};
},
mounted() {
window.addEventListener(
'set-custom-area-features-loading',
this.customAreaFeaturesLoading,
);
},
methods: {
customAreaFeaturesLoading(e) {
this.isLoadingCustomFeatures = e.detail;
},
},
beforeDestroy() {
window.removeEventListener(
'set-custom-area-features-loading',
this.customAreaFeaturesLoading,
);
},
};
</script>

<style lang="scss">
.fetchBtnControls {
.fetchBtn {
width: 36px;
height: 36px !important;
pointer-events: initial;
}
}
</style>
74 changes: 74 additions & 0 deletions app/src/components/map/DatePickerControl.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<template>
<div class="datePickerControls mb-2">
<v-tooltip v-if="!show" left>
<template v-slot:activator="{ on }">
<v-btn
:color="$vuetify.theme.currentTheme.background"
class="pa-0 elevation-2 round datePickerBtn"
style="min-width: 0"
v-on="on"
@click="show = true"
>
<v-icon>mdi-calendar</v-icon>
</v-btn>
</template>
<span>Choose date</span>
</v-tooltip>
<v-date-picker v-else class="mr-6" elevation="2" width="230" v-model="selectedDate" />
</div>
</template>

<script>
import { VDatePicker } from 'vuetify/lib';
import { DateTime } from 'luxon';
import { getMapInstance } from '@/components/map/map';
export default {
props: {
mapId: String,
},
components: {
VDatePicker,
},
data() {
return {
show: false,
selectedDate: DateTime.now().minus({ days: 7 }).toFormat('yyyy-MM-dd'),
};
},
mounted() {
this.$emit('selectedDate', this.selectedDate);
this.$store.commit('features/SET_SELECTED_DATE', this.selectedDate);
},
watch: {
show(value) {
if (value) {
getMapInstance(this.mapId).map.once('click', () => {
this.show = false;
});
}
},
selectedDate(date) {
this.$emit('selectedDate', date);
this.$store.commit('features/SET_SELECTED_DATE', date);
},
},
};
</script>

<style lang="scss">
.datePickerControls {
.datePickerBtn {
width: 36px;
height: 36px !important;
pointer-events: initial;
}
.v-date-picker-title {
flex-direction: row;
.v-date-picker-title__date {
font-size: 1rem;
}
}
}
</style>
53 changes: 48 additions & 5 deletions app/src/components/map/Map.vue
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,24 @@
:key="dataLayerName + '_customArea'"
:drawnArea.sync="drawnArea"
/>
<DatePickerControl
v-if="loaded && mergedConfigsData.length && mergedConfigsData[0].mapTimeDatepicker"
@selectedDate="setDateFromDatePicker"
class="pointerEvents"
:mapId="mapId"
/>
<SliderControl
v-if="loaded && mergedConfigsData.length && mergedConfigsData[0].sliderConfig"
class="pointerEvents"
:mapId="mapId"
:config="mergedConfigsData[0].sliderConfig"
/>
<CustomFeaturesFetchButton
v-if="loaded && mergedConfigsData.length
&& mergedConfigsData[0].mapTimeDatepicker"
class="pointerEvents"
v-on:fetchCustomAreaFeatures="updateSelectedAreaFeature(true)"
/>
<div
v-if="$route.name !== 'demo'"
class="pointerEvents mt-auto mb-2"
Expand Down Expand Up @@ -180,6 +198,9 @@ import getCluster from '@/components/map/Cluster';
import SpecialLayer from '@/components/map/SpecialLayer.vue';
import LayerSwipe from '@/components/map/LayerSwipe.vue';
import CustomAreaButtons from '@/components/map/CustomAreaButtons.vue';
import DatePickerControl from '@/components/map/DatePickerControl.vue';
import CustomFeaturesFetchButton from '@/components/map/CustomFeaturesFetchButton.vue';
import SliderControl from '@/components/map/SliderControl.vue';
import { getMapInstance } from '@/components/map/map';
import MapOverlay from '@/components/map/MapOverlay.vue';
import IndicatorTimeSelection from '@/components/IndicatorTimeSelection.vue';
Expand All @@ -202,6 +223,7 @@ import { DateTime } from 'luxon';
import SubaoiLayer from '@/components/map/SubaoiLayer.vue';
import DarkOverlayLayer from '@/components/map/DarkOverlayLayer.vue';
import Link from 'ol/interaction/Link';
import { Vector as VectorLayer } from 'ol/layer';
import {
loadIndicatorExternalData,
calculatePadding,
Expand All @@ -222,11 +244,14 @@ export default {
IndicatorTimeSelection,
LayerSwipe,
CustomAreaButtons,
DatePickerControl,
SliderControl,
SubaoiLayer,
MapOverlay,
IframeButton,
AddToDashboardButton,
DarkOverlayLayer,
CustomFeaturesFetchButton,
},
props: {
mapId: {
Expand Down Expand Up @@ -562,11 +587,15 @@ export default {
// redraw all time-dependant layers, if time is passed via WMS params
const area = this.drawnArea;
this.mergedConfigsDataIndexAware.filter(
(config) => config.timeFromProperty || config.usedTimes?.time?.length,
(config) => config.mapTimeDatepicker
|| config.timeFromProperty
|| config.usedTimes?.time?.length,
)
.forEach((config) => {
const layer = layers.find((l) => l.get('name') === config.name);
if (layer) {
if (layer instanceof VectorLayer && config.mapTimeDatepicker) {
// do not fetch new features on time changeempty
} else if (layer) {
updateTimeLayer(layer, config, timeObj.value, area);
}
});
Expand Down Expand Up @@ -795,6 +824,12 @@ export default {
this.$emit('update:center', e);
this.currentCenter = e;
},
setDateFromDatePicker(date) {
this.dataLayerTime = {
name: date,
value: DateTime.fromISO(date),
};
},
updateTime(time, compare) {
// Define a function to update the data layer
// direct match on name
Expand Down Expand Up @@ -967,17 +1002,25 @@ export default {
}
}
},
updateSelectedAreaFeature() {
updateSelectedAreaFeature(manualTrigger = false) {
const { map } = getMapInstance(this.mapId);
const dataGroup = map.getLayers().getArray().find((l) => l.get('id') === 'dataGroup');
const layers = dataGroup.getLayers().getArray();
const area = this.drawnArea;
const time = this.dataLayerTime?.value;
this.mergedConfigsDataIndexAware.filter((config) => config.usedTimes?.time?.length)
this.mergedConfigsDataIndexAware.filter(
(config) => config.mapTimeDatepicker || config.usedTimes?.time?.length,
)
.forEach((config) => {
const layer = layers.find((l) => l.get('name') === config.name);
if (layer) {
updateTimeLayer(layer, config, time, area, 'updateArea');
if (manualTrigger) {
updateTimeLayer(layer, config, time, area, 'updateArea');
} else if (layer instanceof VectorLayer && config.mapTimeDatepicker) {
// do nothing
} else {
updateTimeLayer(layer, config, time, area, 'updateArea');
}
}
});
},
Expand Down
96 changes: 96 additions & 0 deletions app/src/components/map/SliderControl.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<template>
<div class="rangeSliderControls mb-2">
<v-tooltip v-if="!show" left>
<template v-slot:activator="{ on }">
<v-btn
:color="$vuetify.theme.currentTheme.background"
class="pa-0 elevation-2 round rangeSliderBtn"
style="min-width: 0"
v-on="on"
@click="show = true"
>
<v-icon>mdi-dots-horizontal</v-icon>
</v-btn>
</template>
<span>{{config.title}}</span>
</v-tooltip>
<v-card v-else class="sliderContainer">
<v-card-title class="mb-4">
{{config.title}}
</v-card-title>
<v-card-text>
<v-slider
v-model="sliderValue"
:max="config.max || 100"
:min="config.min || 0"
:step="config.step || 1"
thumb-label="always"
track-color="grey"
></v-slider>
</v-card-text>
</v-card>
</div>
</template>

<script>
import { VSlider } from 'vuetify/lib';
import { getMapInstance } from '@/components/map/map';
export default {
props: {
mapId: String,
config: Object,
},
components: {
VSlider,
},
data() {
return {
show: false,
sliderValue: null,
};
},
created() {
if (this.config?.default) {
// initial setup
this.sliderValue = this.config?.default;
this.$store.commit('features/SET_SLIDER_VALUE', this.sliderValue);
}
},
watch: {
show(val) {
if (val) {
getMapInstance(this.mapId).map.once('click', () => {
this.show = false;
});
}
},
sliderValue(val) {
const store = this.$store;
// Check, if at least 300ms have passed since the last change, otherwise we commit
// every small change, which makes the slider slow.
this.lastChangeTimestamp = Date.now();
setTimeout(() => {
const currentTime = Date.now();
if (currentTime - this.lastChangeTimestamp >= 300) {
store.commit('features/SET_SLIDER_VALUE', val);
}
}, 300);
},
},
};
</script>
<style lang="scss">
.rangeSliderControls {
.rangeSliderBtn {
width: 36px;
height: 36px !important;
pointer-events: initial;
}
.sliderContainer {
width: 300px;
}
}
</style>
Loading

0 comments on commit 9e06c04

Please sign in to comment.