Skip to content

Commit 97d7d58

Browse files
committed
adding default map position settings
1 parent 43fee0b commit 97d7d58

File tree

9 files changed

+141
-6
lines changed

9 files changed

+141
-6
lines changed
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/Map.vue

Lines changed: 4 additions & 2 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>);

client/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,7 @@ export interface DisplayConfiguration {
906906
enabled_ui: ('Scenarios' | 'Collections' | 'Datasets' | 'Metadata')[];
907907
default_tab: 'Scenarios' | 'Collections' | 'Datasets' | 'Metadata';
908908
default_displayed_layers: Array<{ type: AbstractMapLayer['type']; id: number; dataset_id: number; name: string }>;
909+
default_map_settings?: { location: { center: [number, number], zoom: number } };
909910
}
910911

911912
export interface AbstractMapLayerListItem {

client/src/views/Admin/DisplayAdmin.vue

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@ import {
66
AbstractMapLayerListItem, DisplayConfiguration,
77
} from '../../types';
88
import UVdatApi from '../../api/UVDATApi';
9+
import MapSelection from '../../components/Admin/MapSelection.vue';
910
1011
export default defineComponent({
1112
name: 'DisplayConfigurationEditor',
13+
components: {
14+
MapSelection,
15+
},
1216
setup() {
1317
const config: Ref<DisplayConfiguration | null> = ref(null);
1418
const enabledUiOptions = ['Scenarios', 'Collections', 'Datasets', 'Metadata'];
@@ -31,7 +35,11 @@ export default defineComponent({
3135
const availableLayers = computed(() => {
3236
if (layers.value) {
3337
return layers.value.map((item) => ({
34-
id: item.id, name: item.name, dataset_id: item.dataset_id, type: item.type, index: `${item.type}_${item.id}_${item.dataset_id}`,
38+
id: item.id,
39+
name: item.name,
40+
dataset_id: item.dataset_id,
41+
type: item.type,
42+
index: `${item.type}_${item.id}_${item.dataset_id}`,
3543
}));
3644
}
3745
return [];
@@ -57,6 +65,12 @@ export default defineComponent({
5765
}
5866
};
5967
68+
const updateDefaultMapSettings = (settings: { location: { center: [number, number]; zoom: number; }; }) => {
69+
if (config.value) {
70+
config.value.default_map_settings = settings;
71+
}
72+
};
73+
6074
return {
6175
config,
6276
enabledUiOptions,
@@ -65,6 +79,7 @@ export default defineComponent({
6579
snackbar,
6680
availableLayers,
6781
selectedLayers,
82+
updateDefaultMapSettings,
6883
};
6984
},
7085
});
@@ -114,6 +129,10 @@ export default defineComponent({
114129
</v-list-item>
115130
</template>
116131
</v-select>
132+
<map-selection
133+
:default-map-settings="config?.default_map_settings || { location: { center: [-86.1794, 34.8019], zoom: 6 } }"
134+
@update:settings="updateDefaultMapSettings($event)"
135+
/>
117136
</v-card-text>
118137
<v-card-actions>
119138
<v-btn color="primary" @click="saveConfig">

client/src/views/HomePage.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export default defineComponent({
3232
},
3333
setup() {
3434
const oauthClient = inject<OAuthClient>('oauthClient');
35+
const loading = ref(false);
3536
const drawerOpen = ref(true);
3637
if (oauthClient === undefined) {
3738
throw new Error('Must provide "oauthClient" into component.');
@@ -49,7 +50,9 @@ export default defineComponent({
4950
};
5051
5152
onMounted(async () => {
53+
loading.value = true;
5254
const layers = await MapStore.getDisplayConfiguration(true);
55+
loading.value = false;
5356
layers.forEach((layer) => toggleLayerSelection(layer));
5457
});
5558
@@ -149,6 +152,7 @@ export default defineComponent({
149152
activeSideBar: MapStore.activeSideBarCard,
150153
rightSideBarPadding,
151154
SideBarHasData,
155+
loading,
152156
};
153157
},
154158
});
@@ -313,7 +317,7 @@ export default defineComponent({
313317
>
314318
<source-selection />
315319
</v-navigation-drawer>
316-
<v-row dense class="fill-height">
320+
<v-row v-if="!loading" dense class="fill-height">
317321
<v-col class="d-flex flex-column fill-height" style="min-height: 90vh">
318322
<MapVue />
319323
<MapLayerTableGraph v-if="mapLayerVectorGraphsVisible && mapLayerFeatureGraphs.length" />

uvdat/core/admin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ class DisplayConfigurationAdmin(admin.ModelAdmin):
218218
'enabled_ui',
219219
'default_tab',
220220
'default_displayed_layers',
221+
'default_map_settings',
221222
]
222223

223224

uvdat/core/migrations/0004_displayconfiguration.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Generated by Django 5.0.7 on 2025-03-31 17:03
1+
# Generated by Django 5.0.7 on 2025-04-09 13:07
22

33
from django.db import migrations, models
44

@@ -46,6 +46,15 @@ class Migration(migrations.Migration):
4646
help_text="List of map_layers enabled: [{type: 'netcdf', id: 1}. {type: 'vector', id: 3}, {type: 'raster', id: 4}]",
4747
),
4848
),
49+
(
50+
'default_map_settings',
51+
models.JSONField(
52+
blank=True,
53+
default=None,
54+
help_text='{location: { center: [x, y], zoom: 5 }}',
55+
null=True,
56+
),
57+
),
4958
],
5059
),
5160
]

uvdat/core/models/display_configuration.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ class DisplayConfiguration(models.Model):
2525
help_text="List of map_layers enabled: [{type: 'netcdf', id: 1}. {type: 'vector', id: 3}, {type: 'raster', id: 4}]",
2626
)
2727

28+
default_map_settings = models.JSONField(
29+
blank=True,
30+
null=True,
31+
default=None,
32+
help_text='{location: { center: [x, y], zoom: 5 }}',
33+
)
34+
2835
def clean(self):
2936
"""Ensure default_tab is within enabled_features."""
3037
super().clean()

uvdat/core/rest/display_configuration.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,13 @@ class DisplayConfigurationSerializer(serializers.ModelSerializer):
2525
help_text='Default tab, must be one of the enabled features.'
2626
)
2727
default_displayed_layers = serializers.ListField(child=LayerSerializer())
28+
default_map_settings = serializers.JSONField(
29+
help_text='Map settings, e.g., {"location": {"center": [x, y], "zoom": 5}}.'
30+
)
2831

2932
class Meta:
3033
model = DisplayConfiguration
31-
fields = ['enabled_ui', 'default_tab', 'default_displayed_layers']
34+
fields = ['enabled_ui', 'default_tab', 'default_displayed_layers', 'default_map_settings']
3235

3336
def validate(self, data: Dict[str, Any]) -> Dict[str, Any]:
3437
enabled_ui = data.get('enabled_ui', [])

0 commit comments

Comments
 (0)