Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 54 additions & 2 deletions client/src/MapStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ColorFilters,
Context,
Dataset,
DisplayConfiguration,
LayerCollection,
NetCDFData,
NetCDFLayer,
Expand All @@ -33,6 +34,10 @@ export default class MapStore {

public static proModeButtonEnabled = ref(true);

public static displayConfiguration: Ref<DisplayConfiguration> = ref(
{ default_displayed_layers: [], enabled_ui: ['Collections', 'Datasets', 'Metadata'], default_tab: 'Scenarios' },
);

// Ability to toggle proMode so Staff users can see what other users see.
public static proMode = computed(() => MapStore.userIsStaff.value && MapStore.proModeButtonEnabled.value);

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

public static async getDisplayConfiguration(initial = false) {
MapStore.displayConfiguration.value = await UVdatApi.getDisplayConfiguration();
// Loading first time process default map layers
if (initial && MapStore.displayConfiguration.value.default_displayed_layers.length) {
const datasetIds = MapStore.displayConfiguration.value.default_displayed_layers.map((item) => item.dataset_id);
const datasetIdLayers = await UVdatApi.getDatasetsLayers(datasetIds);
const layerByDataset: Record<number, (VectorMapLayer | RasterMapLayer | NetCDFData)[]> = {};
const toggleLayers: (VectorMapLayer | RasterMapLayer | NetCDFLayer)[] = [];
const enabledLayers = MapStore.displayConfiguration.value.default_displayed_layers;
datasetIdLayers.forEach((item) => {
if (item.dataset_id !== undefined) {
if (layerByDataset[item.dataset_id] === undefined) {
layerByDataset[item.dataset_id] = [];
}
layerByDataset[item.dataset_id].push(item);
}
enabledLayers.forEach((enabledLayer) => {
if (item.type === 'netcdf') {
if (enabledLayer.dataset_id === item.dataset_id) {
const netCDFLayers = ((item as NetCDFData).layers);
for (let i = 0; i < netCDFLayers.length; i += 1) {
const layer = netCDFLayers[i];
if (layer.id === enabledLayer.id) {
toggleLayers.push(layer);
}
}
}
} else if (
enabledLayer.type === item.type
&& enabledLayer.id === item.id
&& enabledLayer.dataset_id === item.dataset_id) {
toggleLayers.push(item);
}
});
});
Object.keys(layerByDataset).forEach((datasetIdKey) => {
const datasetId = parseInt(datasetIdKey, 10);
if (!Number.isNaN(datasetId)) {
MapStore.mapLayersByDataset[datasetId] = layerByDataset[datasetId];
}
});
// Now we enable these default layers
return toggleLayers;
}
return [];
}

public static mapLayerFeatureGraphs = computed(() => {
const foundMapLayerFeatureGraphs: { name: string, id: number; graphs: VectorFeatureTableGraph[] }[] = [];
MapStore.selectedVectorMapLayers.value.forEach((item) => {
if (item.default_style.mapLayerFeatureTableGraphs && item.default_style.mapLayerFeatureTableGraphs.length) {
if (item.default_style?.mapLayerFeatureTableGraphs && item.default_style.mapLayerFeatureTableGraphs.length) {
foundMapLayerFeatureGraphs.push({
name: item.name,
id: item.id,
Expand All @@ -133,7 +185,7 @@ export default class MapStore {
public static mapLayerVectorSearchable = computed(() => {
const foundMapLayerSearchable: { name: string, id: number; searchSettings: SearchableVectorData }[] = [];
MapStore.selectedVectorMapLayers.value.forEach((item) => {
if (item.default_style.searchableVectorFeatureData) {
if (item.default_style?.searchableVectorFeatureData) {
foundMapLayerSearchable.push({
name: item.name,
id: item.id,
Expand Down
35 changes: 34 additions & 1 deletion client/src/api/UVDATApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { ref } from 'vue';
import OauthClient from '@girder/oauth-client/dist/oauth-client';
import {
AbstractMapLayer,
AbstractMapLayerListItem,
Chart,
Context,
ContextWithIds,
Dataset,
DerivedRegion,
DisplayConfiguration,
FeatureGraphData,
FileItem,
LayerCollection,
Expand Down Expand Up @@ -213,7 +215,7 @@ export default class UVdatApi {
return (await UVdatApi.apiClient.delete(`/files/${fileItemId}/`)).data;
}

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

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

public static async getDatasetsLayers(datasetIds: number[]): Promise<(VectorMapLayer | RasterMapLayer | NetCDFData)[]> {
const params = new URLSearchParams();
datasetIds.forEach((item) => params.append('datasetIds', item.toString()));
return (await UVdatApi.apiClient.get('/datasets/map_layers', { params })).data;
}

public static async getProcessingTasks(): Promise<ProcessingTask[]> {
return (await UVdatApi.apiClient.get('/processing-tasks')).data;
}
Expand Down Expand Up @@ -587,7 +595,32 @@ export default class UVdatApi {
return (await UVdatApi.apiClient.get('/map-layers/', { params })).data;
}

public static async getMapLayerAll(): Promise<AbstractMapLayerListItem[]> {
return (await UVdatApi.apiClient.get('/map-layers/all')).data;
}

public static async searchVectorFeatures(requestData: SearchableVectorDataRequest): Promise<SearchableVectorFeatureResponse[]> {
return (await UVdatApi.apiClient.post('/map-layers/search-features/', requestData)).data;
}

public static async getDisplayConfiguration(): Promise<DisplayConfiguration> {
const response = await UVdatApi.apiClient.get('display-configuration/');
return response.data;
}

// Fully update the display configuration (PUT /display_configuration/)
public static async updateDisplayConfiguration(
config: DisplayConfiguration,
): Promise<DisplayConfiguration> {
const response = await UVdatApi.apiClient.put('display-configuration/', config);
return response.data;
}

// Partially update the display configuration (PATCH /display_configuration/)
public static async partialUpdateDisplayConfiguration(
config: Partial<DisplayConfiguration>,
): Promise<DisplayConfiguration> {
const response = await UVdatApi.apiClient.patch('display-configuration/', config);
return response.data;
}
}
89 changes: 89 additions & 0 deletions client/src/components/Admin/MapSelection.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<script lang="ts">
import {
PropType, Ref, defineComponent, onMounted, ref,
} from 'vue';
import maplibre, { Map } from 'maplibre-gl';

export default defineComponent({
name: 'MapSelection',
props: {
defaultMapSettings: {
type: Object as PropType<{ location: {
center: [number, number];
zoom: number;
} }>,
required: true,
},
},
emits: ['update:settings'],
setup(props, { emit }) {
const mapContainer = ref<HTMLDivElement | null>(null);
const mapInstance: Ref<Map | null> = ref(null);

const mapMove = () => {
if (mapInstance.value) {
const center = mapInstance.value.getCenter();
const zoom = mapInstance.value.getZoom();
emit('update:settings', {
location: {
center: [center.lng, center.lat],
zoom,
},
});
}
};
// Initialize Map
onMounted(() => {
if (!mapContainer.value) return;
mapInstance.value = new maplibre.Map({
container: mapContainer.value,
style: {
version: 8,
sources: {
osm: {
type: 'raster',
tiles: [
'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
'https://b.tile.openstreetmap.org/{z}/{x}/{y}.png',
'https://c.tile.openstreetmap.org/{z}/{x}/{y}.png',
],
tileSize: 256,
attribution: '© OpenStreetMap contributors',
},
},
layers: [
{
id: 'osm-tiles',
type: 'raster',
source: 'osm',
maxzoom: 19,
},
],
},
center: props.defaultMapSettings.location.center,
zoom: props.defaultMapSettings.location.zoom,
});
mapInstance.value.on('load', () => {
if (mapInstance.value) {
mapInstance.value.on('move', mapMove);
}
});
});

return {
mapContainer,
};
},
});
</script>

<template>
<div ref="mapContainer" class="map-container" />
</template>

<style scoped>
.map-container {
width: 100%;
height: 400px;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ const renderVectorFeatureGraph = (
g.append('path')
.datum(graph.movingAverage)
.attr('fill', 'none')
.attr('stroke', '#00FFFF')
.attr('stroke', '#FFFF00')
.attr('stroke-width', 5)
.attr('d', line)
.attr('class', `moving-average moving-average-${key}`);
Expand Down
9 changes: 6 additions & 3 deletions client/src/components/Map.vue
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ export default defineComponent({

const initializeMap = () => {
if (mapContainer.value) {
const center = MapStore.displayConfiguration.value.default_map_settings?.location.center || [-86.1794, 34.8019];
const zoom = MapStore.displayConfiguration.value.default_map_settings?.location.zoom || 6;
map.value = new maplibregl.Map({
container: mapContainer.value,
style: {
Expand Down Expand Up @@ -156,8 +158,8 @@ export default defineComponent({
sprite: 'https://maputnik.github.io/osm-liberty/sprites/osm-liberty',
glyphs: 'https://orangemug.github.io/font-glyphs/glyphs/{fontstack}/{range}.pbf',
},
center: [-86.1794, 34.8019], // Coordinates for the relative center of the TVA
zoom: 6, // Initial zoom level
center,
zoom,
});
if (map.value) {
setInternalMap(map as Ref<Map>);
Expand Down Expand Up @@ -237,7 +239,8 @@ export default defineComponent({
watch(
MapStore.hoveredFeatures,
() => {
if (map.value && (MapStore.mapLayerFeatureGraphsVisible.value || MapStore.activeSideBarCard.value === 'searchableVectors')) {
if (map.value
&& (MapStore.mapLayerFeatureGraphsVisible.value || MapStore.activeSideBarCard.value === 'searchableVectors')) {
updateSelected(map.value);
}
},
Expand Down
7 changes: 5 additions & 2 deletions client/src/components/MapLegends/ColorKey.vue
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,11 @@ export default defineComponent({
// D3 allows color strings but says it requires numbers for type definitions
.range(colors);
// Recalculate percentage of width for gradient
const max = domain[domain.length - 1];
const percent = domain.map((item) => (max === 0 ? 0 : item / max));
const min = Math.min(...domain);
const max = Math.max(...domain);
const range = max - min;

const percent = domain.map((item) => (range === 0 ? 0 : (item - min) / range));
// Append multiple color stops using data/enter step
linearGradient.selectAll('stop').remove();
linearGradient
Expand Down
10 changes: 7 additions & 3 deletions client/src/components/TabularData/MapLayerTableGraph.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ export default defineComponent({

const maxMovingAverage = computed(() => {
if (graphData.value) {
if (graphData.value?.graphs && graphData.value.graphs.all) {
const dataLength = graphData.value.graphs.all.data.length;
return Math.floor(dataLength / 4);
if (graphData.value?.graphs) {
const values = Object.values(graphData.value.graphs);
let max = -Infinity;
for (let i = 0; i < values.length; i += 1) {
max = Math.max(max, Math.floor(values[i].data.length / 4));
}
return max;
}
}
return 50;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<!-- eslint-disable vue/max-len -->
<script lang="ts">
import {
Ref, computed, defineComponent, onMounted, ref,
Expand Down Expand Up @@ -75,7 +76,7 @@
value: key,
})));

const availableTitleKeys = computed(() => Object.keys(availableProperties.value || {}).map((key) => (getDisplayName(key))));
const availableTitleKeys = computed(() => Object.keys(availableProperties.value || {}).map((key) => ({ title: getDisplayName(key), value: key })));

const availableSubtitleKeys = computed(() => Object.keys(availableProperties.value || {}).map((key) => ({
title: availableProperties.value?.[key]?.displayName || key,
Expand Down Expand Up @@ -248,7 +249,7 @@
<v-expansion-panel-text>
<p>Filters will be implmented in the future</p>
<VectorFeatureSearchFilterItem
v-if="false"

Check warning on line 252 in client/src/components/VectorFeatureSearch/Editor/VectorFeatureSearchEditor.vue

View workflow job for this annotation

GitHub Actions / lint-client

Unexpected constant condition
v-model="localData.configurableFilters"
:available-property-keys="availablePropertyKeys"
/>
Expand All @@ -269,7 +270,13 @@
<strong>Title</strong>
</v-expansion-panel-title>
<v-expansion-panel-text>
<v-select v-model="localData.display.titleKey" :items="availableTitleKeys" label="Title Key" />
<v-select
v-model="localData.display.titleKey"
:items="availableTitleKeys"
item-title="title"
item-value="value"
label="Title Key"
/>
</v-expansion-panel-text>
</v-expansion-panel>
<v-expansion-panel>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<!-- eslint-disable vue/max-len -->
<!-- eslint-disable vuejs-accessibility/mouse-events-have-key-events -->
<!-- eslint-disable vuejs-accessibility/mouse-events-have-key-events -->
<script lang="ts">
Expand Down Expand Up @@ -183,7 +184,7 @@
</template>
</v-tooltip>
<!-- Eventual Filters added to the system -->
<v-tooltip v-if="false" text="View metadata filters">

Check warning on line 187 in client/src/components/VectorFeatureSearch/VectorFeatureSearch.vue

View workflow job for this annotation

GitHub Actions / lint-client

Unexpected constant condition
<template #activator="{ props }">
<v-icon :color="showFilters ? 'primary' : ''" v-bind="props" @click="showFilters = !showFilters">
mdi-filter
Expand Down
2 changes: 1 addition & 1 deletion client/src/map/mapFilters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import MapStore from '../MapStore';

const getLayerDefaultFilter = (type: AnnotationTypes, layer?: VectorMapLayer) => {
let drawPoints = false;
if (type === 'circle' && layer?.default_style.layers && layer.default_style.layers.line) {
if (type === 'circle' && layer?.default_style?.layers && layer.default_style.layers.line) {
if (layer.default_style.layers.line !== true) {
drawPoints = !!layer.default_style.layers.line.drawPoints;
}
Expand Down
2 changes: 1 addition & 1 deletion client/src/map/mapVectorLayers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ const toggleVectorMapLayers = (map: maplibregl.Map) => {
if (MapStore.visibleMapLayers.value.has(`${layer.type}_${layer.id}`)) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
updateVectorLayer(layer);
if (layer?.default_style.searchableVectorFeatureData) {
if (layer?.default_style?.searchableVectorFeatureData) {
if (layer.default_style.searchableVectorFeatureData.display.autoOpenSideBar) {
if (MapStore.activeSideBarCard.value !== 'searchableVectors') {
MapStore.toggleContext('searchableVectors');
Expand Down
7 changes: 7 additions & 0 deletions client/src/router/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { RouterOptions, createWebHistory } from 'vue-router';
import HomePage from '../views/HomePage.vue';
import DisplayAdmin from '../views/Admin/DisplayAdmin.vue';

function makeOptions(): RouterOptions {
return {
Expand All @@ -10,6 +11,12 @@ function makeOptions(): RouterOptions {
// component: HomePage,
component: HomePage,
},
{
path: '/admin',
// component: HomePage,
component: DisplayAdmin,
},

],
};
}
Expand Down
Loading