Skip to content

Commit

Permalink
Add statistics to multiple temporal analysis (#333)
Browse files Browse the repository at this point in the history
* Update test analysis

* Fix lint

* Fix test

* revert custom geom
  • Loading branch information
zamuzakki authored Feb 13, 2025
1 parent afbab55 commit f32c851
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 8 deletions.
91 changes: 91 additions & 0 deletions django_project/frontend/api_views/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
.. note:: Analysis APIs
"""
import uuid
from collections import OrderedDict
from datetime import date
from copy import deepcopy
from concurrent.futures import ThreadPoolExecutor
Expand Down Expand Up @@ -105,6 +106,93 @@ def add_empty_records(existing_records):
new_records[key] = new_record
return new_records.values()

def add_statistics(features):
new_features = [
a['properties'] for a in filter(
lambda x: x['properties']['year'] in years,
features
)
]

# Process data
aggregated = {}

for row in new_features:
name, year = row["Name"], int(row["year"])

# Convert numeric values
bare_ground = row["Bare ground"]
evi = row["EVI"]
ndvi = row["NDVI"]

key = (name, year)
if key not in aggregated:
aggregated[key] = {
"Bare ground": [],
"EVI": [],
"NDVI": []
}

aggregated[key]["Bare ground"].append(bare_ground)
aggregated[key]["EVI"].append(evi)
aggregated[key]["NDVI"].append(ndvi)

# Compute min, max, and mean
results = {}
unprocessed_years = years
names = set()

for location_year, values in aggregated.items():
location, year = location_year
if year in unprocessed_years:
unprocessed_years.remove(year)
names.add(location)
if year not in results:
results[year] = {}
if location not in results[year]:
results[year][location] = {}

for category, numbers in values.items():
min_val = min(numbers)
max_val = max(numbers)
mean_val = sum(numbers) / len(numbers)
results[year][location][category] = {
'min': min_val,
'max': max_val,
'mean': mean_val
}

empty_data = {
'Bare ground': {
'min': None, 'max': None, 'mean': None
},
'EVI': {
'min': None, 'max': None, 'mean': None
},
'NDVI': {
'min': None, 'max': None, 'mean': None
},
}
for year in unprocessed_years:
for name in names:
if results.get(year, None):
results[year].update({
name: empty_data
})
else:
results[year] = {
name: empty_data
}

results = {
year: {
name: OrderedDict(
sorted(value.items())
) for name, value in sorted(group.items())
} for year, group in sorted(results.items())
}
return results

output_results = []
output_results.append(input_results[0][0])
output_results.append(input_results[0][1])
Expand All @@ -128,6 +216,9 @@ def add_empty_records(existing_records):
output_results[1]['features'],
key=lambda x: x['properties']['date']
)
output_results[0]['statistics'] = add_statistics(
output_results[1]['features']
)

return output_results

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,10 @@ import { Analysis } from "../../../store/analysisSlice";
import { Bar, Line } from "react-chartjs-2";
import { CategoryScale } from "chart.js";
import Chart from "chart.js/auto";
import ChartDataLabels from "chartjs-plugin-datalabels";
import {FeatureCollection} from "geojson";
import 'chartjs-adapter-date-fns';

import './style.css';
import { features } from 'process';
import { rmSync } from 'fs';
import { colors } from '../../../theme/foundations';

Chart.register(CategoryScale);

Expand All @@ -34,6 +30,49 @@ const COLORS = [
"#008080" // Teal
];

export function StatisticTable({analysis}: Props) {
const statistics = analysis.results[0].statistics;
const variable = analysis.data.variable;

const renderRows = () => {
const rows: any[] = [];
Object.keys(statistics).forEach(year => {
Object.keys(statistics[year]).forEach(area => {
const data = statistics[year][area][variable];
rows.push(
<tr key={`${year}-${area}`}>
<td>{year}</td>
<td>{area}</td>
<td>{data.min !== null ? data.min.toFixed(3) : 'N/A'}</td>
<td>{data.max !== null ? data.max.toFixed(3) : 'N/A'}</td>
<td>{data.mean !== null ? data.mean.toFixed(3) : 'N/A'}</td>
</tr>
);
});
});
return rows;
};

return (
<Box>
<table id="Temporal-Statistics-Table" border={1}>
<thead>
<tr>
<th>Year</th>
<th>Area</th>
<th>Min</th>
<th>Max</th>
<th>Avg</th>
</tr>
</thead>
<tbody>
{renderRows()}
</tbody>
</table>
</Box>
);
}


export function BarChart({ analysis }: Props) {
// Extracting data for the chart
Expand All @@ -48,19 +87,23 @@ export function BarChart({ analysis }: Props) {

let datasets: { [key: string]: any } = {}
for (let i = 0; i < jsonData.features.length; i++) {
const key: string = `${jsonData.features[i].properties.Name}`;
const key: string = jsonData.features[i].properties.Name;
if (datasets[key as string]) {
continue;
}
const data = jsonData.features
.filter((feature:any) => feature.properties.Name === jsonData.features[i].properties.Name)
.map((feature:any) => feature.properties[analysis.data.variable]);
const label = jsonData.features[i].properties.Name


datasets[key] = {
label: label,
label: key,
data: data,
backgroundColor: COLORS[i % COLORS.length],
borderColor: "#0000FF",
errorBars: {
color: 'black',
width: 1
}
};
}

Expand Down Expand Up @@ -249,6 +292,7 @@ export function RenderTemporal({ analysis }: Props) {
return <Box maxWidth={400} overflowX={"auto"}>
<BarChart analysis={analysis}></BarChart>
<LineChart analysis={analysis}></LineChart>
<StatisticTable analysis={analysis}/>
</Box>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,13 @@
.BaselineAnalysisResultTable td {
border: 1px solid #aaa;
font-size: 13px !important;
}

#Temporal-Statistics-Table {
tr {
background-color: white;
th,td {
border: 1px solid #aaa;
}
}
}
8 changes: 8 additions & 0 deletions django_project/frontend/tests/api_views/test_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ def side_effect_func(*args, **kwargs):
len(results[0]['features']),
5
)
self.assertEqual(
len(results[0]['statistics']),
3
)
self.assertEqual(
list(results[0]['statistics'][2019].keys()),
['BNP western polygon']
)
self.assertEqual(
results[0]['features'][0]['properties']['year'],
2017
Expand Down

0 comments on commit f32c851

Please sign in to comment.