Skip to content

Commit fbf3040

Browse files
authored
fix: heatmap display in mobile + legend (#983)
1 parent 97624fe commit fbf3040

File tree

3 files changed

+90
-7
lines changed

3 files changed

+90
-7
lines changed

web-app/public/locales/en/feeds.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@
102102
"officialFeedUpdated": "Official verification updated",
103103
"serviceDateRange": "Service Date Range",
104104
"serviceDateRangeTooltip": "Dates are relative to local timezone",
105+
"heatmapIntensity": "Stop Density",
106+
"heatmapExplanationTitle": "What does this mean?",
107+
"heatmapExplanationContent": "This color scale shows the percentage of stops from <code>stops.txt</code> that are located within each geographic area. Darker colors indicate areas where a higher percentage of stops are covered.",
108+
"heatmapLower": "Less Stops",
109+
"heatmapHigher": "More Stops",
105110
"feedStatus": {
106111
"active": {
107112
"label": "Active",

web-app/src/app/components/CoveredAreaMap.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const CoveredAreaMap: React.FC<CoveredAreaMapProps> = ({
5252
>('detailedCoveredAreaView');
5353

5454
useEffect(() => {
55-
if (latestDataset?.hosted_url !== undefined) {
55+
if (latestDataset?.hosted_url !== undefined && boundingBox != undefined) {
5656
setGeoJsonLoading(true);
5757
fetchGeoJson(latestDataset.hosted_url)
5858
.then((data) => {

web-app/src/app/components/MapGeoJSON.tsx

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import {
66
type LatLngExpression,
77
type LeafletMouseEvent,
88
} from 'leaflet';
9+
import { Trans, useTranslation } from 'react-i18next';
910
import { PopupTable } from './PopupTable';
1011
import { createRoot } from 'react-dom/client';
1112
import { useTheme } from '@mui/material/styles';
1213
import { ThemeModeEnum } from '../Theme';
13-
import { Box } from '@mui/material';
14+
import { Box, Typography, Tooltip } from '@mui/material';
15+
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
1416

1517
export interface GeoJSONData {
1618
type: 'FeatureCollection' | 'Feature' | 'GeometryCollection';
@@ -33,6 +35,16 @@ export const MapGeoJSON = (
3335
props: React.PropsWithChildren<MapProps>,
3436
): JSX.Element => {
3537
const theme = useTheme();
38+
const { t } = useTranslation('feeds');
39+
const { geoJSONData } = props;
40+
const bounds = React.useMemo(() => {
41+
return props.polygon.length > 0
42+
? (props.polygon as LatLngBoundsExpression)
43+
: ([
44+
[0, 0],
45+
[0, 0],
46+
] as LatLngBoundsExpression);
47+
}, [props.polygon]);
3648

3749
const handleFeatureClick = (
3850
e: LeafletMouseEvent,
@@ -64,20 +76,86 @@ export const MapGeoJSON = (
6476
}}
6577
>
6678
<MapContainer
67-
bounds={props.polygon as LatLngBoundsExpression}
79+
bounds={bounds}
6880
zoom={8}
69-
style={{
70-
height: '100%',
71-
}}
81+
style={{ minHeight: '400px', height: '100%' }}
7282
data-testid='geojson-map'
7383
>
7484
<TileLayer
7585
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
7686
url={mapTiles}
7787
/>
7888
{props.geoJSONData !== null && (
89+
<Box
90+
sx={{
91+
position: 'absolute',
92+
bottom: 20,
93+
right: 16,
94+
background: theme.palette.background.paper,
95+
padding: 1,
96+
borderRadius: 2,
97+
boxShadow: 3,
98+
maxWidth: 175,
99+
zIndex: 1000,
100+
}}
101+
>
102+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
103+
<Typography style={{ fontSize: '0.85rem', fontWeight: 800 }}>
104+
{t('heatmapIntensity')}
105+
</Typography>
106+
<Tooltip
107+
title={
108+
<React.Fragment>
109+
<Typography sx={{ fontWeight: 800 }}>
110+
<strong>{t('heatmapExplanationTitle')}</strong>
111+
</Typography>
112+
<div>
113+
{' '}
114+
<Trans
115+
i18nKey={t('heatmapExplanationContent')}
116+
components={{ code: <code /> }}
117+
/>
118+
</div>
119+
</React.Fragment>
120+
}
121+
>
122+
<InfoOutlinedIcon
123+
sx={{
124+
fontSize: '16px',
125+
color: theme.palette.text.secondary,
126+
cursor: 'pointer',
127+
}}
128+
/>
129+
</Tooltip>
130+
</Box>
131+
<Box
132+
sx={{
133+
width: '100%',
134+
height: '16px',
135+
background: `linear-gradient(to right, #fb8c58, #7f0000)`,
136+
marginTop: 2,
137+
marginBottom: 1,
138+
borderRadius: 1,
139+
}}
140+
/>
141+
<Box
142+
sx={{
143+
display: 'flex',
144+
justifyContent: 'space-between',
145+
fontSize: '0.75rem',
146+
gap: 2,
147+
color: theme.palette.text.secondary,
148+
}}
149+
>
150+
<span>{t('heatmapLower')}</span>
151+
<span>{t('heatmapHigher')}</span>
152+
</Box>
153+
</Box>
154+
)}
155+
156+
{geoJSONData !== null && (
79157
<GeoJSON
80-
data={props.geoJSONData}
158+
data={geoJSONData}
81159
onEachFeature={(feature, layer) => {
82160
const container = document.createElement('div');
83161
container.style.background = theme.palette.background.default;

0 commit comments

Comments
 (0)