Logo
Published on

Geocode with OpenCage and MapLibre

Authors

Originaly published on dev.to

cover

OpenCage is an API Provider offering two services a geocoding service and a geosearch service

MapLibre is an Open-source mapping libraries for web and mobile app developers.

In this article we will learn how to geocode with OpenCage and the maplibre-gl-geocoder plugin.

First, one need an OpenCage API Key, it is free for testing, signup for your own key here.

This example will be based on a simple HTML page.

The header will then contain a viewport:

<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />

Some CSS and JS for MapLibre and the geocoder plugin

<!-- STYLES -->
<link href="https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.css" rel="stylesheet" />
<link
  href="https://unpkg.com/@maplibre/maplibre-gl-geocoder@1.2.0/dist/maplibre-gl-geocoder.css"
  rel="stylesheet"
/>
<!-- scripts -->
<script src="https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.js"></script>
<script src="https://unpkg.com/@maplibre/maplibre-gl-geocoder@1.2.0/dist/maplibre-gl-geocoder.min.js"></script>
<!-- CSS -->
body { margin: 0; padding: 0; } #map { position: absolute; top: 0; bottom: 0; width: 100%; }

Body

Now let's look at the body of this HTML page

<div id="map"></div>

Pretty sleek, isn't it?

Script

Map

Indeed the magic happens in JS. The first part is the map using MapLibre GL JS. Here, I used a simple openstreetmap basemap using a raster source. The SDK offers various sources link.

var map = new maplibregl.Map({
  container: 'map',
  // Use a minimalist raster style
  style: {
    version: 8,
    name: 'Blank',
    center: [0, 0],
    zoom: 0,
    sources: {
      'raster-tiles': {
        type: 'raster',
        tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
        tileSize: 256,
        minzoom: 0,
        maxzoom: 19,
      },
    },
    layers: [
      {
        id: 'background',
        type: 'background',
        paint: {
          'background-color': '#e0dfdf',
        },
      },
      {
        id: 'simple-tiles',
        type: 'raster',
        source: 'raster-tiles',
      },
    ],
    id: 'blank',
  },
  center: [0, 51],
  zoom: 4,
  antialias: true,
})

Geocode

We define the geocoder API. From the documentation Any geocoder api that supports the functions reverseGeocode and forwardGeocode and returns a response which includes a FeatureCollection of results

We use the OpenCage API and its GeoJSON format, but as you can see, because this is JS, one can use the classical JSON response from the API and transform into GeoJSON.

// Replace it with your API Key.
// The test key returns only
// "Friedrich-Ebert-Straße 7, 48153 Münster, Germany"
// whatever the input is
var YOUR_API_KEY = '6d0e711d72d74daeb2b0bfd2a5cdfdba'

var geocoder_api = {
  forwardGeocode: async (config) => {
    const features = []
    try {
      let request =
        'https://api.opencagedata.com/geocode/v1/geojson?q=' +
        config.query +
        '&key=' +
        YOUR_API_KEY +
        '&no_annotations=1'
      const response = await fetch(request)
      const geojson = await response.json()
      for (let feature of geojson.features) {
        let point = {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: feature.geometry.coordinates,
          },
          place_name: feature.properties.formatted,
          properties: feature.properties,
          text: feature.properties.formatted,
          place_type: ['place'],
        }
        features.push(point)
      }
    } catch (e) {
      console.error(`Failed to forwardGeocode with error: ${e}`)
    }

    return {
      features: features,
    }
  },
}

now add the Geocoder API to the plugin and attached it to the map

map.addControl(
  new MaplibreGeocoder(geocoder_api, {
    maplibregl: maplibregl,
  })
)

and we are all set.

Demo

A live example using the test API key (returning only Münster) is available on codepen

The end

Thanks for reading 🙏

Resources