Logo
Published on

OpenCage API and ArcGIS API for JavaScript

Authors
cover

This post was previously published on DEV.TO on the 10th May 2020.

How to use the OpenCage Data Geocoder API with ArcGIS API for JavaScript

This tutorial will walk you through how to create a map with ArcGIS API for JavaScript and a custom search widget using OpenCage Data Geocoder API.

Get started

  1. We will need a OpenCage Data API key, it is free, signup for your own key here

  2. Your favorite editor: local or online

Local

The Mozilla Developer Network has an excellent guide on setting up a local development server.

Online

You can use sites such as CodeSandbox, JS Bin, CodePen and our own ArcGIS API for JavaScript sandbox

Tutorial

Reference the ArcGIS API for JavaScript

First, set up a basic HTML document:

<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <title>ArcGIS Search widget with OpenCage Data API</title>
  </head>

  <body></body>
</html>

Inside the <head> tag, reference the ArcGIS API for JavaScript using a <link> tag:

<link rel="stylesheet" href="https://js.arcgis.com/4.15/esri/themes/light/main.css" />

Inside the <body> tag, reference the ArcGIS API for JavaScript using <script> tag:

<script src="https://js.arcgis.com/4.15/"></script>

Create a map

In the <head> section add a <style> tag:

<style>
  html,
  body,
  #viewDiv {
    padding: 0;
    margin: 0;
    height: 100%;
    width: 100%;
  }
</style>

In the <body> section add a <div> tag before the <script> tag. This <div> will be the map view container:

<div id="viewDiv"></div>

At the end of the <body>, add a <script> tag and an AMD require statement to load the Map and MapView

<script>
  require(['esri/Map', 'esri/views/MapView'], function (Map, MapView) {
    var map = new Map({
      basemap: 'topo',
    })

    var view = new MapView({
      container: 'viewDiv',
      map: map,
      center: [-2.547855, 54.00366], // lon, lat
      scale: 4000000,
    })
  })
</script>

Run your code to view a map centered on the United Kingdom

Add the search widget

In the require statement, add a reference to the Search module.

require([ 'esri/Map', 'esri/views/MapView', 'esri/widgets/Search' ],
function(Map, MapView, Search) {

At the end of the code in the main function, create a Search widget and set the view. Add the widget to the top right corner of the view.

// Search widget
var searchWidget = new Search({
  view: view,
})

view.ui.add(searchWidget, 'top-right')

Run the code and try searching for the following:

  • London
  • Buckingham Palace
  • ///index.home.raft (sorry joking, it is a different example)
  • -0.20358600,51.521251

Add the custom source : OpenCage Data

In the require statement, add references to the modules:

  • SearchSource the custom source,
  • Graphic so the Search widget can display a point result,
  • Point used with the Graphic,
  • geometryEngine to compute a buffer around the searched location,
  • request for the API request to OpenCage Data API.
require([
  'esri/Map',
  'esri/views/MapView',
  'esri/widgets/Search',
  'esri/widgets/Search/SearchSource',
  'esri/Graphic',
  'esri/geometry/Point',
  'esri/geometry/geometryEngine',
  'esri/request',
], function (Map, MapView, Search, SearchSource, Graphic, Point, geometryEngine, esriRequest) {
  // ...
})

Create a function for the geocoding operations at the beginning of the module (AMD require is only here for hint purpose):

require([
  // ...
  'esri/request',
], function (
  // ...
  esriRequest
) {
  var API_KEY = 'YOUR-API-KEY'
  var OPENCAGEDATA_URL = 'https://api.opencagedata.com/geocode/v1/json'

  function geocode(options) {
    return esriRequest(OPENCAGEDATA_URL, {
      query: {
        key: API_KEY,
        q: options.query,
        proximity: options.proximity,
        no_annotations: 1,
        limit: 6,
      },
      responseType: 'json',
    })
  }
  // ...
})

Before the Search widget create the custom SearchSource:

// Custom SearchSource
var opencageSearchSource = new SearchSource({
  placeholder: 'example: W10',
  minSuggestCharacters: 3,
  getSuggestions: function (params) {
    var address = params.suggestTerm.replace(/ /g, '+')
    return geocode({
      query: address,
      proximity: view.center.latitude + ',' + view.center.longitude,
    }).then(function (response) {
      var suggestions = response.data.results.map(function (feature) {
        return {
          key: 'name',
          text: feature.formatted,
          location: {
            longitude: feature.geometry.lng,
            latitude: feature.geometry.lat,
          },
          sourceIndex: params.sourceIndex,
        }
      })
      return suggestions
    })
  },
  // Provide a getResults method to find
  // results from the suggestions, the device location or the text input
  getResults: function (params) {
    var query
    // Perform a different query if a location is provided
    // HTML5 device location or suggestion selected
    if (params.location) {
      query = params.location.latitude + ',' + params.location.longitude
    } else {
      query = params.suggestResult.text.replace(/ /g, '+')
    }

    return geocode({
      query: query,
    }).then(function (results) {
      // Parse the results of your custom search
      var searchResults = results.data.results.map(function (feature) {
        // Create a Graphic the Search widget can display
        var graphic = new Graphic({
          geometry: new Point({
            x: feature.geometry.lng,
            y: feature.geometry.lat,
          }),
          attributes: {
            name: feature.formatted,
            label: feature.formatted,
            props: feature.properties,
          },
        })
        var buffer = geometryEngine.geodesicBuffer(graphic.geometry, 250, 'meters')
        // Return a Search Result
        var searchResult = {
          extent: buffer.extent,
          feature: graphic,
          name: feature.formatted,
        }
        return searchResult
      })

      // Return an array of Search Results
      return searchResults
    })
  },
})

Update the search widget, disabling the Esri's World Geocoder:

var searchWidget = new Search({
  view: view,
  sources: [opencageSearchSource],
  includeDefaultSources: false,
})

Congratulations, you're done!

Your app should look something like this:

Screenshot

Try searching for the following:

  • London
  • Buckingham Palace
  • 51.521251,-0.20358600 (yes here it is latitude, longitude)

Thank for reading 🙏

You can find the sources in this GitHub repository

  • tutorial.html this step by step tutorial where you only have to change YOUR-API-KEY,
  • index.html an advanced version with an API key prompt, using localStorage for further usage and a basemap widget, as you can also be an addict to the National Geographic basemap, and split files (html, css, js).

Was this post helpful? Don't forget to share because Sharing is Caring.

Resources