Skip to content

Commit

Permalink
initial vector feature table rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
BryonLewis committed Feb 12, 2025
1 parent 59b1389 commit 9ac601a
Show file tree
Hide file tree
Showing 13 changed files with 887 additions and 61 deletions.
27 changes: 27 additions & 0 deletions client/src/api/UVDATApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ContextWithIds,
Dataset,
DerivedRegion,
FeatureGraphData,
FileItem,
LayerCollection,
LayerCollectionLayer,
Expand All @@ -21,6 +22,7 @@ import {
RasterData,
RasterMapLayer,
SimulationType,
TableSummary,
VectorMapLayer,
} from '../types';

Expand Down Expand Up @@ -494,4 +496,29 @@ export default class UVdatApi {
public static async deleteContext(contextId: number): Promise<{ detail: string }> {
return (await UVdatApi.apiClient.delete(`/contexts/${contextId}/`)).data;
}

public static async getVectorTableSummary(layerId: number): Promise<TableSummary> {
return (await UVdatApi.apiClient.get('/vectorfeature/tabledata/table-summary/', { params: { layerId } })).data;
}

public static async getFeatureGraphData(
tableType: string,
vectorFeatureId: number,
xAxis: string = 'index',
yAxis: string = 'mean_va',
filter?: string,
filterVal?: string,
): Promise<FeatureGraphData> {
const response = await UVdatApi.apiClient.get('/vectorfeature/tabledata/feature-graph/', {
params: {
tableType,
vectorFeatureId,
xAxis,
yAxis,
filter,
filterVal,
},
});
return response.data;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { drawBarChart } from '../Metadata/drawChart';
// FeatureChart TypeScript interface (as provided)
export default defineComponent({
name: 'RenderFeatureChart',
name: 'PropertyFeatureChart',
props: {
featureChart: {
type: Object as PropType<FeatureChartWithData>,
Expand Down
37 changes: 33 additions & 4 deletions client/src/components/FeatureSelection/SelectedFeature.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import {
PropType, Ref, computed, defineComponent, onMounted, ref,
} from 'vue';
import MapStore from '../../MapStore';
import { ClickedProps, FeatureChartWithData } from '../../types';
import { ClickedProps, FeatureChartWithData, VectorFeatureTableGraph } from '../../types';
import { colorGenerator } from '../../map/mapColors';
import RenderFeatureChart from './RenderFeatureChart.vue';
import PropertyFeatureChart from './PropertyFeatureChart.vue';
import VectorFeatureChart from './VectorFeatureChart.vue';
export default defineComponent({
components: {
RenderFeatureChart,
PropertyFeatureChart,
VectorFeatureChart,
},
props: {
data: {
Expand All @@ -28,6 +30,8 @@ export default defineComponent({
const mapLayerId = ref(props.data.layerId);
const featureId = ref(props.data.id as number);
const enabledChartPanels: Ref<number[]> = ref([]);
const enabledVectorChartPanel: Ref<number[]> = ref([]);
const vectorGraphs: Ref<VectorFeatureTableGraph[]> = ref([]);
const processLayerProps = () => {
const found = MapStore.selectedVectorMapLayers.value.find((item) => item.id === props.data.layerId);
if (found?.default_style.properties) {
Expand Down Expand Up @@ -65,6 +69,9 @@ export default defineComponent({
});
}
}
if (found?.default_style.vectorFeatureTableGraphs) {
vectorGraphs.value = found.default_style.vectorFeatureTableGraphs;
}
};
onMounted(() => processLayerProps());
Expand All @@ -81,8 +88,10 @@ export default defineComponent({
featureId,
featureCharts,
enabledChartPanels,
enabledVectorChartPanel,
selectedFeatureExpanded,
toggleFeatureExpaned,
vectorGraphs,
};
},
});
Expand Down Expand Up @@ -137,7 +146,24 @@ export default defineComponent({
</v-icon> {{ featureChart.name }}
</v-expansion-panel-title>
<v-expansion-panel-text>
<render-feature-chart :feature-chart="featureChart" />
<property-feature-chart :feature-chart="featureChart" />
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
<v-expansion-panels v-if="vectorGraphs.length" v-model="enabledVectorChartPanel" multiple>
<v-expansion-panel
v-for="vectorGraph, index in vectorGraphs"
:key="`${vectorGraph.name}_${index}`"
:value="index"
class="ma-0 pa-0"
>
<v-expansion-panel-title>
<v-icon class="pr-2">
mdi-chart-line
</v-icon> {{ vectorGraph.name }}
</v-expansion-panel-title>
<v-expansion-panel-text class="ma-0">
<vector-feature-chart :graph-info="vectorGraph" :vector-feature-id="featureId" />
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
Expand All @@ -146,4 +172,7 @@ export default defineComponent({
</template>

<style scoped>
.v-expansion-panel-text>>> .v-expansion-panel-text__wrapper {
padding: 8px !important;
}
</style>
200 changes: 200 additions & 0 deletions client/src/components/FeatureSelection/VectorFeatureChart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
<script lang="ts">
import {
PropType, defineComponent, nextTick, ref, watch,
} from 'vue';
import * as d3 from 'd3';
import UVdatApi from '../../api/UVDATApi';
import { FeatureGraphData, VectorFeatureTableGraph } from '../../types';
type ParameterGraph = Record<string, {
data: [number, number][];
filterVal: string;
}>;
export default defineComponent({
name: 'FeatureGraph',
props: {
graphInfo: {
type: Object as PropType<VectorFeatureTableGraph>,
required: true,
},
vectorFeatureId: {
type: Number as PropType<number>,
required: true,
},
},
setup(props) {
const graphContainer = ref<SVGSVGElement | null>(null);
const graphDialogContainer = ref<SVGSVGElement | null>(null);
const graphData = ref<FeatureGraphData | null>(null);
const dialogVisible = ref(false);
// Fetch feature graph data when component is mounted or props change
const fetchFeatureGraphData = async () => {
try {
const data = await UVdatApi.getFeatureGraphData(
props.graphInfo.type, // Use graphInfo.type (tableType) instead of mapLayerId
props.vectorFeatureId,
props.graphInfo.xAxis,
props.graphInfo.yAxis,
props.graphInfo.filterColumn,
props.graphInfo.filterValue,
);
graphData.value = data;
renderGraph(data);

Check failure on line 44 in client/src/components/FeatureSelection/VectorFeatureChart.vue

View workflow job for this annotation

GitHub Actions / lint-client

'renderGraph' was used before it was defined
} catch (error) {
// eslint-disable-next-line no-console
console.error('Error fetching feature graph data:', error);
}
};
// Render graph using D3
const renderGraph = (data: FeatureGraphData, container = 'graphContainer') => {
const localContainer = container === 'graphContainer' ? graphContainer : graphDialogContainer;
if (!localContainer.value || !data) return;
const svg = d3.select(localContainer.value);
svg.selectAll('*').remove(); // Clear any existing content in the SVG
const margin = {
top: 20, right: container === 'graphContainer' ? 0 : 20, bottom: 40, left: 40,
};
// Set the maximum width to 250px
const width = localContainer.value?.clientWidth || 250 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
const x = d3.scaleLinear().range([0, width]);
const y = d3.scaleLinear().range([height, 0]);
const line = d3.line()
.x((d: [number, number]) => x(d[0]))
.y((d: [number, number]) => y(d[1]));
let dataForGraph: { data: [number, number][], filterVal?: string } | undefined;
// Check for default data or apply filter if necessary
if (data.graphs.default) {
dataForGraph = data.graphs.default; // Use default data if no filter
} else if (props.graphInfo.filterValue && props.graphInfo.filterColumn) {
const filteredData = data.graphs as ParameterGraph;
dataForGraph = filteredData[props.graphInfo.filterValue]; // Apply filter by value
}
if (!dataForGraph) {
return; // Return if no data is available
}
// Set the domain for the axes, ensuring we handle empty arrays correctly
const xExtent = d3.extent(dataForGraph.data.map((item) => item[0]));
const yExtent = d3.extent(dataForGraph.data.map((item) => item[1]));
// Fallback to zero if data is empty
x.domain(xExtent[0] !== undefined ? xExtent : [0, 1]);
y.domain(yExtent[0] !== undefined ? yExtent : [0, 1]);
// Create the graph container
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// Add the line path to the graph
svg.append('path')
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 1.5)
.attr('d', line(dataForGraph.data.sort((a, b) => a[0] - b[0])))
.attr('transform', `translate(${margin.left},${margin.top})`);
// Add the X-axis
g.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x).ticks(5));
// Add the Y-axis
g.append('g')
.attr('class', 'y axis')
.call(d3.axisLeft(y));
};
// Watch for prop changes and refetch data
watch(
() => [props.graphInfo, props.vectorFeatureId],
() => fetchFeatureGraphData(),
{ immediate: true },
);
// Open the dialog to display a larger graph
const openDialog = () => {
dialogVisible.value = true;
nextTick(() => {
if (graphData.value) {
renderGraph(graphData.value, 'graphDialogContainer');
}
});
};
return {
graphContainer,
graphDialogContainer,
graphData,
dialogVisible,
openDialog,
};
},
});
</script>

<template>
<div>
<!-- Button to open dialog -->
<v-btn color="primary" @click="openDialog">
View Larger Graph
</v-btn>

<!-- Graph container -->
<svg ref="graphContainer" width="100%" height="400" class="selectedFeatureSVG" />

<!-- Dialog for larger chart -->
<v-dialog v-model="dialogVisible" max-width="800px">
<v-card>
<v-card-title>
<span class="headline">Feature Graph</span>
</v-card-title>
<v-card-text>
<svg ref="graphDialogContainer" width="100%" height="500" />
</v-card-text>
<v-card-actions>
<v-btn @click="dialogVisible = false">
Close
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>

<style scoped>
.selectedFeatureSVG {
max-width: 250px;
width: 100%;
height: auto;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5;
}
.axis path,
.axis line {
fill: none;
shape-rendering: crispEdges;
}
.x.axis text,
.y.axis text {
font-size: 12px;
}
</style>
6 changes: 3 additions & 3 deletions client/src/components/Metadata/FeatureChartEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import { FeatureChart } from '../../types';
import MapStore from '../../MapStore';
import { drawBarChart } from './drawChart'; // Separate drawChart function
import { colorGenerator } from '../../map/mapColors';
import RenderFeatureChart from '../FeatureSelection/RenderFeatureChart.vue';
import PropertyFeatureChart from '../FeatureSelection/PropertyFeatureChart.vue';
export default defineComponent({
name: 'FeatureChartEditor',
components: {
draggable,
RenderFeatureChart,
PropertyFeatureChart,
},
props: {
layerId: {
Expand Down Expand Up @@ -278,7 +278,7 @@ export default defineComponent({
</v-icon> {{ chart.name }}
</v-expansion-panel-title>
<v-expansion-panel-text>
<render-feature-chart :feature-chart="{ ...chart, data: generatedData }" :max-width="500" />
<property-feature-chart :feature-chart="{ ...chart, data: generatedData }" :max-width="500" />
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
Expand Down
Loading

0 comments on commit 9ac601a

Please sign in to comment.