Hands On

Reverse Geocoding a location using Golang

By Vidhan Bhonsle | 25 February 2021

Try HERE Maps

Create a free API key to build location-aware apps and services.

Get Started

Across the globe and for a variety of business reasons, billions of devices are collecting GPS data. That data is usually a set of unique numbers representing geographic coordinates of a location. These numbers are known as latitude and longitude and they each represent a specific point, somewhere on earth. To make more sense of it, we need to resolve it into the closest street address. In order to do exactly this, we use the reverse Geocode endpoint of the HERE Geocoding and Search API.

Lets say you are trying to understand the geographical coordinates; 48.2181679, 16.3899064. Once you pass them through the API, it gives us the nearest address, which is "Heinestraße 42, 1020 Vienna, Austria".

In this blog post we are going to use the reverse geocoding feature of the HERE Geocoding and Search API and the Go programming language.

Prerequisites

Your machine should have Golang installed (you can get it from here) and if you haven't done so yet, you will need to sign up for a Freemium account on developer.here.com. Once you have signed up, you can generate your free API key and get 250.000 free transactions every month.

Working with Reverse Geocoding Endpoint

The reverse geocoding endpoint of the Geocoding and Search API helps in finding the nearest possible address to a specific geographic coordinate. All you need is to append the latitude and longitude values with your API key to the endpoint URL.


https://revgeocode.search.hereapi.com/v1/revgeocode
?at=12.98022751,77.60104064
&apikey=YOUR_API_KEY

The apikey is from the Freemium account generated on the developer portal and the value of at is latitude and longitude value separated by a comma.
The REST API returns a JSON response containing nearest address along with additional details.


{
  "items": [
    {
      "title": "Creativity",
      "id": "here:pds:place:356jx7ps-6b18784695c70f5001df83da965f2570",
      "resultType": "place",
      "address": {
        "label": "Creativity, Shivajinagar, Bengaluru 560001, India",
        "countryCode": "IND",
        "countryName": "India",
        "stateCode": "KA",
        "state": "Karnataka",
        "county": "Bengaluru",
        "city": "Bengaluru",
        "district": "Shivajinagar",
        "postalCode": "560001"
      },
      "position": {
        "lat": 12.98023,
        "lng": 77.60094
      },
      "access": [
        {
          "lat": 12.98022,
          "lng": 77.60093
        }
      ],
      "distance": 11,
      "categories": [
        {
          "id": "800-8200-0174",
          "name": "Школа",
          "primary": true
        }
      ]
    }
  ]
}

From the above response, all we need is the value associated with ‘title’. It is the nearest address to the latitude longitude pair.

Parsing JSON Response using Golang

To extract the address from the JSON response, we first need to make the API call programmatically. Then parse the response to take out a specific value.
We are using “net/http” to access the core go http functionality and make HTTP request.


// File name: final.go
package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

var apikey = "YOUR_API_KEY"
var latitude = 42.36399
var longitude = -71.05493
var address string

func main() {
    url := "https://revgeocode.search.hereapi.com/v1/revgeocode?apiKey=" + apikey + "&at=" + fmt.Sprint(latitude) + "," + fmt.Sprint(longitude)

    res, err := http.Get(url)
    if err != nil {
        log.Fatalln(err)
    }
    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        log.Fatalln(err)
    }

    fmt.Println(string(body))
}

When you provide your API key above (var apikey value) and run the code.
go run final.go
You will get the complete JSON response.


{"items":[{"title":"25 Parmenter St, Boston, MA 02113-2306, United States","id":"here:af:streetsection:vuwDn8KJgOmAHcM1iubqIA:CgcIBCDgs7IhEAEaAjI1","resultType":"houseNumber","houseNumberType":"PA","address":{"label":"25 Parmenter St, Boston, MA 02113-2306,
United States","countryCode":"USA","countryName":"United States","stateCode":"MA","state":"Massachusetts","county":"Suffolk","city":"Boston","district":"North End","street":"Parmenter St","postalCode":"02113-2306","houseNumber":"25"},"position":{"lat":42.36408,"lng":-71.0549},"access":[{"lat":42.36391,"lng":-71.05507}],"distance":10,"mapView":{"west":-71.05556,"south":42.36367,"east":-71.05466,"north":42.36411}}]}

We need to take out the value associated with ‘title’ as it will be our address. Let us do that by adding a struct above main function.


type output struct {
    Items []struct {
        Title string `json:"title"`
    } `json:"items"`
}

We have added a struct called output, which contains minimal required fields to acquire address. We have mapped JSON response in a struct which Go can understand. If you want to explore it more, I will suggest looking at JSON to go converter.
Next, we have to iterate through the response data and take out required values. Add following code inside main function, below “fmt.Println(string(body))


var data output
    json.Unmarshal(body, &data)
    // fmt.Println(data)
    for _, add := range data.Items {
        address = add.Title
        fmt.Printf(address)
    }

To convert JSON data into Go objects, we make use of ‘Unmarshal’ method. On running the code, we get the address

“25 Parmenter St, Boston, MA 02113-2306, United States”

Creating Go web server

What makes location data so interesting is that we can visualize it on a map and do whole bunch of other stuff with it. Now, we have all the values needed (latitude, longitude, address, API key) to show our address on a map in a browser. We need to run Go as a web server. Golang lets you create a web server with net/http package. Add following lines at the bottom of main function.


    http.HandleFunc("/", MapPage)
    log.Fatal(http.ListenAndServe(":8080", nil))

We are calling a new function, MapPage and also listening/serving on localhost at 8080 port.
To show the address on a map, we will be passing latitude, longitude, address and API key to the HTML page (create map.html file in the same place as final.go file). This will be handled by MapPage function and a new struct, namely, MapValues.


//Add it above main function
type MapValues struct {
    API       string
    Latitude  float64
    Longitude float64
    Address   string
}

//Add it below main function
func MapPage(w http.ResponseWriter, r *http.Request) {
    Values := MapValues{
        API:       apikey,
        Latitude:  latitude,
        Longitude: longitude,
        Address:   address,
    }
    t, err := template.ParseFiles("map.html") //parse the html file homepage.html
    if err != nil {                           // if there is an error
        log.Print("template parsing error: ", err) // log it
    }
    err = t.Execute(w, Values) //execute the template and pass it the HomePageVars struct to fill in the gaps
    if err != nil {            // if there is an error
        log.Print("template executing error: ", err) //log it
    }
}

The above code is passing values to map.html file and handling a bunch of errors in process.

Creating HERE Map in HTML and JavaScript

A HTML file with JavaScript code is enough to plot a beautiful looking map. The file name is map.html.


<html>  
<head>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<script src="https://js.api.here.com/v3/3.1/mapsjs-core.js"type="text/javascript" charset="utf-8"></script>
<script src="https://js.api.here.com/v3/3.1/mapsjs-service.js"type="text/javascript" charset="utf-8"></script>
<script src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js" type="text/javascript" charset="utf-8"></script>
<script src="https://js.api.here.com/v3/3.1/mapsjs-ui.js" type="text/javascript" charset="utf-8"></script>
<link rel="stylesheet" type="text/css"href="https://js.api.here.com/v3/3.1/mapsjs-ui.css" />
</head>
  
<body style='margin: 0'>
<div style="width: 100%; height: 100%" id="mapContainer"></div>

<script>	
      // Initialize the platform object:
      var platform = new H.service.Platform({
        'apikey': '{{apikey}}'
      });
	  
	   const lat = {{latitude}};
	   const lon = {{longitude}};
	
	// Obtain the default map types from the platform object
      var maptypes = platform.createDefaultLayers();

      // Initialize a map:
      var map = new H.Map(
        document.getElementById('mapContainer'),
        maptypes.raster.terrain.map,
        {
          zoom: 17,
          center: { lat: lat, lng: lon }  
        });
		
	// Enable the event system on the map instance:
	  var mapEvents = new H.mapevents.MapEvents(map);
	
	// Instantiate the default behavior, providing the mapEvents object:
	 var behavior = new H.mapevents.Behavior(mapEvents);

	//window.addEventListener('resize',()=> get.ViewProt().resize())
	var marker = new H.map.Marker({ lat: lat, lng: lon });
		
	// Add the marker to the map:
	map.addObject(marker);
	
	// Create the default UI:
	var ui = H.ui.UI.createDefault(map, maptypes);
	
	// Add event listener to the marker:
	marker.addEventListener('tap', function(evt) {
    
		// Create an info bubble object at a specific geographic location:
		var bubble = new H.ui.InfoBubble({ lng: lon, lat: lat }, {
                content: '<p> <h4>Address:</h4>{{address}}</p>'
             });

		// Add info bubble to the UI:
		ui.addBubble(bubble);
	});	

</script>
</body>
</html>

The code plots the latitude and longitude on a map and shows a marker on the location. When the marker is clicked, a box pops up, showing the address we input. The detailed information of Map API can be found here and of Marker, here.

Complete Golang code


// File name: final.go
package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
)

var apikey = "YOUR_API_KEY"
var latitude = 42.36399
var longitude = -71.05493
var address string

type output struct {
	Items []struct {
		Title string `json:"title"`
	} `json:"items"`
}

type MapValues struct {
	API       string
	Latitude  float64
	Longitude float64
	Address   string
}

func main() {
	url := "https://revgeocode.search.hereapi.com/v1/revgeocode?apiKey=" + apikey + "&at=" + fmt.Sprint(latitude) + "," + fmt.Sprint(longitude)

	res, err := http.Get(url)
	if err != nil {
		log.Fatalln(err)
	}
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatalln(err)
	}

	// fmt.Println(string(body))
	var data output
	json.Unmarshal(body, &data)
	// fmt.Println(data)
	for _, add := range data.Items {
		address = add.Title
		fmt.Printf(address)
	}

	http.HandleFunc("/", MapPage)
	log.Fatal(http.ListenAndServe(":8080", nil))

}

func MapPage(w http.ResponseWriter, r *http.Request) {
	Values := MapValues{
		API:       apikey,
		Latitude:  latitude,
		Longitude: longitude,
		Address:   address,
	}
	t, err := template.ParseFiles("map.html") //parse the html file homepage.html
	if err != nil {                           // if there is an error
		log.Print("template parsing error: ", err) // log it
	}
	err = t.Execute(w, Values) //execute the template and pass it the HomePageVars struct to fill in the gaps
	if err != nil {            // if there is an error
		log.Print("template executing error: ", err) //log it
	}
}

Running the code

Run the code using any IDE that supports Go like Visual Studio Code or else simple use command prompt or terminal to execute the code.
go run final.go
Copy the address http://localhost:8080 and paste it in the browser application.

marker

When you click on marker, the address passed in the code will pop up!

cover_picture