Skip to content

Commit 7e8c425

Browse files
authored
Merge pull request #29 from OpenGeoscience/display-configuration
initial configuration models, api
2 parents f8ec533 + 97d7d58 commit 7e8c425

44 files changed

Lines changed: 2128 additions & 54 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

client/src/MapStore.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
ColorFilters,
88
Context,
99
Dataset,
10+
DisplayConfiguration,
1011
LayerCollection,
1112
NetCDFData,
1213
NetCDFLayer,
@@ -33,6 +34,10 @@ export default class MapStore {
3334

3435
public static proModeButtonEnabled = ref(true);
3536

37+
public static displayConfiguration: Ref<DisplayConfiguration> = ref(
38+
{ default_displayed_layers: [], enabled_ui: ['Collections', 'Datasets', 'Metadata'], default_tab: 'Scenarios' },
39+
);
40+
3641
// Ability to toggle proMode so Staff users can see what other users see.
3742
public static proMode = computed(() => MapStore.userIsStaff.value && MapStore.proModeButtonEnabled.value);
3843

@@ -103,10 +108,57 @@ export default class MapStore {
103108
MapStore.mapLayersByDataset[datasetId] = await UVdatApi.getDatasetLayers(datasetId);
104109
}
105110

111+
public static async getDisplayConfiguration(initial = false) {
112+
MapStore.displayConfiguration.value = await UVdatApi.getDisplayConfiguration();
113+
// Loading first time process default map layers
114+
if (initial && MapStore.displayConfiguration.value.default_displayed_layers.length) {
115+
const datasetIds = MapStore.displayConfiguration.value.default_displayed_layers.map((item) => item.dataset_id);
116+
const datasetIdLayers = await UVdatApi.getDatasetsLayers(datasetIds);
117+
const layerByDataset: Record<number, (VectorMapLayer | RasterMapLayer | NetCDFData)[]> = {};
118+
const toggleLayers: (VectorMapLayer | RasterMapLayer | NetCDFLayer)[] = [];
119+
const enabledLayers = MapStore.displayConfiguration.value.default_displayed_layers;
120+
datasetIdLayers.forEach((item) => {
121+
if (item.dataset_id !== undefined) {
122+
if (layerByDataset[item.dataset_id] === undefined) {
123+
layerByDataset[item.dataset_id] = [];
124+
}
125+
layerByDataset[item.dataset_id].push(item);
126+
}
127+
enabledLayers.forEach((enabledLayer) => {
128+
if (item.type === 'netcdf') {
129+
if (enabledLayer.dataset_id === item.dataset_id) {
130+
const netCDFLayers = ((item as NetCDFData).layers);
131+
for (let i = 0; i < netCDFLayers.length; i += 1) {
132+
const layer = netCDFLayers[i];
133+
if (layer.id === enabledLayer.id) {
134+
toggleLayers.push(layer);
135+
}
136+
}
137+
}
138+
} else if (
139+
enabledLayer.type === item.type
140+
&& enabledLayer.id === item.id
141+
&& enabledLayer.dataset_id === item.dataset_id) {
142+
toggleLayers.push(item);
143+
}
144+
});
145+
});
146+
Object.keys(layerByDataset).forEach((datasetIdKey) => {
147+
const datasetId = parseInt(datasetIdKey, 10);
148+
if (!Number.isNaN(datasetId)) {
149+
MapStore.mapLayersByDataset[datasetId] = layerByDataset[datasetId];
150+
}
151+
});
152+
// Now we enable these default layers
153+
return toggleLayers;
154+
}
155+
return [];
156+
}
157+
106158
public static mapLayerFeatureGraphs = computed(() => {
107159
const foundMapLayerFeatureGraphs: { name: string, id: number; graphs: VectorFeatureTableGraph[] }[] = [];
108160
MapStore.selectedVectorMapLayers.value.forEach((item) => {
109-
if (item.default_style.mapLayerFeatureTableGraphs && item.default_style.mapLayerFeatureTableGraphs.length) {
161+
if (item.default_style?.mapLayerFeatureTableGraphs && item.default_style.mapLayerFeatureTableGraphs.length) {
110162
foundMapLayerFeatureGraphs.push({
111163
name: item.name,
112164
id: item.id,
@@ -133,7 +185,7 @@ export default class MapStore {
133185
public static mapLayerVectorSearchable = computed(() => {
134186
const foundMapLayerSearchable: { name: string, id: number; searchSettings: SearchableVectorData }[] = [];
135187
MapStore.selectedVectorMapLayers.value.forEach((item) => {
136-
if (item.default_style.searchableVectorFeatureData) {
188+
if (item.default_style?.searchableVectorFeatureData) {
137189
foundMapLayerSearchable.push({
138190
name: item.name,
139191
id: item.id,

client/src/api/UVDATApi.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import { ref } from 'vue';
33
import OauthClient from '@girder/oauth-client/dist/oauth-client';
44
import {
55
AbstractMapLayer,
6+
AbstractMapLayerListItem,
67
Chart,
78
Context,
89
ContextWithIds,
910
Dataset,
1011
DerivedRegion,
12+
DisplayConfiguration,
1113
FeatureGraphData,
1214
FileItem,
1315
LayerCollection,
@@ -213,7 +215,7 @@ export default class UVdatApi {
213215
return (await UVdatApi.apiClient.delete(`/files/${fileItemId}/`)).data;
214216
}
215217

216-
public static async getGlobalDatasets(filter: { unconnected: boolean }): Promise<(Dataset & { contextCount: number })[]> {
218+
public static async getGlobalDatasets(filter?: { unconnected: boolean }): Promise<(Dataset & { contextCount: number })[]> {
217219
return (await UVdatApi.apiClient.get('datasets', { params: { ...filter } })).data.results;
218220
}
219221

@@ -450,6 +452,12 @@ export default class UVdatApi {
450452
return (await UVdatApi.apiClient.get(`/datasets/${datasetId}/map_layers`)).data;
451453
}
452454

455+
public static async getDatasetsLayers(datasetIds: number[]): Promise<(VectorMapLayer | RasterMapLayer | NetCDFData)[]> {
456+
const params = new URLSearchParams();
457+
datasetIds.forEach((item) => params.append('datasetIds', item.toString()));
458+
return (await UVdatApi.apiClient.get('/datasets/map_layers', { params })).data;
459+
}
460+
453461
public static async getProcessingTasks(): Promise<ProcessingTask[]> {
454462
return (await UVdatApi.apiClient.get('/processing-tasks')).data;
455463
}
@@ -587,7 +595,32 @@ export default class UVdatApi {
587595
return (await UVdatApi.apiClient.get('/map-layers/', { params })).data;
588596
}
589597

598+
public static async getMapLayerAll(): Promise<AbstractMapLayerListItem[]> {
599+
return (await UVdatApi.apiClient.get('/map-layers/all')).data;
600+
}
601+
590602
public static async searchVectorFeatures(requestData: SearchableVectorDataRequest): Promise<SearchableVectorFeatureResponse[]> {
591603
return (await UVdatApi.apiClient.post('/map-layers/search-features/', requestData)).data;
592604
}
605+
606+
public static async getDisplayConfiguration(): Promise<DisplayConfiguration> {
607+
const response = await UVdatApi.apiClient.get('display-configuration/');
608+
return response.data;
609+
}
610+
611+
// Fully update the display configuration (PUT /display_configuration/)
612+
public static async updateDisplayConfiguration(
613+
config: DisplayConfiguration,
614+
): Promise<DisplayConfiguration> {
615+
const response = await UVdatApi.apiClient.put('display-configuration/', config);
616+
return response.data;
617+
}
618+
619+
// Partially update the display configuration (PATCH /display_configuration/)
620+
public static async partialUpdateDisplayConfiguration(
621+
config: Partial<DisplayConfiguration>,
622+
): Promise<DisplayConfiguration> {
623+
const response = await UVdatApi.apiClient.patch('display-configuration/', config);
624+
return response.data;
625+
}
593626
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<script lang="ts">
2+
import {
3+
PropType, Ref, defineComponent, onMounted, ref,
4+
} from 'vue';
5+
import maplibre, { Map } from 'maplibre-gl';
6+
7+
export default defineComponent({
8+
name: 'MapSelection',
9+
props: {
10+
defaultMapSettings: {
11+
type: Object as PropType<{ location: {
12+
center: [number, number];
13+
zoom: number;
14+
} }>,
15+
required: true,
16+
},
17+
},
18+
emits: ['update:settings'],
19+
setup(props, { emit }) {
20+
const mapContainer = ref<HTMLDivElement | null>(null);
21+
const mapInstance: Ref<Map | null> = ref(null);
22+
23+
const mapMove = () => {
24+
if (mapInstance.value) {
25+
const center = mapInstance.value.getCenter();
26+
const zoom = mapInstance.value.getZoom();
27+
emit('update:settings', {
28+
location: {
29+
center: [center.lng, center.lat],
30+
zoom,
31+
},
32+
});
33+
}
34+
};
35+
// Initialize Map
36+
onMounted(() => {
37+
if (!mapContainer.value) return;
38+
mapInstance.value = new maplibre.Map({
39+
container: mapContainer.value,
40+
style: {
41+
version: 8,
42+
sources: {
43+
osm: {
44+
type: 'raster',
45+
tiles: [
46+
'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
47+
'https://b.tile.openstreetmap.org/{z}/{x}/{y}.png',
48+
'https://c.tile.openstreetmap.org/{z}/{x}/{y}.png',
49+
],
50+
tileSize: 256,
51+
attribution: '© OpenStreetMap contributors',
52+
},
53+
},
54+
layers: [
55+
{
56+
id: 'osm-tiles',
57+
type: 'raster',
58+
source: 'osm',
59+
maxzoom: 19,
60+
},
61+
],
62+
},
63+
center: props.defaultMapSettings.location.center,
64+
zoom: props.defaultMapSettings.location.zoom,
65+
});
66+
mapInstance.value.on('load', () => {
67+
if (mapInstance.value) {
68+
mapInstance.value.on('move', mapMove);
69+
}
70+
});
71+
});
72+
73+
return {
74+
mapContainer,
75+
};
76+
},
77+
});
78+
</script>
79+
80+
<template>
81+
<div ref="mapContainer" class="map-container" />
82+
</template>
83+
84+
<style scoped>
85+
.map-container {
86+
width: 100%;
87+
height: 400px;
88+
}
89+
</style>

client/src/components/FeatureSelection/vectorFeatureGraphUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ const renderVectorFeatureGraph = (
230230
g.append('path')
231231
.datum(graph.movingAverage)
232232
.attr('fill', 'none')
233-
.attr('stroke', '#00FFFF')
233+
.attr('stroke', '#FFFF00')
234234
.attr('stroke-width', 5)
235235
.attr('d', line)
236236
.attr('class', `moving-average moving-average-${key}`);

client/src/components/Map.vue

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ export default defineComponent({
6666
6767
const initializeMap = () => {
6868
if (mapContainer.value) {
69+
const center = MapStore.displayConfiguration.value.default_map_settings?.location.center || [-86.1794, 34.8019];
70+
const zoom = MapStore.displayConfiguration.value.default_map_settings?.location.zoom || 6;
6971
map.value = new maplibregl.Map({
7072
container: mapContainer.value,
7173
style: {
@@ -156,8 +158,8 @@ export default defineComponent({
156158
sprite: 'https://maputnik.github.io/osm-liberty/sprites/osm-liberty',
157159
glyphs: 'https://orangemug.github.io/font-glyphs/glyphs/{fontstack}/{range}.pbf',
158160
},
159-
center: [-86.1794, 34.8019], // Coordinates for the relative center of the TVA
160-
zoom: 6, // Initial zoom level
161+
center,
162+
zoom,
161163
});
162164
if (map.value) {
163165
setInternalMap(map as Ref<Map>);
@@ -237,7 +239,8 @@ export default defineComponent({
237239
watch(
238240
MapStore.hoveredFeatures,
239241
() => {
240-
if (map.value && (MapStore.mapLayerFeatureGraphsVisible.value || MapStore.activeSideBarCard.value === 'searchableVectors')) {
242+
if (map.value
243+
&& (MapStore.mapLayerFeatureGraphsVisible.value || MapStore.activeSideBarCard.value === 'searchableVectors')) {
241244
updateSelected(map.value);
242245
}
243246
},

client/src/components/MapLegends/ColorKey.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,11 @@ export default defineComponent({
269269
// D3 allows color strings but says it requires numbers for type definitions
270270
.range(colors);
271271
// Recalculate percentage of width for gradient
272-
const max = domain[domain.length - 1];
273-
const percent = domain.map((item) => (max === 0 ? 0 : item / max));
272+
const min = Math.min(...domain);
273+
const max = Math.max(...domain);
274+
const range = max - min;
275+
276+
const percent = domain.map((item) => (range === 0 ? 0 : (item - min) / range));
274277
// Append multiple color stops using data/enter step
275278
linearGradient.selectAll('stop').remove();
276279
linearGradient

client/src/components/TabularData/MapLayerTableGraph.vue

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,13 @@ export default defineComponent({
3535
3636
const maxMovingAverage = computed(() => {
3737
if (graphData.value) {
38-
if (graphData.value?.graphs && graphData.value.graphs.all) {
39-
const dataLength = graphData.value.graphs.all.data.length;
40-
return Math.floor(dataLength / 4);
38+
if (graphData.value?.graphs) {
39+
const values = Object.values(graphData.value.graphs);
40+
let max = -Infinity;
41+
for (let i = 0; i < values.length; i += 1) {
42+
max = Math.max(max, Math.floor(values[i].data.length / 4));
43+
}
44+
return max;
4145
}
4246
}
4347
return 50;

client/src/components/VectorFeatureSearch/Editor/VectorFeatureSearchEditor.vue

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<!-- eslint-disable vue/max-len -->
12
<script lang="ts">
23
import {
34
Ref, computed, defineComponent, onMounted, ref,
@@ -75,7 +76,7 @@ export default defineComponent({
7576
value: key,
7677
})));
7778
78-
const availableTitleKeys = computed(() => Object.keys(availableProperties.value || {}).map((key) => (getDisplayName(key))));
79+
const availableTitleKeys = computed(() => Object.keys(availableProperties.value || {}).map((key) => ({ title: getDisplayName(key), value: key })));
7980
8081
const availableSubtitleKeys = computed(() => Object.keys(availableProperties.value || {}).map((key) => ({
8182
title: availableProperties.value?.[key]?.displayName || key,
@@ -269,7 +270,13 @@ export default defineComponent({
269270
<strong>Title</strong>
270271
</v-expansion-panel-title>
271272
<v-expansion-panel-text>
272-
<v-select v-model="localData.display.titleKey" :items="availableTitleKeys" label="Title Key" />
273+
<v-select
274+
v-model="localData.display.titleKey"
275+
:items="availableTitleKeys"
276+
item-title="title"
277+
item-value="value"
278+
label="Title Key"
279+
/>
273280
</v-expansion-panel-text>
274281
</v-expansion-panel>
275282
<v-expansion-panel>

client/src/components/VectorFeatureSearch/VectorFeatureSearch.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<!-- eslint-disable vue/max-len -->
12
<!-- eslint-disable vuejs-accessibility/mouse-events-have-key-events -->
23
<!-- eslint-disable vuejs-accessibility/mouse-events-have-key-events -->
34
<script lang="ts">

client/src/map/mapFilters.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import MapStore from '../MapStore';
1818

1919
const getLayerDefaultFilter = (type: AnnotationTypes, layer?: VectorMapLayer) => {
2020
let drawPoints = false;
21-
if (type === 'circle' && layer?.default_style.layers && layer.default_style.layers.line) {
21+
if (type === 'circle' && layer?.default_style?.layers && layer.default_style.layers.line) {
2222
if (layer.default_style.layers.line !== true) {
2323
drawPoints = !!layer.default_style.layers.line.drawPoints;
2424
}

0 commit comments

Comments
 (0)