Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AA storm feature / revisit landfall popup display #1410

Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
954eeee
report the change from PR #1384
Jan 10, 2025
5fa8df2
permanently show last analysed time point popup
Jan 10, 2025
103eb85
Merge branch 'feature/add-new-AA-storm' into feature/permanently-show…
ericboucher Jan 12, 2025
a8966e4
Merge branch 'feature/add-new-AA-storm' into improve-style-of-windpoi…
ericboucher Jan 12, 2025
84e7856
refacto types
Jan 13, 2025
f938589
remove unused types, more refacto
Jan 13, 2025
f9f46a8
Merge branch 'feature/permanently-show-last-analysed-date-tooltip' of…
Jan 13, 2025
53fca9f
improve timeline tooltip arrow
Jan 14, 2025
57c3b52
update landfall popup
Jan 14, 2025
da8d19d
fix test
Jan 14, 2025
19015fb
Merge branch 'feature/permanently-show-last-analysed-date-tooltip' in…
Jan 14, 2025
1635a2c
show small landfall popup
Jan 14, 2025
6eddfab
move popup of the right
Jan 15, 2025
edd7cda
Merge branch 'feature/add-new-AA-storm' into aa-storm-feature/revisit…
ericboucher Jan 15, 2025
57cbbc9
Fix lint
ericboucher Jan 15, 2025
e395922
Update index.tsx
ericboucher Jan 15, 2025
977735c
Update index.tsx
ericboucher Jan 15, 2025
1f2d078
Cleanup PR
ericboucher Jan 15, 2025
98ab91f
remove usage of unknown keyword
Jan 16, 2025
7a4a9b5
Merge branch 'aa-storm-feature/revisit-landfall-popup-display' of ssh…
Jan 16, 2025
7dd24d9
fix tooltip behaviour when clicking on it several times
Jan 16, 2025
e76f601
redefine how landfall marker is displayed
Jan 17, 2025
92a3f97
display landfall marker based on report date instead of current date
Jan 17, 2025
32845f0
Update AAStormLandfallMarker.tsx
ericboucher Jan 17, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Popup } from 'react-map-gl/maplibre';
import { ParsedStormData } from 'context/anticipatoryAction/AAStormStateSlice/parsedStromDataTypes';
import _React from 'react';
import { createStyles, makeStyles, Typography } from '@material-ui/core';
import { findLandfallWindPoint } from '../utils';

function AAStormLandfallMarker({ stormData }: AAStormLandfallPopupProps) {
const classes = useStyles();

const windpoint = findLandfallWindPoint(stormData);

if (!windpoint) {
return null;
}

const lng = windpoint.geometry.coordinates[0];
const lat = windpoint.geometry.coordinates[1];

return (
<Popup
longitude={lng}
latitude={lat}
anchor="bottom"
offset={25}
closeButton={false}
closeOnClick={false}
className={classes.popup}
>
<Typography className={classes.tooltipText} variant="body1">
landfall
</Typography>
</Popup>
);
}

interface AAStormLandfallPopupProps {
stormData: ParsedStormData;
}

const useStyles = makeStyles(() =>
createStyles({
tooltipText: {
fontSize: '14px',
fontWeight: 600,
lineHeight: '14px',
paddingBottom: '2px',
},

popup: {
'& > .maplibregl-popup-content': {
border: 'none',
padding: '4px',
borderRadius: '4px',
background: 'white',
boxShadow: 'inset 0px 0px 0px 1px #A4A4A4',
position: 'relative',
},

'& > .maplibregl-popup-tip': {
display: 'none',
},

// hack to display the popup tip without overlapping border
'&::after': {
content: '""',
position: 'absolute',
left: '50%',
bottom: '-5px',
width: '10px',
height: '10px',
background: 'white',
transform: 'translateX(-50%) rotate(45deg)',
borderWidth: '0px 1px 1px 0px',
borderColor: '#A4A4A4',
borderStyle: 'solid',
},
},
}),
);

export default AAStormLandfallMarker;
Original file line number Diff line number Diff line change
@@ -1,26 +1,43 @@
import { createStyles, makeStyles } from '@material-ui/core';
import { Point } from 'geojson';
import { Offset } from 'maplibre-gl';
import { Popup } from 'react-map-gl/maplibre';
import { LandfallInfo } from 'context/anticipatoryAction/AAStormStateSlice/parsedStormDataTypes';
import { AAStormTimeSeriesFeature } from 'context/anticipatoryAction/AAStormStateSlice/rawStormDataTypes';
import PopupContent from './PopupContent';
import { isFeatureAtLandfallEstimateTime } from './utils';

const verticalLandfallPopupOffset = -50;
const horizontalLandfallPopupOffset = 25;

function AAStormLandfallPopup({
point,
feature,
onClose,
landfallInfo,
reportDate,
}: AAStormLandfallPopupProps) {
const classes = useStyles();

const lng = point.coordinates[0];
const lat = point.coordinates[1];
if (!landfallInfo) {
return null;
}

const isVisible = isFeatureAtLandfallEstimateTime(feature, landfallInfo.time);

if (!isVisible || !landfallInfo) {
return null;
}

const lng = feature.geometry.coordinates[0];
const lat = feature.geometry.coordinates[1];

return (
<Popup
longitude={lng}
latitude={lat}
anchor="top"
offset={15}
anchor="top-left"
offset={
[verticalLandfallPopupOffset, horizontalLandfallPopupOffset] as Offset
}
closeButton={false}
onClose={onClose}
closeOnClick
Expand All @@ -33,8 +50,8 @@ function AAStormLandfallPopup({
}

interface AAStormLandfallPopupProps {
point: Point;
landfallInfo: LandfallInfo;
feature: AAStormTimeSeriesFeature;
landfallInfo: LandfallInfo | undefined;
reportDate: string;
onClose: () => void;
}
Expand All @@ -43,15 +60,31 @@ const useStyles = makeStyles(() =>
createStyles({
popup: {
width: '280px',

'& > .maplibregl-popup-content': {
background: '#F1F1F1',
padding: '0px 2px 0px 2px',
border: 'none',
borderRadius: '4px',
boxShadow: 'inset 0px 1px 0px 0px #A4A4A4',
position: 'relative',
},

'& > .maplibregl-popup-tip': {
borderBottomColor: '#F1F1F1',
borderLeftWidth: '8px',
borderRightWidth: '8px',
display: 'none',
},

// hack to display the popup tip without overlapping border
'&::after': {
background: '#F1F1F1',
content: '""',
position: 'absolute',
left: `${horizontalLandfallPopupOffset * 2}px`,
top: -5,
width: '10px',
height: '10px',

transform: 'translateX(-50%) rotate(45deg)',
boxShadow: 'inset 1px 1px 0px 0px #A4A4A4',
},
},
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ParsedStormData } from 'context/anticipatoryAction/AAStormStateSlice/parsedStromDataTypes';
import { AAStormTimeSeriesFeature } from 'context/anticipatoryAction/AAStormStateSlice/rawStormDataTypes';

/* find the wind point which time corresponds to the landfall estimated time */
export function findLandfallWindPoint(stormData: ParsedStormData) {
const { landfall } = stormData;
if (!landfall) {
return null;
}

const landfallEstimatedtime = landfall.time;

const windpoints = stormData.timeSeries?.features;
return windpoints?.find(windpoint =>
isFeatureAtLandfallEstimateTime(windpoint, landfallEstimatedtime),
);
}

export function isFeatureAtLandfallEstimateTime(
feature: AAStormTimeSeriesFeature,
landfallEstimatedtime: string[],
) {
return (
landfallEstimatedtime &&
feature.properties.time === landfallEstimatedtime[0]
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ import { useWindStatesByTime } from 'components/MapView/DateSelector/TimelineIte
import { getAAColor } from 'components/MapView/LeftPanel/AnticipatoryActionPanel/AnticipatoryActionStormPanel/utils';
import { AACategory } from 'context/anticipatoryAction/AAStormStateSlice/parsedStormDataTypes';
import anticipatoryActionIcons from 'components/Common/AnticipatoryAction/icons';
import { AAStormTimeSeriesFeature } from 'context/anticipatoryAction/AAStormStateSlice/rawStormDataTypes';
import AAStormDatePopup from './AAStormDatePopup';
import AAStormLandfallPopup from './AAStormLandfallPopup';

import { TimeSeries } from './types';
import AAStormLandfallMarker from './AAStormLandfallPopup/AAStormLandfallMarker/AAStormLandfallMarker';

interface AnticipatoryActionStormLayerProps {
layer: AnticipatoryActionLayerProps;
Expand Down Expand Up @@ -105,7 +107,7 @@ const AnticipatoryActionStormLayer = React.memo(
const { data: boundaryData } = boundaryLayerState || {};

const [selectedFeature, setSelectedFeature] =
useState<Feature<Point> | null>(null);
useState<AAStormTimeSeriesFeature | null>(null);

function enhanceTimeSeries(timeSeries: TimeSeries) {
const { features, ...timeSeriesRest } = timeSeries;
Expand Down Expand Up @@ -227,7 +229,7 @@ const AnticipatoryActionStormLayer = React.memo(
e.preventDefault();
dispatch(hidePopup()); // hides the black tooltip containing the district names
const feature = e.features?.[0];
setSelectedFeature(feature as Feature<Point>);
setSelectedFeature(feature as unknown as AAStormTimeSeriesFeature);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this avoidable?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(using "unknown")

Copy link
Collaborator Author

@Max-Z80 Max-Z80 Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I have pushed a simple version without unknown.

};

useMapCallback<'click', null>(
Expand Down Expand Up @@ -436,14 +438,16 @@ const AnticipatoryActionStormLayer = React.memo(

<AAStormDatePopup timeSeries={stormData.timeSeries} />

{selectedFeature && stormData.landfall?.time && (
{selectedFeature && (
<AAStormLandfallPopup
point={selectedFeature.geometry}
feature={selectedFeature}
reportDate={stormData.forecastDetails?.reference_time || ''}
landfallInfo={stormData.landfall}
onClose={() => landfallPopupCloseHandler()}
/>
)}

<AAStormLandfallMarker stormData={stormData} />
</>
);
},
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