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

Integrate Ships-detection indicator on-the-fly integration #2349

Merged
merged 14 commits into from
Apr 8, 2024
Merged
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 @@ -965,17 +1000,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
Loading