MapSnippets logoMapSnippets logo

Draw Polygons with MapLibre GL JS

MapLibre GL JS

Draw polygons on the map by clicking to place vertices and updating a GeoJSON source in real time.

progress_activityLoading map...
const map = new maplibregl.Map({
  container: 'map',
  style: 'https://api.maptiler.com/maps/streets-v4/style.json?key=YOUR_MAPTILER_KEY',
  center: [14.42, 50.08],
  zoom: 13
});
const coords = [];
const geojson = { type: 'Feature', geometry: { type: 'Polygon', coordinates: [[]] } };
map.on('load', () => {
  map.addSource('draw', { type: 'geojson', data: geojson });
  map.addLayer({ id: 'draw-fill', type: 'fill', source: 'draw', paint: { 'fill-color': '#2196F3', 'fill-opacity': 0.2 } });
  map.addLayer({ id: 'draw-line', type: 'line', source: 'draw', paint: { 'line-color': '#2196F3', 'line-width': 2 } });
  map.addSource('vertices', { type: 'geojson', data: { type: 'FeatureCollection', features: [] } });
  map.addLayer({ id: 'draw-points', type: 'circle', source: 'vertices', paint: { 'circle-radius': 5, 'circle-color': '#fff', 'circle-stroke-width': 2, 'circle-stroke-color': '#2196F3' } });
});
map.on('click', (e) => {
  coords.push([e.lngLat.lng, e.lngLat.lat]);
  const ring = [...coords, coords[0]];
  geojson.geometry.coordinates = [ring];
  map.getSource('draw').setData(JSON.parse(JSON.stringify(geojson)));
  map.getSource('vertices').setData({ type: 'FeatureCollection', features: coords.map(c => ({ type: 'Feature', geometry: { type: 'Point', coordinates: c } })) });
});
document.getElementById('finish-btn').addEventListener('click', () => {
  if (coords.length < 3) return;
  map.off('click');
  map.getCanvas().style.cursor = '';
});

How it works

This example implements polygon drawing without external plugins. Each map click adds a vertex to a coordinate array. The polygon is rendered by closing the ring (appending the first coordinate) and updating the GeoJSON source.

Three layers visualize the drawing: a fill layer for the polygon interior, a line layer for the border, and a circle layer for individual vertices. All three update in real time as the user clicks.

Clicking the “Finish Drawing” button removes the click listener to finalize the polygon. With 3 or more vertices, you have a valid polygon.

Key APIs

API Description Docs
source.setData() Update GeoJSON source data maplibre.org docs
map.on('click') Listen for map clicks maplibre.org docs
map.off('click') Remove click listener maplibre.org docs

FAQ

How do I calculate the polygon area?

Use a library like Turf.js: turf.area(polygon) returns the area in square meters.

Can I edit vertices after drawing?

This basic implementation does not support vertex editing. For full draw/edit functionality, consider implementing drag handlers on vertex points.

Common mistakes

  • Not closing the polygon ring: append the first coordinate to the end of the array
  • Forgetting to deep-copy the GeoJSON before setData(): MapLibre may cache references
  • Allowing polygon creation with fewer than 3 points: 2 points or fewer cannot form a valid polygon
auto_awesome

Build maps using MapLibre GL JS with AI

Generate code like this draw polygons with maplibre gl js example using the MapLibre GL JS skill in your IDE.