MapSnippets logoMapSnippets logo

Reverse Geocoding

Click the map to get the address at any location Each library has its own approach: here's how they compare.

Quick Recommendations

Leaflet — Best when you want a simple click-to-address flow with the result shown in a Leaflet popup at the clicked location.

MapLibre GL JS — Ideal when reverse geocoding integrates with data-driven map interactions and you need the result as structured GeoJSON.

OpenLayers — The right choice when click coordinates need projection transformation and results display in an ol.Overlay popup.

MapTiler SDK — Works best for the simplest API: the SDK's geocoding helpers handle the API call with the pre-configured API key.

At a Glance

LeafletMapLibreOpenLayersSDK
Coordinates[lat, lng][lng, lat]fromLonLat([lng, lat])[lng, lat]
RenderingDOM + Canvas (2D)WebGL (GPU, 60fps)Canvas + WebGLWebGL (GPU, 60fps)
Bundle size~42 KB gzipped~250 KB gzipped~290 KB gzipped (tree-shakeable)~280 KB gzipped (includes MapLibre)
Learning curveEasiest (simple API, great tutorials)Moderate (source/layer model, expressions)Steepest (projections, OGC standards, verbose API)Easy (MapLibre API with convenience helpers)
3D supportNo native 3D (2D only)Full: terrain, fill-extrusion, globe, skyLimited: WebGL layers, no native globeFull (same as MapLibre)
Code lines14202215

Code Snippets

Click a library to expand its code. Each snippet shows only the core logic: the scaffold handles HTML boilerplate.

Leaflet14 linesShow code
arrow_forwardFull example
const map = L.map('map').setView([48.8566, 2.3522], 13);
L.tileLayer('https://api.maptiler.com/maps/streets-v4/256/{z}/{x}/{y}.png?key=YOUR_MAPTILER_KEY', {
  tileSize: 256,
  attribution: '&copy; <a href="https://www.maptiler.com/">MapTiler</a> &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
}).addTo(map);
let marker;
map.on('click', async (e) => {
  const { lat, lng } = e.latlng;
  const res = await fetch(`https://api.maptiler.com/geocoding/${lng},${lat}.json?key=YOUR_MAPTILER_KEY`);
  const data = await res.json();
  const label = data.features.length ? data.features[0].place_name : 'Unknown location';
  if (marker) marker.remove();
  marker = L.marker([lat, lng]).addTo(map).bindPopup(label).openPopup();
});
MapLibre GL JS20 linesShow code
arrow_forwardFull example
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: 12
});
map.addControl(new maplibregl.NavigationControl());
let marker;
map.on('click', async (e) => {
  const { lng, lat } = e.lngLat;
  const res = await fetch(`https://api.maptiler.com/geocoding/${lng},${lat}.json?key=YOUR_MAPTILER_KEY`);
  const data = await res.json();
  if (marker) marker.remove();
  const name = data.features.length ? data.features[0].place_name : 'Unknown location';
  marker = new maplibregl.Marker({ color: '#F44336' })
    .setLngLat(e.lngLat)
    .setPopup(new maplibregl.Popup({ offset: 25 }).setHTML(`<b>${name}</b><br>${lng.toFixed(5)}, ${lat.toFixed(5)}`))
    .addTo(map);
  marker.togglePopup();
});
OpenLayers22 linesShow code
arrow_forwardFull example
var map = new ol.Map({
  target: 'map',
  layers: [
    new ol.layer.Tile({ source: new ol.source.XYZ({ url: 'https://api.maptiler.com/maps/streets-v4/256/{z}/{x}/{y}.png?key=YOUR_MAPTILER_KEY', attributions: '&copy; <a href="https://www.maptiler.com/copyright/">MapTiler</a>' }) })
  ],
  view: new ol.View({ center: ol.proj.fromLonLat([2.3522, 48.8566]), zoom: 13 })
});
var popupEl = document.getElementById('popup');
var overlay = new ol.Overlay({ element: popupEl, positioning: 'bottom-center', offset: [0, -5] });
map.addOverlay(overlay);
map.on('click', function(evt) {
  var lonlat = ol.proj.toLonLat(evt.coordinate);
  popupEl.style.display = 'block';
  popupEl.textContent = 'Loading...';
  overlay.setPosition(evt.coordinate);
  fetch('https://api.maptiler.com/geocoding/' + lonlat[0].toFixed(6) + ',' + lonlat[1].toFixed(6) + '.json?key=YOUR_MAPTILER_KEY')
    .then(function(r) { return r.json(); })
    .then(function(data) {
      popupEl.textContent = data.features.length ? data.features[0].place_name : 'No address found';
    })
    .catch(function() { popupEl.textContent = 'Error fetching address'; });
});
MapTiler SDK15 linesShow code
arrow_forwardFull example
maptilersdk.config.apiKey = 'YOUR_MAPTILER_KEY';
const map = new maptilersdk.Map({
  container: 'map',
  style: maptilersdk.MapStyle.STREETS,
  center: [2.3522, 48.8566],
  zoom: 13
});
const popup = new maptilersdk.Popup();
map.on('click', async (e) => {
  const { lng, lat } = e.lngLat;
  const res = await fetch(`https://api.maptiler.com/geocoding/${lng},${lat}.json?key=YOUR_MAPTILER_KEY`);
  const data = await res.json();
  const name = data.features.length ? data.features[0].place_name : 'No result';
  popup.setLngLat([lng, lat]).setHTML(`<b>${name}</b><br>${lng.toFixed(5)}, ${lat.toFixed(5)}`).addTo(map);
});

Things to Know

Implementation-specific details that matter for reverse geocoding.

Leaflet

Click event provides [lat, lng] which must be swapped to [lng, lat] for the MapTiler Geocoding API query parameter.

MapLibre GL JS

Click event gives { lng, lat } directly in the correct order for the MapTiler API; results shown in a Popup.

OpenLayers

Click coordinates are in EPSG:3857 and must be transformed to EPSG:4326 with ol.proj.toLonLat() before the API call.

MapTiler SDK

Identical click event format to MapLibre; the SDK adds no reverse geocoding conveniences beyond the shared API.

Which Library to Use?

auto_awesome

Build Maps with AI

Generate code like these examples using mapping library skills in your IDE.