Creating a Golang Vessel Map Using Google Maps API

In this tutorial, you’ll learn how to create a Golang vessel map that shows real-time ship location on a map using Google Maps API and Datalastic Vessel tracking API. This article will show you a step-by-step tutorial about how to create an interactive vessel map using the Go programming language.
With Go and Datalastic API, you can easily create a live map that displays vessel routes, their current locations, and other important information in real-time. Whether you’re a shipping company or simply interested in vessel tracking, this tutorial will provide you with the knowledge and tools to create a vessel’s live map with Go and the Datalastic API. We’ll cover everything from setting up your development environment to importing necessary libraries and making the necessary components to render the map.
So, let’s dive in and create a vessel map that will allow you to track vessel routes and their locations with ease.
Setting Up Your Golang Vessel Map Project
The idea of the application is simple and exciting. A user can navigate through a map and if the user clicks on some point on the map, the application will put on the map all the vessels in a radius of 50 nautical miles around the point. Every displayed vessel also shows available info like vessel name, speed, course, MMSI, and coordinates.
As a map engine, we will use Google Maps with its JavaScript API. The platform datalastic.com with its incredible API will provide us with information about vessels’ real-time positions and status. The backend is responsible for hosting static files and handling API requests. This part will be implemented using the Go programming language.
To summarize all the steps we will need to perform to get the map working we will need to do the following:
- Set Up the Project
- Get the Datalastic API Key
- Acquire the Google Maps JavaScript API Key
- Implement the Golang Backend
- Host Static Files with the Embed Filesystem
- Handle API Requests
- Integrate the Google Maps JavaScript API
- Enable User Navigation
- Display Vessel Information
Prerequisites To Build a Vessels Map:
Before diving into building a vessel’s map, it’s essential to ensure you have the necessary prerequisites in place. This may be familiarizing yourself with the maritime API you’ll be using, and understanding the data you’ll need to incorporate into your map.
What we will need in order to proceed:
- Datalastic API KEY (Maritime API). You can easily get your own API key by submitting for the trial subscriptions.
- We need Google Maps JavaScript API KEY. You can easily get one if you follow this tutorial. Google Cloud Platform offers a really generous free tier quota for using Google Maps JavaScript API.
It’s time to discuss our scope of work for the project.
First of all, we should let users navigate through Google Maps. We will implement this using the standard HTML, JS, and CSS file pack.
Next, we should host these static files. This part will be handled by the Golang backend using the embed filesystem, introduced in Go version 1.16.
Later, we should handle the user’s clicks on the map and display the nearest vessels. This logic will be handled both on the Go and JS sides.
Importing a Map for Vessel Visualization in GO Programming Language:
We created a working directory inradius-golang and initialized a new Go project in this directory with the command:
go mod init inradius-golang
The first thing we want to do is to show the map to users. For this, we create index.html file inside the new templates directory (why we call the new folder “templates” you will know a bit later). Also, we create index.js and style.css files inside the static folder.
The content of the index.html file:
go mod init inradius-golang
The first thing we want to do is to show the map to users. For this, we create index.html file inside the new templates directory (why we call the new folder “templates” you will know a bit later). Also, we create index.js and style.css files inside the static folder.
The content of the index.html file:
<html>
<head>
<title>Vessels from Datalastic.com</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<link rel="stylesheet" type="text/css" href="static/style.css" />
<script type="module" src="static/index.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="map"></div>
<script
src="https://maps.googleapis.com/maps/api/js?key=<GM_API_KEY>&callback=initMap"
defer
></script>
</body>
</html>
We also included polyfill.min.js and axios.min.js
These libraries are necessary for map rendering and making HTTP requests. Instead of <GM_API_KEY> you should use your Google Maps API KEY.
The content of style.css and index.js files:
#map {
height: 100%;
}
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
let map;
function initMap() {
map = new google.maps.Map(document.getElementById("map"), {
zoom: 2,
zoomControl: true,
scaleControl: true,
center: { lat: 0, lng: 0 },
});
}
window.initMap = initMap;
index.js file contains initMap() functions which initialize Google Maps map.
To host all these files we will use Golang standard library HTTP server.
For this we create main.go file in the project root directory with the following content:
package main
import (
"embed"
"html/template"
"log"
"net/http"
"os"
)
//go:embed static
var static embed.FS
//go:embed templates
var templates embed.FS
var indexTemplate *template.Template
var gmAPIKey string
func main() {
gmAPIKey = os.Getenv("GM_API_KEY")
staticFS := http.FS(static)
staticFileServer := http.FileServer(staticFS)
var err error
indexTemplate, err = template.ParseFS(templates, "templates/index.html")
if err != nil {
log.Fatalln(err)
}
mux := http.NewServeMux()
mux.Handle("/static/", staticFileServer)
mux.HandleFunc("/", handleIndex)
http.ListenAndServe(":8080", mux)
}
func handleIndex(w http.ResponseWriter, r *http.Request) {
_ = indexTemplate.Execute(w, gmAPIKey)
}
You may notice a few features not discussed before.
We create a new embed filesystem var static embed.FS contains all files inside static directory. Then we create a new fileserver handler staticFileServer and this handler is accessible by “/static/” route.
We create new embed filesystem var templates embed.FS contains all files inside the templates directory with only one file index.html inside. The reason for naming the directory this way is that we process this file as a template. Instead of hardcoding Google Maps API KEY value in the index.html, we will pass this key value via the env variable GM_API_KEY. Then we get this env variable to gmAPIKey = os.Getenv (“GM_API_KEY”) and render the template using indexTemplate.Execute(w, gmAPIKey). The rendered index.html template is available by “/” route.
The final fix for this step is to turn index.html file into template so this file would be able to get GM_API_KEY value and use it inside the code. For this, we should modify the Google Map initialization script and replate <GM_API_KEY> value with template placeholder {{.}}.
So, it should look like this https://maps.googleapis.com/maps/api/js?key={{.}}&callback=initMap
Now if we run the application with Go run main.go command and open the browser on-page localhost:8080 we will see an error. This is because we didn’t provide Google Maps API KEY.
To do this we should run it this way:
GM_API_KEY=<your_google_map_api_key> go run main.go
Getting Vessels Coordinates in real-time With Datalastic API:
This project is possible due to the existence of the Datalastic platform with its incredible Vessel Tracking API. The API endpoint we will use is called Static Location Tracking and it allows users to specify a geographic point and to see all vessels that are currently inside the radius of the point. To use this endpoint we should send a GET HTTP request to https://api.datalastic.com/api/v0/vessel_inradius and specify the following parameters:
- Lat — latitude of selected geo-point;
- Lon — longitude of selected geo-point;
- Radius — radius of the search area. Expressed in Nautical Miles (NM);
- Api-key — Datalastic API KEY.
{
"data": {
"point": {
"lat": 41,
"lon": 1,
"radius": 50
},
"total": 1,
"vessels": [
{
"uuid": "35240a40-82d4-51f8-917e-e7bb7c408bb1",
"name": "CAMBRILS 28560 F",
"mmsi": "992241130",
"imo": null,
"eni": null,
"country_iso": "ES",
"type": "Beacon, Starboard Hand",
"type_specific": "Beacon, Starboard Hand",
"lat": 41.06011,
"lon": 1.060472,
"speed": null,
"course": null,
"heading": null,
"destination": null,
"last_position_epoch": 1675029600,
"last_position_UTC": "2023-01-29T22:00:00Z",
"eta_epoch": null,
"eta_UTC": null,
"distance": 4.53069923894873
}
]
},
"meta": {
"duration": 0.040764131,
"endpoint": "/api/v0/vessel_inradius",
"success": true
}
}
We could use this request directly from JS. But it is extremely important to keep Datalastic API KEY secure, so, we will create a new handler that will wrap Static Location Tracking Datalastic API endpoint.
The new part of Golang code looks like this:
const datalasticBaseURL = "https://api.datalastic.com/api/v0/vessel_inradius?"
var datalasticAPIKey string
...
func main() {
...
datalasticAPIKey = os.Getenv("DATALASTIC_API_KEY")
...
mux.HandleFunc("/api/get-vessels", handleGetVessels)
...
}
func handleGetVessels(w http.ResponseWriter, r *http.Request) {
lat := r.URL.Query().Get("lat")
_, err := strconv.ParseFloat(lat, 64)
if err != nil {
w.Write([]byte("lat has wrong format"))
w.WriteHeader(http.StatusBadRequest)
return
}
lon := r.URL.Query().Get("lon")
_, err = strconv.ParseFloat(lon, 64)
if err != nil {
w.Write([]byte("lon has wrong format"))
w.WriteHeader(http.StatusBadRequest)
return
}
values := url.Values{}
values.Add("lat", lat)
values.Add("lon", lon)
values.Add("radius", "50")
values.Add("api-key", datalasticAPIKey)
resp, err := http.Get(datalasticBaseURL + values.Encode())
if err != nil {
w.Write([]byte("unable to get vessels"))
w.WriteHeader(http.StatusInternalServerError)
return
}
if resp.StatusCode != http.StatusOK {
w.Write([]byte("unable to get vessels"))
w.WriteHeader(http.StatusInternalServerError)
return
}
io.Copy(w, resp.Body)
defer resp.Body.Close()
}
In this code, we create a new handleGetVessels handler that is available by “/api/get-vessels” route. This handler requires two query parameters: lat and lon. Datalastic API KEY is passed the same way as Google Maps API KEY — via env variable DATALASTIC_API_KEY. The radius value is hardcoded to 50 nautical miles.
When handleGetVessels handler is called, it makes the request to Datalastic API and returns a successful response body to the caller.
Now, to run the app we should specify two env variables like here:
DATALASTIC_API_KEY=xxxxxx GM_API_KEY=xxxxx go run main.go
Implementing Vessels Tracking data Display On The Map Using GO:
Now we are ready to implement the designed logic: show vessels when the user clicks some point on the map and display information about the vessel when the user clicks on the one of vessels.
Updated with new logic, static/index.js file looks like this:
let map;
let markers = [];
let infoWindow;
function initMap() {
map = new google.maps.Map(document.getElementById("map"), {
zoom: 2,
zoomControl: true,
scaleControl: true,
center: { lat: 0, lng: 0 },
});
infoWindow = new google.maps.InfoWindow();
map.addListener("click", (e) => {
deleteMarkers()
addCenterMarker(e.latLng)
getVessels(e.latLng)
});
}
function getVessels(latLng) {
const config = {
method: 'get',
baseUrl: window.location.origin,
url: 'api/get-vessels',
params: {
lat: latLng.toJSON().lat,
lon: latLng.toJSON().lng,
},
timeout: 10000,
}
axios(config).then(function (response) {
for (let i = 0; i < response.data.data.vessels.length; i++) {
let vessel = response.data.data.vessels[i];
addVesselMarker(vessel);
}
});
}
function addVesselMarker(vessel) {
const marker = new google.maps.Marker({
position: {
lat: vessel.lat,
lng: vessel.lon,
},
map,
title: vessel.name,
});
marker.addListener("click", () => {
infoWindow.close();
infoWindow.setContent(
"<b>Name: </b> " +
vessel.name +
"<br><b>MMSI: </b> " +
vessel.mmsi +
"<br><b>Lat: </b> " +
vessel.lat +
"<br><b>Lng: </b> " +
vessel.lon +
"<br><b>Speed: </b> " +
vessel.speed +
"<br><b>Course: </b> " +
vessel.course
);
infoWindow.open({
anchor: marker,
map,
});
});
markers.push(marker);
}
function addCenterMarker(latLng) {
const marker = new google.maps.Marker({
position: latLng,
map,
title: "Center",
});
marker.addListener("click", () => {
infoWindow.close();
infoWindow.setContent(
JSON.stringify(latLng.toJSON(), null, 2)
);
infoWindow.open({
anchor: marker,
map,
});
});
markers.push(marker);
}
function deleteMarkers() {
//We skipped this function to make this code block shorter
}
window.initMap = initMap;
We added infoWindow object of InfoWindow class to show users information about vessels. This object is reused between different vessels.
The most important part is that we added an event listener for the map object. When the user clicks on the map, the callback function does the following:
- Deletes all previous vessels from the map.
- Adds new map marker to the center point by addCenterMarker() function. When the user clicks on this marker, the infoWindow displays information about the coordinates of the point;
- Retrieves all the vessels in the radius and puts them on the map by getVessels() function. getVessels() calls the “/api/get-vessels” endpoint we created before and for every received vessel puts a new marker to the map.
Now our project is ready for launch!
In this article, we combined Google Maps API and Datalastic API to be able to find ships in real-time near some point on the map.
Our project is available on Github, so you can explore the code and see how we combined the power of these APIs to create a user-friendly vessel tracking map. Additionally, we invite you to check out our public Swagger (OpenAPI) docs to learn more about the Datalastic API. And since you read until now I’m sure you’re curious about what the final product looks like, we’ve included a few screenshots of the running app. So why not give it a try and build your own vessel map with Go today?


Read Other Maritime Articles
Flight API
Access real-time, historical and future dates flight API to get flight status, location, delay, route data and more. Flight APIs of Aviation Edge are useful data tools for developers with global flight data and uptime rates of 99.9%. Flight Tracking: Keep tabs...
Flight Tracker API
Get real-time aircraft location and speed data with Aviation Edge Flight Tracker API. The flight tracking data allows you to monitor an aircraft’s real-time location, speed, and status throughout its route. The API uses REST which allows a clear and simple integration...
Flight Schedules API
Flight Schedules API developed by Aviation Edge provides real-time, historical and future airport timetable data. Track departure and arrival schedules of airports worldwide. Alternatively, use an airline filter to track an airline’s flight schedule. Flight number ...
Why the Maritime API is a Must-Have?
Maritime API, a revolution in the shipping and logistics sector, have transformed the way businesses in this industry operate. They grant access to a wealth of data about ships, their journeys, and intricate details that can be leveraged to streamline operations....
Find Ships In New York Port
Finding ships in the New York Port is akin to tapping into the pulse of global commerce. Serving as the eastern gateway to the U.S., the NYC port is a bustling hub of trade. Its terminals show how important it is for worldwide links. In this article, we will identify...
Find 15 Major Maritime Ports in the World with API
Find major ports with API: In the world of maritime transportation, ports play a crucial role as key hubs for global trade and commerce. Identifying the major ports is essential for various stakeholders, including shipping companies, logistics providers, and port...
Recent Comments