Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Create Vessels Map With React

Tags:

Create Vessels Map With React

Create a vessels map with React and visualize vessel tracking data in real-time using vessel and Datalastic APIs.

Vessel maps have become increasingly popular in the maritime industry as it helps with tracking and monitoring vessel movements, and optimizing shipping routes.

Using React, a popular JavaScript library for building user interfaces, we will create an interactive and dynamic vessel map and this article, will be your guide through the process of creating a new service that allow users to track marine vessels on the map in real-time using Datalastic APIs and React.

Introducing the Vessel Tracking Map:

To begin with, the idea of our future web application is to create an interactive map where users can enter vessel identifiers and see their location on the map. Specifically, we will use vessels MMSI as the identifiers (assuming the end user already knows the target vessels’ MMSIs).

Moreover, the source of vessels’ real-time data will be the Datalastic platform, which has an awesome API. You can check out its Swagger (OpenAPI) documentation to know all available API endpoints.

In addition, we plan to use Google Maps service as a map engine and its Google Maps JavaScript API for interacting with it.

Lastly, the entire project is a type of front-end project, which we will implement using React JS library in combination with TypeScript.

Prerequisites For The Map Project:

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. and for that what we will need is:

1. Google Maps JavaScript API KEY. You can easily get one if you follow this tutorial. Also, Google Cloud Platform offers a really generous free tier quota for using Google Maps JavaScript API.

2. If you (most probably) would like to test this project when it is done, you also need Datalastic API KEY. In case you haven’t one, you can easily get started with one of the trial subscriptions.

It’s time to discuss our scope of work for the project. The tasks will be as follow:

  • Create Google Map basis layout;
  • Add input form to the layout;
  • Retrive information about vessels from Datalastic platform;
  • Display vessels markers on the map.

Creating Project Skeleton and Adding Dependencies

To create the project skeleton we use create-react-app library. We also add template flag to tell the library that we will use TypeScript instead of plain JS.

npx create-react-app vessels-reactjs –template typescript

After this we need some additional dependencies. We need @types/googlemaps library to work with Google Maps API. To install it:

npm install –save @types/googlemaps

After this, let’s create Google Maps basis layout.

We don’t need to make any changes in public/index.html or src/index.tsx files. The first working file is src/types/types.tsx where we will declare custom types and interfaces. And we start with this content of this file:

export interface IMap {
   mapType: google.maps.MapTypeId;
   mapTypeControl?: boolean;
}

export type GoogleLatLng = google.maps.LatLng;
export type GoogleMap = google.maps.Map;
export type GoogleMarker = google.maps.Marker;
We created IMap interface for future Map component. We also declared GoogleLatLng, GoogleMap, GoogleMarker custom types just as a wrappers for types from @types/googlemaps library.
The next step is to create new Map component in src/components/Map/Map.tsx file:
import React from 'react';
import { IMap, GoogleLatLng, GoogleMap, GoogleMarker } from '../../types/types';
import './map.css';

const Map: React.FC = ({ mapType, mapTypeControl = false}) => {
 const ref = React.useRef(null);
 const [map, setMap] = React.useState();

 const initMap = (zoomLevel: number, address: GoogleLatLng): void => {
   if(ref.current){
     setMap(
       new google.maps.Map(ref.current,{
         zoom: zoomLevel,
         center: address,
         mapTypeControl: mapTypeControl,
         streetViewControl: false,
         rotateControl: false,
         scaleControl: false,
         fullscreenControl: false,
         panControl: false,
         zoomControl: true,
         gestureHandling: 'cooperative',
         mapTypeId: mapType,
         draggableCursor: 'pointer'
       })
     )
   }
 }

 const defaultMapStart = ():void => {
   const defaultAddress = new google.maps.LatLng(0, 0);
   initMap(2, defaultAddress);
 }

 const startMap = ():void => {
   if(!map){
     defaultMapStart();
   }
 }

 React.useEffect(startMap,[map]);

 return (
   
); } export default Map

Map component first of all should be able to initialize the map layout, ant it uses useEffect hook to initialize the map.

The next file, we bring to the project, is src/utils/mapUtils.tsx file and it contains only one function:

export const loadMapApi = () => {
   const mapsURL = `https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_MAPS_API_KEY}&v=quarterly`;
   const scripts = document.getElementsByTagName('script');

   for (let i = 0; i 

It looks a bit overcomplicated but this construction is necessary to make this entire project more flexible and to not hardcode Google Maps API KEY in the code. Now, if someone wants to launch the project with their own keys, all they need to do is to create .env file in the project root with the content:

REACT_APP_GOOGLE_MAPS_API_KEY=

The last thing we need before we could modify App.tsx file is context. For this, we create file src/context.tsx with the context stub. Of course, we will modify it in the future, but for now it looks like this:

import React from 'react';

export const AppContext = React.createContext({});
Now we can put it all together and update our src/App.tsx file.
import React from 'react';
import { loadMapApi } from './utils/mapUtils';
import Map from './components/Map/Map';
import { AppContext } from './context';

function App() {
 const [scriptLoaded, setScriptLoaded] = React.useState(false);

 React.useEffect(() => {
     loadMapApi();
     window.addEventListener('load', function () {
         setScriptLoaded(true);
     });
 }, []);

 return (
   
   
{scriptLoaded && ( )}
); } export default App;

In the App we define following behaviour: using useEffect hook it loads Google Maps layout initialization script. As well as using event listener mechanism it catches window load event and sets variable scriptLoaded = true . After this Map component is rendered.

If you try to launch the project, all you will see is just a Google Maps map. Let’s make it more useful.

Retrieving Real-time Vessels Data Using Datalastic API Bulk Tracking Endpoint

To retrieve realtime vessels data we will use Datalastic API bulk tracking endpoint.

In order to use it we should send GET HTTP request with following query:

https://api.datalastic.com/api/v0/vessel_bulk?api_key={YOUR_API_KEY}&{PARAMETER}={PARAMETER_NUMBER} .

This endpoint allows to send multiple similar parameters. In our case we want to search by vessel MMSI number, so, we will send multiple mmsi query parameters.

We consider, that users will input vessel MMSI numbers, separated by comma, into one input field. To deal with this, we create new src/api/url.ts file, containing the logic of constructing query string:

export const createURL = (apiKey: string, mmsi: string) => {
 const url = new URL("https://api.datalastic.com/api/v0/vessel_bulk");
 url.searchParams.append("api-key", apiKey);
 const mmsiArr = mmsi.split(',').map(p => p.trim());

 mmsiArr.forEach(mmsi => {
   url.searchParams.append("mmsi", mmsi);
 })

 return url;
}

The next new src/api/api.ts file contains getVessels function, that we will use to fetch data from Datalastic API according to user input:

import { createURL } from './url';

export const getVessels = async(apiKey: string, mmsi: string) => {
   try {
     const response = await fetch(`${createURL(apiKey,mmsi)}`, {
       method: 'GET',
     });
     if (response.ok) {
       const data = await response.json();
       return data;
     } else {
       return await Promise.reject(new Error(response.statusText));
     }
   } catch (error) {
     return 'error';
   }
}

Developing Vessel Tracking Feature with Datalastic API and Input Form:

In order to allow users to track vessels in real-time and get maritime data, we need to add a form with two input fields:

1. Datalastic API KEY.

2. Vessels MMSI numbers.

Firstly, we will start with the new components for the form: Input and Button components.

src/components/Input/Input.tsx file:

import React from 'react';
import './input.css';

interface Props {
 placeholder: string;
 className: string;
}

const Input:React.FC = ({placeholder, className}) => {
 return(
   
 )
}
  export default Input

src/components/Button/Button.tsx file:

import React from 'react';
import './button.css';

interface Props {
 text: string;
 func: () => void;
}

const Button:React.FC = ({text, func}) => {
 return(
   
) } export default Button

Also, we will add new Error component. It will allow us to display modal window with additional information to users, for example, in case if some API request fails to get data.

src/components/Error/Error.tsx file:

import React from 'react';
import Button from '../Button/Button';
import { AppContext } from '../../context';

import './error.css';

const Error:React.FC = () => {
 const {setError, errorText} = React.useContext(AppContext);

 const closeError = (): void => {
   setError(false);
 }

 return(
   

{errorText}

) } export default Error

Now we are ready to introduce entire Form component in src/components/Form/Form.tsx file:

import React from 'react';
import Input from '../Input/Input';
import Button from '../Button/Button';
import { AppContext } from '../../context';
import { getVessels } from '../../api/api';

import './form.css';

const Form:React.FC = () => {
 const {setApiKey, setMMSI, apiKey, mmsi, setVessels, setError, setErrorText} = React.useContext(AppContext);

 const saveInputsValues = ():void => {
   const apiKey = document.querySelector('.input-key') as HTMLInputElement;
   const mmsiOfVessels = document.querySelector('.input-mmsi') as HTMLInputElement;
   setApiKey(apiKey.value);
   setMMSI(mmsiOfVessels.value);
 }

 const searchVessels = async() => {
   const res = await getVessels(apiKey, mmsi);
   if(res === 'error'){
     setError(true);
     setErrorText('Please, check your api key or vessel mmsi');
     setVessels([]);
     return;
   }
   if(res.data.total === 0) {
     setError(true);
     setErrorText("Vessels with such mmsi aren't found");
     setVessels([]);
   } else {
     setVessels(res.data.vessels);
   }
 }

 return(
   
) } export default Form

Form component contains two input fiels and button. When user clicks button, searchVessels function is triggered, that sends request to Datalastic API bulk tracking endpoint. In case, that there’s some error the result will be that there is no found vessels, users will see error message as following:  Error component.

As you could notice, in the Form component we get a lot of variables from the context. Let’s take a look at how we also updated the src/context.tsx file:

import React from 'react';
import { InitialValue } from './types/types';
const initialValue:InitialValue = {
 apiKey: '',
 setApiKey: (apiKey) => { },
 mmsi: '',
 setMMSI: (mmsi) => { },
 vessels: [],
 setVessels: (vessels) => { },
 error: false,
 setError: (error) => { },
 errorText: '',
 setErrorText: (errorText) => { }
}
export const AppContext = React.createContext(initialValue);

And InitialValue type in src/types/types.ts file:

export interface InitialValue {
 apiKey: string,
 setApiKey: (apiKey: string) => void,
 mmsi: string,
 setMMSI: (mmsi: string) => void,
 vessels: IVessel[],
 setVessels: (vessels: IVessel[]) => void,
 error: boolean,
 setError: (error: boolean) => void,
 errorText: string,
 setErrorText: (errorText: string) => void
}

To bring new functionality to the UI, we update App component: initialize context variables and add Form and Error components. Now src/App.tsx looks like this:

import React from 'react';
import { loadMapApi } from './utils/mapUtils';
import Map from './components/Map/Map';
import Form from './components/Form/Form';
import { IVessel } from './types/types';
import { AppContext } from './context';
import Error from './components/Error/Error';

function App() {
 const [apiKey, setApiKey] = React.useState('');
 const [mmsi, setMMSI] = React.useState('');
 const [vessels, setVessels] = React.useState([]);
 const [error, setError] = React.useState(false);
 const [errorText, setErrorText] = React.useState('');
 const [scriptLoaded, setScriptLoaded] = React.useState(false);

 React.useEffect(() => {
     loadMapApi();
     window.addEventListener('load', function () {
         setScriptLoaded(true);
     });
 }, []);

 return (
   
   
{scriptLoaded && ( )} {scriptLoaded && (
)} {error && }
); } export default App;

Display vessels on the map

Users may not see inputted vessel location data, which could be problematic if real-time visibility is needed. We’ll add a feature for immediate viewing to solve this. Real-time vessel location information improves user experience and app usefulness.

And here’s an example format of a successful response from the Datalastic API bulk tracking endpoint:

When using the Datalastic API bulk tracking endpoint, a successful response will include information about vessel locations in a structured format. This may include vessel identification numbers, latitude and longitude coordinates, timestamps, and other relevant data points. By utilizing this information, users can track vessel movements, monitor traffic patterns, and make informed decisions related to their business or personal needs.

{
 "data": {
   "total": 1,
   "vessels": [
     {
       "uuid": "17959a4c-f5a9-ed71-1a42-5bfab00670ef",
       "name": "GARGANO",
       "mmsi": "235362000",
       "imo": "9249403",
       "eni": null,
       "country_iso": "GB",
       "type": "Other",
       "type_specific": "Supply Vessel",
       "lat": 54.105115,
       "lon": 0.016225,
       "speed": 4.4,
       "course": 332.8,
       "heading": 333,
       "navigation_status": "Under way using engine",
       "destination": "SUNDERLAND",
       "last_position_epoch": 1638543058,
       "last_position_UTC": "2021-12-03T14:50:58Z",
       "eta_epoch": null,
       "eta_UTC": null
     }
   ]
 },
 "meta": {
   "duration": 0.007911177,
   "endpoint": "/api/v0/vessel_bulk",
   "success": true
 }
}

To use it, we will create a new IVessel interface in src/types/types.ts file:

export type IVessel = {
 uuid: string,
 name: string,
 mmsi: string,
 imo: string,
 eni: string,
 country_iso: string,
 type: string,
 type_specific: string,
 lat: number,
 lon: number,
 speed: number,
 course: number,
 heading: number,
 navigation_status: string,
 destination: string,
 last_position_epoch: number,
 last_position_UTC: string,
 eta_epoch: number,
 eta_UTC: string
}

You could notice that we already used this interface in src/App.tsx file in this expression: const [vessels, setVessels] = React.useState([]); . Variables vessels, setVessels are already put to the context. So we can use them in the Map component to display to users.

The final version of src/components/Map/Map.tsx file looks like this:

import React from 'react';
import { AppContext } from '../../context';
import { IMap, GoogleLatLng, GoogleMap, GoogleMarker } from '../../types/types';
import { IVessel } from '../../types/types';

import './map.css';
import Icon from '../../assets/icon.png';


const Map: React.FC = ({ mapType, mapTypeControl = false}) => {
 const ref = React.useRef(null);
 const [map, setMap] = React.useState();
 const [markers, setMarkers] = React.useState([]);
 const {vessels} = React.useContext(AppContext);
 let markersArr: GoogleMarker[] = [];
 let infoWindow = new google.maps.InfoWindow();


 const initMap = (zoomLevel: number, address: GoogleLatLng): void => {
   if(ref.current){
     setMap(
       new google.maps.Map(ref.current,{
         zoom: zoomLevel,
         center: address,
         mapTypeControl: mapTypeControl,
         streetViewControl: false,
         rotateControl: false,
         scaleControl: false,
         fullscreenControl: false,
         panControl: false,
         zoomControl: true,
         gestureHandling: 'cooperative',
         mapTypeId: mapType,
         draggableCursor: 'pointer'
       })
     )
   }
 }

 const defaultMapStart = ():void => {
   const defaultAddress = new google.maps.LatLng(0, 0);
   initMap(2, defaultAddress);
 }

 const startMap = ():void => {
   if(!map){
     defaultMapStart();
   }
 }

 const setMapOnAll = (map: google.maps.Map | null):void => {
   for (let i = 0; i  {
   setMapOnAll(null);
   markersArr = [];
   setMarkers([]);
 }

 const addMarker = (vessel:IVessel): void => {
   const location = new google.maps.LatLng(vessel.lat, vessel.lon);

   const marker:GoogleMarker = new google.maps.Marker({
             position: location,
             map: map,
             animation: google.maps.Animation.DROP,
             icon: Icon,
             title: `Name: ${vessel.name}, mmsi: ${vessel.mmsi}`,
         });
         const info = `Name: ${vessel.name}
mmsi: ${vessel.mmsi}
lat: ${vessel.lat}
lon: ${vessel.lon}`; marker.addListener("click", () => { infoWindow.close(); infoWindow.setContent(info); infoWindow.open(map, marker); }); markersArr.push(marker); } React.useEffect(() => { deleteMarkers(); vessels.forEach(vessel => { addMarker(vessel); }); setMarkers(markersArr); if(map && vessels.length){ map.setCenter({lat: 0, lng: 0}); map.setZoom(2); }; },[vessels]); React.useEffect(startMap,[map]); return (
); } export default Map

Launching our maritime project with vessel tracking and maps:

We have implemented a new useEffect hook in our project for vessel tracking, which efficiently follows changes to the vessel’s variables. With the help of this hook, we can perform the following maritime-related actions:

  • Delete all previously displayed vessel markers from the map using vessel tracking.
  • Create a new array of vessel markers for better visualization of vessels on maps.
  • Display these markers on the map to enhance vessel tracking.
  • Return the map to its center view.

To add an enjoyable element to our project, we have incorporated custom icon PNG files instead of using Google Maps standard markers. These custom icons can help identify vessels quickly. Moreover, each vessel marker has an info window containing the vessel’s main information that can be easily accessed by clicking on the marker. This feature enhances the user experience of our vessel tracking project.

Our project is now ready to launch, and we are excited to showcase some screenshots of our working application that will provide a sneak peek into the functionality of our maritime project using vessel tracking and maps.

By following our tutorial, you’ll be able to create your own vessel tracking application using these powerful tools. So why wait? Let’s get started!

In this informative article, we have demonstrated how to create a highly effective and user-friendly web application that enables users to track vessels in real-time using the reliable and trusted Google Maps API and Datalastic API.

If you’re interested in exploring the project further, you can find the complete project on our Github page, which includes detailed documentation and instructions on how to set up and use the application.

And if you’re curious to learn more about Datalastic API and its features, we encourage you to check out our public Swagger (OpenAPI) documentation, which provides comprehensive information on how to use the API and its various endpoints.

You might also like to read: Track ships in your port with API

Read Other Maritime Articles 

Create Vessels Map With React

Create a vessels map with React and visualize vessel tracking data in real-time using vessel and Datalastic APIs. Vessel maps have become increasingly popular in the maritime industry as it helps with tracking and monitoring vessel movements, and optimizing shipping...

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...

Fishing Vessels Data

The fishing industry is a vital part of our economy, providing us with a steady source of food and employment. However, it's no secret that the industry has its fair share of challenges, one of which is the ability to track and monitor fishing vessels. This is where...

Access Any Vessel With IMO API

As a developer, you understand the importance of reliable and efficient APIs in your projects. Therefore, IMO API, also known as the International Maritime Organization API, is a comprehensive database of global shipping information. It provides access to a wide range...

Maritime API: 10 Popular Questions And Their Answers

A maritime API, or application programming interface, is a set of protocols and tools that allows developers to build software applications that interact with maritime data. These APIs enable developers to access and integrate maritime data from various sources, such...

How To Use Maritime Port API For PHP

As a PHP developer, you may be looking for ways to enhance your applications and provide your users with valuable and relevant data. One tool that can help you achieve this is the Datalastic Maritime Port data API for PHP developers. This API provides access to...

The post Create Vessels Map With React appeared first on Datalastic.



This post first appeared on Datalastic Maritime Vessels And Ports Data API, please read the originial post: here

Share the post

Create Vessels Map With React

×