Skip to content

Create a Draggable Point ​

Allow users to drag a GeoJSON point on the map using mouse events.

Click and drag the point to move it

How It Works ​

  1. Add a GeoJSON Point source and display it as a circle layer
  2. Listen for mousedown on the layer to start dragging
  3. On mousemove, update the point position with setData()
  4. On mouseup, end the drag and re-enable map panning

Key Pattern ​

javascript
let isDragging = false;

// Start drag when user clicks the point
map.on('mousedown', 'my-point', (e) => {
  e.preventDefault();
  isDragging = true;
  map.dragPan.disable(); // prevent map from panning
  map.getCanvas().style.cursor = 'grabbing';
});

// Move point while dragging
map.on('mousemove', (e) => {
  if (!isDragging) return;
  map.getSource('my-point').setData({
    type: 'Feature',
    geometry: { type: 'Point', coordinates: [e.lngLat.lng, e.lngLat.lat] },
    properties: {}
  });
});

// End drag
map.on('mouseup', () => {
  isDragging = false;
  map.dragPan.enable();
  map.getCanvas().style.cursor = '';
});

Complete Example ​

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <link href="https://cdn.mapmetrics-atlas.net/versions/latest/mapmetrics-gl.css" rel="stylesheet" />
    <script src="https://cdn.mapmetrics-atlas.net/versions/latest/mapmetrics-gl.js"></script>
    <style>#map { height: 500px; width: 100%; }</style>
  </head>
  <body>
    <div id="map"></div>
    <p id="info">Drag the point</p>
    <script>
      const map = new mapmetricsgl.Map({
        container: 'map',
        style: '<StyleFile_URL_with_Token>',
        center: [0, 20],
        zoom: 2
      });

      map.on('load', () => {
        map.addSource('point', {
          type: 'geojson',
          data: { type: 'Feature', geometry: { type: 'Point', coordinates: [0, 20] }, properties: {} }
        });

        map.addLayer({
          id: 'point',
          type: 'circle',
          source: 'point',
          paint: { 'circle-radius': 10, 'circle-color': '#3b82f6', 'circle-stroke-width': 3, 'circle-stroke-color': '#fff' }
        });

        let dragging = false;

        map.on('mousedown', 'point', (e) => {
          e.preventDefault();
          dragging = true;
          map.dragPan.disable();
          map.getCanvas().style.cursor = 'grabbing';
        });

        map.on('mousemove', (e) => {
          if (!dragging) return;
          map.getSource('point').setData({
            type: 'Feature',
            geometry: { type: 'Point', coordinates: [e.lngLat.lng, e.lngLat.lat] },
            properties: {}
          });
          document.getElementById('info').textContent =
            `Lng: ${e.lngLat.lng.toFixed(4)}, Lat: ${e.lngLat.lat.toFixed(4)}`;
        });

        map.on('mouseup', () => {
          dragging = false;
          map.dragPan.enable();
          map.getCanvas().style.cursor = '';
        });
      });
    </script>
  </body>
</html>
jsx
import React, { useEffect, useRef } from 'react';
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';

const DraggablePoint = () => {
  const mapContainer = useRef(null);
  const map = useRef(null);
  const isDragging = useRef(false);
  const [coords, setCoords] = React.useState([0, 20]);

  useEffect(() => {
    if (map.current) return;

    map.current = new mapmetricsgl.Map({
      container: mapContainer.current,
      style: '<StyleFile_URL_with_Token>',
      center: [0, 20],
      zoom: 2
    });

    map.current.on('load', () => {
      map.current.addSource('point', {
        type: 'geojson',
        data: { type: 'Feature', geometry: { type: 'Point', coordinates: [0, 20] }, properties: {} }
      });

      map.current.addLayer({
        id: 'point',
        type: 'circle',
        source: 'point',
        paint: { 'circle-radius': 10, 'circle-color': '#3b82f6', 'circle-stroke-width': 3, 'circle-stroke-color': '#fff' }
      });

      map.current.on('mousedown', 'point', (e) => {
        e.preventDefault();
        isDragging.current = true;
        map.current.dragPan.disable();
        map.current.getCanvas().style.cursor = 'grabbing';
      });

      map.current.on('mousemove', (e) => {
        if (!isDragging.current) return;
        const newCoords = [e.lngLat.lng, e.lngLat.lat];
        map.current.getSource('point').setData({
          type: 'Feature',
          geometry: { type: 'Point', coordinates: newCoords },
          properties: {}
        });
        setCoords(newCoords);
      });

      map.current.on('mouseup', () => {
        isDragging.current = false;
        map.current.dragPan.enable();
        map.current.getCanvas().style.cursor = '';
      });
    });

    return () => { map.current?.remove(); map.current = null; };
  }, []);

  return (
    <div>
      <div ref={mapContainer} style={{ height: '500px', width: '100%' }} />
      <p>Position: [{coords[0].toFixed(4)}, {coords[1].toFixed(4)}]</p>
    </div>
  );
};

export default DraggablePoint;

For more information, visit the MapMetrics GitHub repository.