Skip links

Real-Time Tracking App: Open Layers, Redis, WebSocket, Node.js Guide

Jump To Section

In this article, I’m going to write a very basic Real-Time Tracking application based on Open Layers, Redis, WebSocket and Node.js.

In this article, I’m going to write a very basic Real-Time Tracking application based on Open Layers, Redis, WebSocket and Node.js.

What are we building?

The use case revolves around managing rides (preconfigured routes for cabs/buses) and providing ride subscribers real time visibility to the current location of the rides on a map.

Note on Architecture and Technology Choices

The idea is to use Open Layers to display a map and show the current location of the ride on the map. The vehicle that serves the route would send back its location back to the servers, The backend servers perform the logic and calculations before publishing the current location back to the clients for the client to update the map display. Client could be web pages which is used by operations , Android or IPhone Application used by subscribers. The scope for this article is to show on web pages and in subsequent posts do similarly for Native Mobile Apps.

Node.js and WebSocket are becoming the de facto standard for developing non-blocking, event-driven servers, due to its single-threaded nature. It’s used for traditional web sites and back-end API services, but was designed with real-time, push-based architectures in mind.

Redis A backend Publish-Subscribe mechanism is imperative to scaling WebSocket architectures. For this article, I am using Redis as a pub-sub provider. It is also possible to use other messaging infrastructures like RabbitMQ, Kafka etc.

Project Setup

Install a redis cluster, acquire the credential is and start the Redis server etc.I had installed a redis server locally, but the reader could use a Managed Redis Instance. There are many like AWS Redis, Scalegrid etc.

Install Node and NPM and create a directory to hold the source code and serve it using Node.js. Perform an NPM Init inside the directory. A Package.json file should be created and its content may look like below

{
  "name": "realtimetracking",
  "version": "0.0.1",
  "description": "A realtime GPS tracking using redis, nodejs and websockets",
  "dependencies": {
    "body-parser": "^1.15.2",
    "express": "^4.10.2",
    "redis": "^2.6.3",
    "socket.io": "^1.7.1"
  },
  "main": "index.js",
  "author": ""
}

Please note that we are using express framework for developing our app, and Socket.io provides that WebSocket implementation that will be required later in this article.

npm install --save // This command should resolve all the dependencies

create a views directory and create a file called index.html. Create some test content in index.html, we will update as needed later. Further, create a public directory with css , js and image subdirectories to serve client-side css, javascript files and images.

Create an index.js file in the directory

var express = require('express');
var bodyParser = require('body-parser');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);var port = process.env.PORT || 3000;

// Start the Server
http.listen(port, function () {
    console.log('Server Started. Listening on *:' + port);
});


// Express Middleware
app.use(express.static('public'));
app.use(bodyParser.urlencoded({
    extended: true
}));

// Render Main HTML file
app.get('/', function (req, res) {
    res.sendFile('views/index.html', {
        root: __dirname
    });
});

At this point start the server with node index.js command to start the server and hit use the browser to ensure the contents of the index.html is served when http://{host}:3000/ is accessed from a browser.

Displaying the Open Layers map with a marker.

Update index.html to include appropriate CSS and JS files

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Node.js + Socket.io + Redis + OpenLayers + Real Time Tracking </title>
    <link rel="stylesheet" href="https://openlayers.org/en/v5.3.0/css/ol.css" type="text/css">

    <link rel="stylesheet" href="css/main.css">
</head>
<body>
<div class="container">
    <h1>Node.js + Socket.io + Redis + OpenLayers + Real Time Tracking </h1>

    <div id="map">
        <!-- Your map will be shown inside this div-->
    </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://openlayers.org/en/v5.3.0/build/ol.js"></script>
<script type="text/javascript" src="js/main.js"></script>

</body>
</html>

The bolded portion in the above code indicate the various css and js files. Our Maps will be displayed inside the div tag with id as “map”.

Note that we have included a main.js file , this needs to be created in the public/js folder this file would contain the code required to display the Open Layers Map.

/Base Layer with Open Street Maps
var baseMapLayer = new ol.layer.Tile({
  source: new ol.source.OSM()

});

//Construct the Map Object
var map = new ol.Map({
  target: 'map',
  layers: [ baseMapLayer],
  view: new ol.View({
          center: ol.proj.fromLonLat([80.2459,12.9860]),
          zoom: 15 //Initial Zoom Level
        })
});

//Set up an  Style for the marker note the image used for marker
var iconStyle = new ol.style.Style({
    image: new ol.style.Icon(/** @type {module:ol/style/Icon~Options} */ ({
      anchor: [0.5, 16],
      anchorXUnits: 'fraction',
      anchorYUnits: 'pixels',
      src: 'image/icon.png'
    }))
});


//Adding a marker on the map
var marker = new ol.Feature({
  geometry: new ol.geom.Point(
    ol.proj.fromLonLat([80.24586,12.9859])
  )
});

marker.setStyle(iconStyle);

var vectorSource = new ol.source.Vector({
  features: [marker]
});


var markerVectorLayer = new ol.layer.Vector({
  source: vectorSource,

});

// add style to Vector layer style map
map.addLayer(markerVectorLayer);

With the above update, the web page should display a map with a pointer at a location I have chosen. (Hard Coded, but this can be dynamic as well).

Making the Marker Move from the client-side.

function updateCoordinate(item) { 
    // Structure of the input Item
    // {"Coordinate":{"Longitude":80.2244,"Latitude":12.97784}}    var featureToUpdate = marker;

    var coord = ol.proj.fromLonLat([item.Coordinate.Longitude, item.Coordinate.Latitude]);

    featureToUpdate.getGeometry().setCoordinates(coord);
}

Include the above function at the bottom of main.js which updates the co-ordinates of the marker. The below piece of code helps to quickly test the marker movement on the client-side.

var longlats =
[[80.24586,12.98598],
[80.24537,12.98597],
[80.24522,12.98596],
[80.24522,12.98614],
[80.24523,12.98626]]var count = 1;
var item = {};
item.id = marker.getId;
item.Coordinate = {};
setInterval(function() {
  item.Coordinate.Longitude = longlats[count][0];
  item.Coordinate.Latitude = longlats[count][1];
  count++;
  updateCoordinate(item);
}, 5000);

Publishing the Last Known Location

In the real world, the location of the subject would be published by a device that is the subject. In our case, we simulate the same by developing a page that takes a co-ordinate from the longlats lists and send it through web sockets to our back end.

//Serve a Publisher HTML
app.get('/publish', function (req, res) {
    res.sendFile('views/publisher.html', {
        root: __dirname
    });
});

Publisher HTML — create any html file and ensure to include the below scripts

//
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.slim.js"></script>
<script type="text/javascript" src="js/publish.js" type="text/javascript"></script>

publish.js would contain similar script code like our test script described above.

var longlats =
[[80.24586,12.98598],
[80.24537,12.98597],
[80.24522,12.98596],
[80.24522,12.98614],
[80.24523,12.98626]];const socket = io({ transports: ['websocket'] });var count = 1;

setInterval(function() {
  console.log(count);
  if (count < 10000){
    var item = {};
    item.Coordinate = {};
    item.Coordinate.Longitude = longlats[count][0];
    item.Coordinate.Latitude = longlats[count][1];
    count++;
    socket.emit('lastKnownLocation', item);
  }
}, 5000);

Set up the server code to receive this location. The highlighted pieces of code demonstrate how to receive the data from the client on the server. Note the specific Line redisPublisher.publish, the explanation is in the next section.

io.on('connection', function (socket) {
    console.log('socket created');

    let previousId;
    const safeJoin = currentId => {
        socket.leave(previousId);
        socket.join(currentId);
        previousId = currentId;
      };

    socket.on('disconnect', function() {
      console.log('Got disconnect!');
   });

   socket.on('lastKnownLocation', function (data) {
            var location = JSON.stringify(data);
           redisPublisher.publish('locationUpdate', location);
     });

});

Publishing the last known location to subscribers for the clients to update the map

On index.js set up connections to redis like below. I use 2 connections one for subscribing from redis and one for publishing to redis. I believe a single connection may suffice as well. Also, note that when the server receives a location update the data is published to the redis channel ‘locationUpdate’. Further, when a message is received from redis, the same is sent back to all websocket clients using the bolded code below.

var redis = require('redis');
var redisSubscriber = redis.createClient();
var redisPublisher = redis.createClient();redisSubscriber.on('subscribe', function (channel, count) {
        console.log('client subscribed to ' + channel + ', ' + count + ' total subscriptions');
});

redisSubscriber.on('message', function (channel, message) {
    console.log('client channel ' + channel + ': ' + message);
    io.emit('locationUpdate', message);
});

Also notice that earlier when we served the maps content we just served back the index.html contents. This piece could be updated to start subscrbing for ‘locationUpdate’ channel like below.

app.get('/', function (req, res) {
    redisSubscriber.subscribe('locationUpdate');
    res.sendFile('views/index.html', {
        root: __dirname
    });
});

Now , load the Main Page and the Publisher page, and see the marker move as there are the new location publishes.

Conclusion

That concludes our basic real-time tracking app. There are many cases that needs to be handled to make it production ready like, what should be done when the system receives some weird location, what should be done when the marker moves out of the visible div area, how to initialise the location of the centre of the map based on route information, how to handle security, how to set up web sockets across fire walls and load balancers. but this should provide the basic set up required for developers to get started on such an app.

Rangarajan Seetharaman

Rangarajan Seetharaman

Latest Reads

Subscribe

Suggested Reading

Ready to Unlock Yours Enterprise's Full Potential?

Adaptive Clinical Trial Designs: Modify trials based on interim results for faster identification of effective drugs.Identify effective drugs faster with data analytics and machine learning algorithms to analyze interim trial results and modify.
Real-World Evidence (RWE) Integration: Supplement trial data with real-world insights for drug effectiveness and safety.Supplement trial data with real-world insights for drug effectiveness and safety.
Biomarker Identification and Validation: Validate biomarkers predicting treatment response for targeted therapies.Utilize bioinformatics and computational biology to validate biomarkers predicting treatment response for targeted therapies.
Collaborative Clinical Research Networks: Establish networks for better patient recruitment and data sharing.Leverage cloud-based platforms and collaborative software to establish networks for better patient recruitment and data sharing.
Master Protocols and Basket Trials: Evaluate multiple drugs in one trial for efficient drug development.Implement electronic data capture systems and digital platforms to efficiently manage and evaluate multiple drugs or drug combinations within a single trial, enabling more streamlined drug development
Remote and Decentralized Trials: Embrace virtual trials for broader patient participation.Embrace telemedicine, virtual monitoring, and digital health tools to conduct remote and decentralized trials, allowing patients to participate from home and reducing the need for frequent in-person visits
Patient-Centric Trials: Design trials with patient needs in mind for better recruitment and retention.Develop patient-centric mobile apps and web portals that provide trial information, virtual support groups, and patient-reported outcome tracking to enhance patient engagement, recruitment, and retention
Regulatory Engagement and Expedited Review Pathways: Engage regulators early for faster approvals.Utilize digital communication tools to engage regulatory agencies early in the drug development process, enabling faster feedback and exploration of expedited review pathways for accelerated approvals
Companion Diagnostics Development: Develop diagnostics for targeted recruitment and personalized treatment.Implement bioinformatics and genomics technologies to develop companion diagnostics that can identify patient subpopulations likely to benefit from the drug, aiding in targeted recruitment and personalized treatment
Data Standardization and Interoperability: Ensure seamless data exchange among research sites.Utilize interoperable electronic health record systems and health data standards to ensure seamless data exchange among different research sites, promoting efficient data aggregation and analysis
Use of AI and Predictive Analytics: Apply AI for drug candidate identification and data analysis.Leverage AI algorithms and predictive analytics to analyze large datasets, identify potential drug candidates, optimize trial designs, and predict treatment outcomes, accelerating the drug development process
R&D Investments: Improve the drug or expand indicationsUtilize computational modelling and simulation techniques to accelerate drug discovery and optimize drug development processes