Angelos Orfanakos

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();
        }}
      >
        &#x21ba;      </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 and zoomResetTitle 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='&copy; <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;
    }