Skip to content

Commit

Permalink
Merge branch 'feature/permanently-show-last-analysed-date-tooltip' in…
Browse files Browse the repository at this point in the history
…to aa-strom-feature/revisit-landfall-popup-display
  • Loading branch information
Maxime Chaillet committed Jan 14, 2025
2 parents da8d19d + f9f46a8 commit 19015fb
Show file tree
Hide file tree
Showing 15 changed files with 247 additions and 183 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@ import _React, { useCallback, useState } from 'react';
import { createStyles, makeStyles, Typography } from '@material-ui/core';
import { useMapCallback } from 'utils/map-utils';
import { formatInUTC, getDateInUTC } from '../utils';
import { TimeSeriesFeature } from '../types';
import {
FeaturePropertyDataType,
TimeSeries,
TimeSeriesFeature,
} from '../types';

function AAStormDatePopup() {
interface AAStormDatePopupProps {
timeSeries?: TimeSeries;
}

function AAStormDatePopup({ timeSeries }: AAStormDatePopupProps) {
const classes = useStyles();
const [selectedFeature, setSelectedFeature] =
useState<TimeSeriesFeature | null>(null);
Expand Down Expand Up @@ -50,31 +58,62 @@ function AAStormDatePopup() {
return formatInUTC(dateInUTC, 'dd - Kaaa');
}

if (!selectedFeature) {
return null;
const lastAnalysedTimePoint: TimeSeriesFeature | undefined =
// eslint-disable-next-line fp/no-mutating-methods
timeSeries?.features
.slice()
.reverse()
.find(
feature =>
feature.properties.data_type === FeaturePropertyDataType.analysis,
);

function renderPopup(feature?: TimeSeriesFeature | null) {
if (!feature) {
return null;
}

const lng = feature.geometry.coordinates[0];
const lat = feature.geometry.coordinates[1];
const { time } = feature.properties;

return (
<Popup
key={feature.id}
longitude={lng}
latitude={lat}
anchor="top"
offset={15}
closeButton={false}
onClose={() => null}
closeOnClick={false}
className={classes.popup}
>
<Typography className={classes.toolTipDate} variant="body1">
{getDayAndTime(time)}
</Typography>
</Popup>
);
}

const lng = selectedFeature.geometry.coordinates[0];
const lat = selectedFeature.geometry.coordinates[1];
function renderHoveredPopup() {
const isLastAnalysedFeatureHovered =
selectedFeature?.id === lastAnalysedTimePoint?.id;

if (!isLastAnalysedFeatureHovered && selectedFeature) {
return renderPopup(selectedFeature);
}

const { time } = selectedFeature.properties;
return null;
}

return (
<Popup
key={selectedFeature.id}
longitude={lng}
latitude={lat}
anchor="top"
offset={25}
closeButton={false}
onClose={() => null}
closeOnClick={false}
className={classes.popup}
>
<Typography className={classes.toolTipDate} variant="body1">
{getDayAndTime(time)}
</Typography>
</Popup>
<>
{/* Permanently render the popup for the last analysed point */}
{renderPopup(lastAnalysedTimePoint)}

{renderHoveredPopup()}
</>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { render } from '@testing-library/react';
import { AACategory } from 'context/anticipatoryAction/AAStormStateSlice/types';

import { AACategory } from 'context/anticipatoryAction/AAStormStateSlice/parsedStromDataTypes';
import PopupContent from '.';

describe('AAStormLandfallPopup component', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createStyles, makeStyles, Typography } from '@material-ui/core';
import { LandfallInfo } from 'context/anticipatoryAction/AAStormStateSlice/types';

import { LandfallInfo } from 'context/anticipatoryAction/AAStormStateSlice/parsedStromDataTypes';
import {
formatLandfallDate,
formatLandfallEstimatedLeadtime,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createStyles, makeStyles } from '@material-ui/core';
import { Point } from 'geojson';
import { Popup } from 'react-map-gl/maplibre';
import { LandfallInfo } from 'context/anticipatoryAction/AAStormStateSlice/types';
import { LandfallInfo } from 'context/anticipatoryAction/AAStormStateSlice/parsedStromDataTypes';
import PopupContent from './PopupContent';

function AAStormLandfallPopup({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import {
AALoadingSelector,
loadStormReport,
} from 'context/anticipatoryAction/AAStormStateSlice';
import { AACategory } from 'context/anticipatoryAction/AAStormStateSlice/types';
import { updateDateRange } from 'context/mapStateSlice';
import { useWindStatesByTime } from 'components/MapView/DateSelector/TimelineItems/hooks';
import { getAAColor } from 'components/MapView/LeftPanel/AnticipatoryActionPanel/AnticipatoryActionStormPanel/utils';
import { AACategory } from 'context/anticipatoryAction/AAStormStateSlice/parsedStromDataTypes';
import anticipatoryActionIcons from 'components/Common/AnticipatoryAction/icons';
import AAStormDatePopup from './AAStormDatePopup';
import AAStormLandfallPopup from './AAStormLandfallPopup';
Expand Down Expand Up @@ -434,7 +434,7 @@ const AnticipatoryActionStormLayer = React.memo(
</Source>
)}

<AAStormDatePopup />
<AAStormDatePopup timeSeries={stormData.timeSeries} />

{selectedFeature && stormData.landfall?.time && (
<AAStormLandfallPopup
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
// Q - still needed or needs to be improved?
import { FeatureCollection, Point, Feature } from 'geojson';

export enum FeaturePropertyDataType {
analysis = 'analysis',
forecast = 'forecast',
}

interface FeatureProperty {
data_type: 'analysis' | 'forecast';
data_type: FeaturePropertyDataType;
time: string;
development: string;
// maximum_wind_speed: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ import { Typography, createStyles, makeStyles } from '@material-ui/core';
import { useSelector } from 'react-redux';
import { AADataSelector } from 'context/anticipatoryAction/AAStormStateSlice';
import { useSafeTranslation } from 'i18n';
import {
AADisplayCategory,
AACategory,
AAPanelCategories,
} from 'context/anticipatoryAction/AAStormStateSlice/types';
import { AACategory } from 'context/anticipatoryAction/AAStormStateSlice/parsedStromDataTypes';
import { getAAColor } from '../utils';
import { useAACommonStyles } from '../../utils';
import { AADisplayCategory, AAPanelCategories } from './types';

interface AreaTagProps {
name: string;
Expand Down Expand Up @@ -96,17 +93,17 @@ interface ActivationTriggerProps {
function ActivationTrigger({ dialogs }: ActivationTriggerProps) {
const { t } = useSafeTranslation();
const classes = useActivationTriggerStyles();
const rawAAData = useSelector(AADataSelector);
const parsedStromData = useSelector(AADataSelector);
const commonClasses = useAACommonStyles();

const filteredActiveDistricts = rawAAData.activeDistricts
? Object.entries(rawAAData.activeDistricts).filter(([category]) =>
const filteredActiveDistricts = parsedStromData.activeDistricts
? Object.entries(parsedStromData.activeDistricts).filter(([category]) =>
AAPanelCategories.includes(category as AACategory),
)
: [];

const filteredNADistricts = rawAAData.naDistricts
? Object.entries(rawAAData.naDistricts).filter(([category]) =>
const filteredNADistricts = parsedStromData.naDistricts
? Object.entries(parsedStromData.naDistricts).filter(([category]) =>
AAPanelCategories.includes(category as AACategory),
)
: [];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { AACategory } from 'context/anticipatoryAction/AAStormStateSlice/parsedStromDataTypes';

export const AAPanelCategories: AACategory[] = [
AACategory.Severe,
AACategory.Moderate,
];

export const AADisplayCategory: {
[key in AACategory]?: string;
} = {
[AACategory.Severe]: ' >118 KM/H',
[AACategory.Moderate]: ' >89 KM/H',
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/* eslint-disable react-refresh/only-export-components */
import {
AAPhaseType,
AACategory,
} from 'context/anticipatoryAction/AAStormStateSlice/types';
import { AACategory } from 'context/anticipatoryAction/AAStormStateSlice/parsedStromDataTypes';
import { AAPhaseType } from 'context/anticipatoryAction/AAStormStateSlice/types';

const AACategoryPhaseMap: { [key in AACategory]?: any } = {
[AACategory.Severe]: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ import {
updateLayersCapabilities,
} from 'context/serverStateSlice';
import { AnticipatoryActionData } from 'context/anticipatoryAction/AADroughtStateSlice/types';
import { AAStormData } from 'context/anticipatoryAction/AAStormStateSlice/types';
import { getFormattedDate } from 'utils/date-utils';
import { DateFormat } from 'utils/name-utils';
import { ParsedStormData } from 'context/anticipatoryAction/AAStormStateSlice/parsedStromDataTypes';
import { toggleRemoveLayer } from '../layersPanel/MenuItem/MenuSwitch/SwitchItem/utils';

type AADataByAction<T extends AnticipatoryAction> =
T extends AnticipatoryAction.storm
? AAStormData
? ParsedStormData
: Record<'Window 1' | 'Window 2', AnticipatoryActionData>;

type AAAvailableDatesByAction<T extends AnticipatoryAction> =
Expand Down
17 changes: 7 additions & 10 deletions frontend/src/context/anticipatoryAction/AAStormStateSlice/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { DateItem } from 'config/types';
import type { CreateAsyncThunkTypes, RootState } from '../../store';
import {
AAStormData,
AAStormWindStateReports,
AnticipatoryActionState,
StormData,
} from './types';
import { AAStormWindStateReports, AnticipatoryActionState } from './types';
import { parseAndTransformAA } from './utils';
import { ParsedStormData } from './parsedStromDataTypes';
import { StormDataResponseBody } from './rawStormDataTypes';

const initialState: AnticipatoryActionState = {
data: {},
Expand All @@ -28,7 +25,7 @@ export const loadAllAAStormData = createAsyncThunk<

export const loadLatestStormReport = createAsyncThunk<
{
data: AAStormData;
data: ParsedStormData;
},
undefined,
CreateAsyncThunkTypes
Expand All @@ -40,7 +37,7 @@ export const loadLatestStormReport = createAsyncThunk<
'https://data.earthobservation.vam.wfp.org/public-share/aa/ts/outputs/latest.json',
);
const stormData = await response.json();
const data = parseAndTransformAA(stormData as StormData);
const data = parseAndTransformAA(stormData as StormDataResponseBody);
return data;
} catch (error) {
return rejectWithValue(error);
Expand All @@ -50,7 +47,7 @@ export const loadLatestStormReport = createAsyncThunk<

export const loadStormReport = createAsyncThunk<
{
data: AAStormData;
data: ParsedStormData;
},
{ stormName: string; date: string },
CreateAsyncThunkTypes
Expand All @@ -67,7 +64,7 @@ export const loadStormReport = createAsyncThunk<
`https://data.earthobservation.vam.wfp.org/public-share/aa/ts/outputs/${stormName}/${date}.json?v2`,
);
const stormData = await response.json();
const data = parseAndTransformAA(stormData as StormData);
const data = parseAndTransformAA(stormData as StormDataResponseBody);
return data;
} catch (error) {
return rejectWithValue(error);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { ForecastDetails, TimeSeries } from './rawStormDataTypes';

export enum AACategory {
Severe = 'Severe',
Moderate = 'Moderate',
Risk = 'Risk',
}

export enum AACategoryKey {
Severe = 'exposed_area_64kt',
Moderate = 'exposed_area_48kt',
Proba = 'proba_48kt_20_5d',
}

export enum AACategoryLandfall {
Severe = 'severe tropical storm',
Moderate = 'moderate tropical storm',
}

export interface AAData {
districtNames: string[];
polygon: any;
}

export type DistrictDataType = {
[key in AACategory]?: AAData;
};

export interface LandfallInfo {
district: string;
time: string[];
severity: AACategory[];
}

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

export const AACategoryDataToLandfallMap: {
[key in AACategoryLandfall]: AACategory;
} = {
[AACategoryLandfall.Severe]: AACategory.Severe,
[AACategoryLandfall.Moderate]: AACategory.Moderate,
};

export const AACategoryKeyToCategoryMap: {
[key in AACategoryKey]: AACategory;
} = {
[AACategoryKey.Severe]: AACategory.Severe,
[AACategoryKey.Moderate]: AACategory.Moderate,
[AACategoryKey.Proba]: AACategory.Risk,
};

/* parsed storm data type */
export type ParsedStormData = {
activeDistricts?: DistrictDataType;
naDistricts?: DistrictDataType;
landfall?: LandfallInfo;
timeSeries?: TimeSeries;
landfallDetected?: boolean;
forecastDetails?: ForecastDetails;
uncertaintyCone?: FeatureProperties;
};

export type ResultType = {
data: ParsedStormData;
};
Loading

0 comments on commit 19015fb

Please sign in to comment.