Skip to content

Commit

Permalink
Merge branch 'feature/add-new-AA-storm' into feat/alert-storm-mail-te…
Browse files Browse the repository at this point in the history
…mplate
  • Loading branch information
ericboucher authored Jan 30, 2025
2 parents ccaf682 + fd23ee8 commit 50b1499
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { Typography, createStyles, makeStyles } from '@material-ui/core';
import {
Typography,
createStyles,
makeStyles,
Button,
} from '@material-ui/core';
import { useSelector } from 'react-redux';
import { AADataSelector } from 'context/anticipatoryAction/AAStormStateSlice';
import { useSafeTranslation } from 'i18n';
Expand Down Expand Up @@ -96,6 +101,27 @@ function ActivationTrigger({ dialogs }: ActivationTriggerProps) {
const parsedStormData = useSelector(AADataSelector);
const commonClasses = useAACommonStyles();

const handleDownloadGeoJSON = () => {
if (!parsedStormData.mergedGeoJSON || !parsedStormData.forecastDetails) {
return;
}

const dataStr = JSON.stringify(parsedStormData.mergedGeoJSON);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(dataBlob);
const link = document.createElement('a');
// eslint-disable-next-line fp/no-mutation
link.href = url;
const date =
parsedStormData.forecastDetails.reference_time.split(':00Z')[0];
// eslint-disable-next-line fp/no-mutation
link.download = `${parsedStormData.forecastDetails?.cyclone_name || 'cyclone'}_${date}.geojson`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};

const filteredActiveDistricts = parsedStormData.activeDistricts
? Object.entries(parsedStormData.activeDistricts).filter(([category]) =>
AAPanelCategories.includes(category as AACategory),
Expand Down Expand Up @@ -188,6 +214,20 @@ function ActivationTrigger({ dialogs }: ActivationTriggerProps) {
{t(dialog.text)}
</Typography>
))}
{parsedStormData.mergedGeoJSON && (
<Button
style={{
width: '50%',
margin: '1rem auto',
}}
className={commonClasses.footerButton}
variant="outlined"
fullWidth
onClick={handleDownloadGeoJSON}
>
<Typography>{t('Download GeoJSON')}</Typography>
</Button>
)}
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Feature, Geometry, GeoJsonProperties } from 'geojson';
import { ForecastDetails, TimeSeries } from './rawStormDataTypes';

export enum AACategory {
Expand Down Expand Up @@ -32,11 +33,6 @@ export interface LandfallInfo {
severity: AACategory[];
}

interface FeatureProperties {
time: string;
[key: string]: any;
}

export const AACategoryDataToLandfallMap: {
[key in AACategoryLandfall]: AACategory;
} = {
Expand All @@ -52,6 +48,11 @@ export const AACategoryKeyToCategoryMap: {
[AACategoryKey.Proba]: AACategory.Risk,
};

export interface MergedFeatures<P = GeoJsonProperties>
extends Feature<Geometry | null, P> {
geometry: Geometry | null;
}

/* parsed storm data type */
export type ParsedStormData = {
activeDistricts?: DistrictDataType;
Expand All @@ -60,7 +61,11 @@ export type ParsedStormData = {
timeSeries?: TimeSeries;
landfallDetected?: boolean;
forecastDetails?: ForecastDetails;
uncertaintyCone?: FeatureProperties;
uncertaintyCone?: GeoJSON.Geometry;
mergedGeoJSON?: {
type: string;
features: MergedFeatures[];
};
};

export type ResultType = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ export enum FeaturePropertyDataType {
forecast = 'forecast',
}

interface FeatureProperties {
time: string;
[key: string]: any;
}

/* timeserie types */

export interface TimeSerieFeatureProperty {
data_type: FeaturePropertyDataType;
time: string;
Expand Down Expand Up @@ -45,15 +39,15 @@ export enum AACategoryLandfall {
/* ready_set_results types */
interface ExposedAreaStorm {
affected_districts: string[];
polygon: any;
polygon: GeoJSON.Geometry;
}

/* storm data reponse body type */
export interface StormDataResponseBody {
time_series: TimeSeries;
landfall_detected: boolean;
forecast_details: ForecastDetails;
uncertainty_cone: FeatureProperties;
uncertainty_cone: GeoJSON.Geometry;
landfall_info: {
landfall_time: string[];
landfall_impact_district: string;
Expand Down
75 changes: 75 additions & 0 deletions frontend/src/context/anticipatoryAction/AAStormStateSlice/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
AACategoryKeyToCategoryMap,
AACategoryLandfall,
DistrictDataType,
MergedFeatures,
ResultType,
} from './parsedStormDataTypes';
import { StormDataResponseBody } from './rawStormDataTypes';
Expand All @@ -27,6 +28,77 @@ const watchedDistricts: { [key in AACategory]: string[] } = {
[AACategory.Risk]: [],
};

/**
* Creates a merged GeoJSON FeatureCollection from storm data response ready to be downloaded.
* Combines exposed areas, uncertainty cone, time series, and metadata into a single GeoJSON object.
*
* @param data - The storm data response containing various geographical features
* @returns A GeoJSON FeatureCollection containing all merged features
*/
function createMergedGeoJSON(data: StormDataResponseBody) {
const features: MergedFeatures[] = [];

// Helper function to add exposed area features
const addExposedArea = (
key: 'exposed_area_48kt' | 'exposed_area_64kt' | 'proba_48kt_20_5d',
) => {
const exposedArea = data.ready_set_results?.[key];
if (exposedArea) {
// eslint-disable-next-line fp/no-mutating-methods
features.push({
type: 'Feature',
properties: {
data_type: key,
affected_districts: exposedArea.affected_districts,
},
geometry: exposedArea.polygon,
});
}
};

// Add exposed areas
addExposedArea('exposed_area_48kt');
addExposedArea('exposed_area_64kt');

// Add uncertainty cone if it exists
if (data.uncertainty_cone) {
// eslint-disable-next-line fp/no-mutating-methods
features.push({
type: 'Feature',
properties: {
data_type: 'uncertainty_cone',
},
geometry: data.uncertainty_cone,
});
}

// Add time series features if they exist
if (data.time_series) {
data.time_series.features.forEach(feature => {
// eslint-disable-next-line fp/no-mutating-methods
features.push(feature);
});
}

// Create metadata feature
// eslint-disable-next-line fp/no-mutating-methods
features.push({
type: 'Feature',
properties: {
data_type: 'metadata',
forecast_details: data.forecast_details,
landfall_detected: data.landfall_detected,
landfall_info: data.landfall_info,
},
geometry: null,
});

return {
type: 'FeatureCollection',
features,
};
}

// DRAFT: This is a provisional implementation based on a test dataset with a temporary structure that is subject to change.
export function parseAndTransformAA(data: StormDataResponseBody): ResultType {
const exposedAreas = data.ready_set_results;
Expand Down Expand Up @@ -90,6 +162,8 @@ export function parseAndTransformAA(data: StormDataResponseBody): ResultType {
}
: undefined;

const mergedGeoJSON = createMergedGeoJSON(data);

return {
data: {
activeDistricts,
Expand All @@ -99,6 +173,7 @@ export function parseAndTransformAA(data: StormDataResponseBody): ResultType {
landfallDetected: data.landfall_detected,
forecastDetails: data.forecast_details,
uncertaintyCone: data.uncertainty_cone,
mergedGeoJSON,
},
};
}

0 comments on commit 50b1499

Please sign in to comment.