Skip to content

Commit 7e5facb

Browse files
authored
Merge pull request #33 from OpenGeoscience/global-time
GlobalTime Configuration
2 parents 7e8c425 + 5dcbbf1 commit 7e5facb

20 files changed

Lines changed: 1903 additions & 30 deletions

client/src/MapStore.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
DisplayConfiguration,
1111
LayerCollection,
1212
NetCDFData,
13+
NetCDFImageWorking,
1314
NetCDFLayer,
1415
RasterMapLayer,
1516
SearchableVectorData,
@@ -34,6 +35,8 @@ export default class MapStore {
3435

3536
public static proModeButtonEnabled = ref(true);
3637

38+
public static globalTime = ref(Math.floor(new Date().getTime() / 1000));
39+
3740
public static displayConfiguration: Ref<DisplayConfiguration> = ref(
3841
{ default_displayed_layers: [], enabled_ui: ['Collections', 'Datasets', 'Metadata'], default_tab: 'Scenarios' },
3942
);
@@ -69,6 +72,10 @@ export default class MapStore {
6972

7073
public static visibleMapLayers: Ref<Set<string>> = ref(new Set());
7174

75+
// Net CDF Layers
76+
77+
public static visibleNetCDFLayers: Ref<NetCDFImageWorking[]> = ref([]);
78+
7279
public static selectedVectorMapLayers: Ref<VectorMapLayer[]> = computed(
7380
() => MapStore.selectedMapLayers.value.filter((layer) => layer.type === 'vector'),
7481
);
@@ -171,6 +178,27 @@ export default class MapStore {
171178

172179
public static mapLayerFeatureGraphsVisible = ref(false);
173180

181+
public static vectorFeatureTableGraphVisible = ref(false);
182+
183+
public static vectorFeatureTableData: Ref<{ layerId: number, vectorFeatureId: number, defaultGraphs?: string[] } | null> = ref(null);
184+
185+
public static setVectorFeatureTableData = (layerId: number, vectorFeatureId: number, defaultGraphs?: string[]) => {
186+
if (MapStore.mapLayerFeatureGraphsVisible.value) {
187+
MapStore.mapLayerFeatureGraphsVisible.value = false;
188+
}
189+
MapStore.vectorFeatureTableData.value = {
190+
layerId,
191+
vectorFeatureId,
192+
defaultGraphs,
193+
};
194+
MapStore.vectorFeatureTableGraphVisible.value = true;
195+
};
196+
197+
public static clearVectorFeatureTableData = () => {
198+
MapStore.vectorFeatureTableData.value = null;
199+
MapStore.vectorFeatureTableGraphVisible.value = false;
200+
};
201+
174202
// Graph color mapping implementation
175203
public static enabledMapLayerFeatureColorMapping = ref(false);
176204

@@ -353,4 +381,39 @@ export default class MapStore {
353381
}
354382
}
355383
};
384+
385+
// Graph Charts current Min/Max Values in unix_time
386+
public static graphChartsMinMax = ref({
387+
min: 0,
388+
max: 0,
389+
stepSize: 0,
390+
});
391+
392+
public static timeLinked = ref(true);
393+
394+
public static updateChartsMinMax = (min: number, max: number, stepSize: number) => {
395+
MapStore.graphChartsMinMax.value = { min, max, stepSize };
396+
};
397+
398+
// Computes in Unix Time
399+
public static globalTimeRange: Ref<{ min: number; max: number, stepSize: number }> = computed(() => {
400+
let globalMin = Infinity;
401+
let globalMax = -Infinity;
402+
let stepSize = Infinity;
403+
MapStore.visibleNetCDFLayers.value.forEach((layer) => {
404+
if (layer.sliding) {
405+
const { min, max } = layer.sliding;
406+
const stepsize = layer.images.length;
407+
stepSize = Math.min(stepSize, (max - min) / stepsize);
408+
globalMin = Math.min(globalMin, min);
409+
globalMax = Math.max(globalMax, max);
410+
}
411+
});
412+
if ((MapStore.mapLayerFeatureGraphsVisible.value && MapStore.mapLayerFeatureGraphs.value.length) || MapStore.vectorFeatureTableGraphVisible.value) {
413+
globalMin = Math.min(globalMin, MapStore.graphChartsMinMax.value.min);
414+
globalMax = Math.max(globalMax, MapStore.graphChartsMinMax.value.max);
415+
stepSize = Math.min(stepSize, MapStore.graphChartsMinMax.value.stepSize);
416+
}
417+
return { min: globalMin, max: globalMax, stepSize };
418+
});
356419
}

client/src/api/UVDATApi.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
DerivedRegion,
1212
DisplayConfiguration,
1313
FeatureGraphData,
14+
FeatureGraphs,
15+
FeatureGraphsRequest,
1416
FileItem,
1517
LayerCollection,
1618
LayerCollectionLayer,
@@ -538,6 +540,44 @@ export default class UVdatApi {
538540
return response.data;
539541
}
540542

543+
public static async getFeatureGraphsData(
544+
payload: FeatureGraphsRequest,
545+
): Promise<FeatureGraphs[]> {
546+
const {
547+
tableTypes,
548+
vectorFeatureId,
549+
xAxes = ['index'],
550+
yAxes = ['mean_va'],
551+
indexers = [],
552+
display = ['data', 'trendLine'],
553+
confidenceLevel = 95,
554+
aggregate = false,
555+
movingAverage,
556+
} = payload;
557+
558+
const params = new URLSearchParams();
559+
560+
tableTypes.forEach((type) => params.append('tableType', type));
561+
xAxes.forEach((x) => params.append('xAxis', x));
562+
yAxes.forEach((y) => params.append('yAxis', y));
563+
indexers.forEach((indexer) => params.append('indexer', indexer));
564+
display.forEach((d) => params.append('display', d));
565+
566+
params.append('vectorFeatureId', vectorFeatureId.toString());
567+
params.append('confidenceLevel', confidenceLevel.toString());
568+
params.append('aggregate', aggregate.toString());
569+
if (movingAverage !== undefined) {
570+
params.append('movingAverage', movingAverage.toString());
571+
}
572+
573+
const response = await UVdatApi.apiClient.get<FeatureGraphs[]>(
574+
'/vectorfeature/tabledata/feature-graphs/',
575+
{ params },
576+
);
577+
578+
return response.data;
579+
}
580+
541581
public static async getMapLayerFeatureGraphData(
542582
tableType: string,
543583
mapLayerId: number,

client/src/components/FeatureSelection/VectorFeatureChart.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { throttle } from 'lodash';
66
import UVdatApi from '../../api/UVDATApi';
77
import { FeatureGraphData, VectorFeatureTableGraph } from '../../types';
88
import { renderVectorFeatureGraph } from './vectorFeatureGraphUtils';
9+
import MapStore from '../../MapStore';
910
1011
export default defineComponent({
1112
name: 'FeatureGraph',
@@ -66,7 +67,6 @@ export default defineComponent({
6667
confidenceLevel.value,
6768
false,
6869
movingAverageValue.value,
69-
7070
);
7171
if (data.graphs && Object.keys(data.graphs).length === 0) {
7272
noGraphData.value = true;
@@ -157,6 +157,10 @@ export default defineComponent({
157157
movingAverageValue,
158158
], throttledUpateDialogGraph);
159159
160+
const openBottomGraph = () => {
161+
MapStore.setVectorFeatureTableData(props.mapLayerId, props.vectorFeatureId, [props.graphInfo.name]);
162+
};
163+
160164
return {
161165
graphContainer,
162166
graphDialogContainer,
@@ -171,6 +175,7 @@ export default defineComponent({
171175
movingAverageEnabled,
172176
movingAverageValue,
173177
maxMovingAverage,
178+
openBottomGraph,
174179
};
175180
},
176181
});
@@ -184,7 +189,7 @@ export default defineComponent({
184189
</v-alert>
185190
</div>
186191
<div v-if="graphData">
187-
<v-btn color="primary" size="x-small" @click="openDialog">
192+
<v-btn color="primary" size="x-small" @click="openBottomGraph">
188193
View Larger Graph
189194
</v-btn>
190195
</div>

client/src/components/FeatureSelection/vectorFeatureGraphUtils.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const renderVectorFeatureGraph = (
77
container: SVGSVGElement,
88
options?: {
99
specificGraphKey?: number;
10-
colors?: Record<number, string>;
10+
colors?: Record<number | string, string>;
1111
xAxisLabel?: string;
1212
yAxisLabel?: string;
1313
showXYValuesOnHover?: boolean;
@@ -21,10 +21,11 @@ const renderVectorFeatureGraph = (
2121
showTrendline?: boolean;
2222
showMovingAverage?: boolean;
2323
showConfidenceInterval?: boolean;
24+
useKeyTooltip?: boolean;
2425
},
2526
baseHeight = 400,
2627
) => {
27-
const outputColorMapping: Record<number, string> = {};
28+
const outputColorMapping: Record<number | string, string> = {};
2829
const localContainer = container;
2930
if (!localContainer || !data) return outputColorMapping;
3031

@@ -94,7 +95,12 @@ const renderVectorFeatureGraph = (
9495
const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);
9596

9697
const colorScale = d3.scaleOrdinal(d3.schemeCategory10);
97-
const graphKeys = Object.keys(graphsToRender).map(Number);
98+
const graphKeys = Object.keys(graphsToRender).map((item) => {
99+
if (!Number.isNaN(parseInt(item, 10))) {
100+
return parseInt(item, 10);
101+
}
102+
return item;
103+
});
98104
const tooltipGroup = g.append('g')
99105
.attr('opacity', 0)
100106
.attr('pointer-events', 'none');
@@ -148,7 +154,7 @@ const renderVectorFeatureGraph = (
148154
.on('mousemove', (event) => {
149155
const [mouseX, mouseY] = d3.pointer(event);
150156

151-
const tooltipTextContent = graph.indexer.toString();
157+
const tooltipTextContent = options?.useKeyTooltip ? key.toString() : graph.indexer.toString();
152158
tooltipText.text(tooltipTextContent)
153159
.call((text) => {
154160
// Adjust the background size based on text width

client/src/components/MapLegends/ControlsKey.vue

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
<script lang="ts">
33
import {
44
computed, defineComponent,
5+
watch,
56
} from 'vue';
67
import { throttle } from 'lodash';
8+
import GlobalTime from './GlobalTime.vue';
79
import {
810
AnnotationTypes,
911
NetCDFLayer,
@@ -13,12 +15,15 @@ import {
1315
VectorMapLayer,
1416
} from '../../types'; // Import your defined types
1517
import MapStore from '../../MapStore';
16-
import { updateNetCDFLayer, visibleNetCDFLayers } from '../../map/mapNetCDFLayer';
18+
import { updateNetCDFLayer } from '../../map/mapNetCDFLayer';
1719
import { getRasterLayerDisplayConfig, getVectorLayerDisplayConfig } from '../../utils';
1820
import { updateLayer } from '../../map/mapLayers';
1921
2022
export default defineComponent({
2123
name: 'ControlsKey',
24+
components: {
25+
GlobalTime,
26+
},
2227
props: {
2328
vectorLayers: {
2429
type: Array as () => VectorMapLayer[],
@@ -73,7 +78,7 @@ export default defineComponent({
7378
// Compute NetCDF Layer Keys
7479
const stepIndexMap: Record<string, { length: number, currentIndex: number }> = {};
7580
const resamplingMap: Record<string, 'linear' | 'nearest'> = {};
76-
visibleNetCDFLayers.value.forEach((item) => {
81+
MapStore.visibleNetCDFLayers.value.forEach((item) => {
7782
const found = props.netcdfLayers.find((layer) => layer.id === item.netCDFLayer);
7883
if (found) {
7984
const { opacity } = item;
@@ -123,7 +128,7 @@ export default defineComponent({
123128
124129
const stepMapping = computed(() => {
125130
const mappedStepMapping: Record<string, Record<number, string | number>> = {};
126-
visibleNetCDFLayers.value.forEach((item) => {
131+
MapStore.visibleNetCDFLayers.value.forEach((item) => {
127132
const foundLayer = props.netcdfLayers.find((layer) => layer.id === item.netCDFLayer);
128133
mappedStepMapping[`netcdf_${foundLayer?.id}`] = {};
129134
const mapSlicer: Record<number, string | number> = {};
@@ -174,7 +179,7 @@ export default defineComponent({
174179
});
175180
}
176181
if (item.type === 'netcdf') {
177-
const found = visibleNetCDFLayers.value.find((layer) => item.id === layer.netCDFLayer);
182+
const found = MapStore.visibleNetCDFLayers.value.find((layer) => item.id === layer.netCDFLayer);
178183
if (found) {
179184
found.opacity = val;
180185
updateNetCDFLayer(item.id, { opacity: val });
@@ -194,26 +199,59 @@ export default defineComponent({
194199
};
195200
196201
const toggleResampling = (id: number) => {
197-
const found = visibleNetCDFLayers.value.find((layer) => id === layer.netCDFLayer);
202+
const found = MapStore.visibleNetCDFLayers.value.find((layer) => id === layer.netCDFLayer);
198203
if (found) {
199204
const val = found.resampling === 'linear' ? 'nearest' : 'linear';
200205
found.resampling = val;
201206
updateNetCDFLayer(id, { resampling: val });
202207
}
203208
};
209+
210+
watch(MapStore.globalTime, (newVal) => {
211+
if (!MapStore.timeLinked.value) {
212+
return;
213+
}
214+
props.netcdfLayers.forEach((netcdfLayer) => {
215+
const found = MapStore.visibleNetCDFLayers.value.find((layer) => layer.netCDFLayer === netcdfLayer.id);
216+
if (found) {
217+
// now we need to find the closest index to the current time
218+
const { min } = found.sliding;
219+
const stepSize = found.sliding.step;
220+
const currentIndex = Math.round((newVal - min) / stepSize);
221+
if (currentIndex >= 0 && currentIndex < found.images.length) {
222+
found.currentIndex = currentIndex;
223+
throttledUpdateNetCDFLayer(netcdfLayer.id, currentIndex);
224+
}
225+
}
226+
});
227+
});
228+
229+
const globalTimeEnabled = computed(() => {
230+
let enabled = false;
231+
if (props.netcdfLayers.length > 0) {
232+
enabled = true;
233+
}
234+
return enabled;
235+
});
204236
return {
205237
processedLayers,
206238
iconMapper,
207239
updateOpacity,
208240
throttledUpdateNetCDFLayer,
209241
stepMapping,
210242
toggleResampling,
243+
globalTimeEnabled,
211244
};
212245
},
213246
});
214247
</script>
215248

216249
<template>
250+
<v-card v-if="globalTimeEnabled" class="pa-0 ma-0 mb-2">
251+
<v-card-text class="pa-0 ma-0">
252+
<global-time />
253+
</v-card-text>
254+
</v-card>
217255
<v-card
218256
v-for="(item, index) in processedLayers"
219257
:key="`opacity_${index}`"

0 commit comments

Comments
 (0)