Angelos Orfanakos

MouseCoordinates 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 third 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 MouseCoordinates component which renders the latitude and longitude that the mouse pointer hovers on and allows the user to copy them by pressing Ctrl-C. You can see it in action on vouna.gr.

First, the component:

import React from 'react';
import { useMapEvents } from 'react-leaflet';

function round(number, precision = 0) {
  return (
    Math.round(number * Math.pow(10, precision) + Number.EPSILON) /
    Math.pow(10, precision)
  );
}

function formatLatitude(latitude) {
  const direction = latitude > 0 ? 'N' : 'S';
  return `${round(Math.abs(latitude), 6)}° ${direction}`;
}

function formatLongitude(longitude) {
  const direction = longitude > 0 ? 'E' : 'W';
  return `${round(Math.abs(longitude), 6)}° ${direction}`;
}

function MouseCoordinates() {
  const [mousePoint, setMousePoint] = React.useState(null);

  const formattedCoordinates =
    mousePoint === null
      ? ''
      : `${formatLatitude(mousePoint.lat)}, ${formatLongitude(mousePoint.lng)}`;
  React.useEffect(
    function copyToClipboard() {
      function handleCtrlCKeydown(event) {
        if (
          event.key === 'c' &&          event.ctrlKey &&          formattedCoordinates.length > 0 &&
          navigator.clipboard
        ) {
          navigator.clipboard.writeText(formattedCoordinates);
        }
      }

      document.addEventListener('keydown', handleCtrlCKeydown);

      return function cleanup() {
        document.removeEventListener('keydown', handleCtrlCKeydown);
      };
    },
    [formattedCoordinates]
  );

  useMapEvents({
    mousemove(event) {
      setMousePoint(event.latlng);
    },
    mouseout() {      setMousePoint(null);    },  });

  if (formattedCoordinates.length === 0) return null;
  return (
    <div className="leaflet-control-attribution leaflet-control">       {formattedCoordinates}
    </div>
  );
}

export default MouseCoordinates;

Things to note:

  • Line 27: formatLatitude and formatLongitude are used to format the coordinates
  • Lines 33-34: It is possible to copy the coordinates by pressing Ctrl-C
  • Lines 55-57, 60: When the mouse pointer leaves the map, no coordinates are shown
  • Line 63: The component is agnostic regarding its corner/position in the map. Instead, it is expected that you wrap it with a <div> that has the desired position classes.

And here’s how you’d use it in a map:

import React from 'react';

import MouseCoordinates from './MouseCoordinates';

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-bottom leaflet-left">         <MouseCoordinates />
      </div>
    </MapContainer>
  );
}

export default MyMap;

Things to note:

  • Line 20: In this example, the coordinates are shown on the bottom-left corner of the map