ZoomControlWithReset component for React Leaflet
React Leaflet is a React library that exposes Leaflet classes as React components, making it very easy to add interactive maps to React web apps.
This is the first in a series of posts on how I use the library. My intention is to share back some of the things I’ve learned and implemented in the hope of them being useful to others.
In this post, I present the ZoomControlWithReset
component which provides the
classic zoom +/- control with one extra button which resets the map view to fit
some bounds (usually calculated for map points).
First, the component:
import React from 'react';
import PropTypes from 'prop-types';import { useMap } from 'react-leaflet';
function ZoomControlWithReset(props) {
const { zoomInTitle, zoomResetTitle, zoomOutTitle, bounds } = props;
const map = useMap();
return (
<div className="leaflet-control leaflet-control-zoom leaflet-bar">
<a
className="leaflet-control-zoom-in"
href="#"
title={zoomInTitle}
role="button"
aria-label="Zoom in"
onClick={(e) => {
map.zoomIn();
e.preventDefault();
}}
>
+
</a>
<a
className="leaflet-control-zoom-out"
href="#"
title={zoomOutTitle}
role="button"
aria-label="Zoom out"
onClick={(e) => {
map.zoomOut();
e.preventDefault();
}}
>
-
</a>
<a
className="leaflet-control-zoom-out" href="#"
title={zoomResetTitle}
role="button"
aria-label="Reset zoom"
onClick={(e) => {
map.fitBounds(bounds); e.preventDefault();
}}
>
↺ </a>
</div>
);
}
ZoomControlWithReset.propTypes = {
zoomInTitle: PropTypes.string,
zoomOutTitle: PropTypes.string,
zoomResetTitle: PropTypes.string,
bounds: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)).isRequired,
};
ZoomControlWithReset.defaultProps = {
zoomInTitle: 'Zoom in', zoomOutTitle: 'Zoom out', zoomResetTitle: 'Reset zoom',};
export default ZoomControlWithReset;
Things to note:
- Line 2: The component uses the prop-types npm package for validating props in development
- Line 6: It’s possible to override the title attribute of the buttons with the
zoomInTitle
,zoomOutTitle
andzoomResetTitle
props - Line 39: Reuse the
leaflet-control-zoom-out
class to style the reset button - Line 45: Reset the view
- Line 49: Anti-clockwise open circle arrow
- Lines 63-65: Default values for the button titles
And here’s how you’d use it in a map:
import React from 'react';
import { TileLayer } from 'react-leaflet';
import ZoomControlWithReset from './ZoomControlWithReset';
const GREECE_BOUNDS = [ [31.08, 14.26], [44.91, 34.69],];
function MyMap() {
return (
<MapContainer
bounds={GREECE_BOUNDS}
style={{ width: '100%', height: '100vh' }}
>
<TileLayer
url="https://{s}.tile.osm.org/{z}/{x}/{y}.png"
attribution='© <a href="https://osm.org/copyright">OpenStreetMap</a> contributors'
/>
<div className="leaflet-top leaflet-left">
<ZoomControlWithReset bounds={bounds} />
</div>
</MapContainer>
);
}
export default MyMap;
Things to note:
-
Lines 6-9: In Leaflet, bounds can be specified with two coordinate pairs, in which the first element is the top-left corner and the second element is the bottom-right. In your case, the bounds will probably be those of the rectangle that contains all of your map’s points.
Here’s a function I’ve written to calculate the bounds of an array of points:
function boundsForPoints(points) { if (points.length === 0) return []; const [latitude, longitude] = points[0]; const bounds = [ [latitude, longitude], [latitude, longitude], ]; points.forEach(([latitude, longitude]) => { bounds[0][0] = Math.min(bounds[0][0], latitude); bounds[0][1] = Math.min(bounds[0][1], longitude); bounds[1][0] = Math.max(bounds[1][0], latitude); bounds[1][1] = Math.max(bounds[1][1], longitude); }); return bounds; }