Skip to main content

Real-time Geographical Data Visualization with Node.js, Socket.IO and Leaflet

Screenshot
Body
Data visualisation is becoming an increasingly important subject. As the complexity an volumes of data increase, it is getting harder and harder to make sense of bare arrays of numbers without relevant visualisation models. Let me give you a good example. Right now I work in China. Few months ago there appeared a new startup: bycicle sharing service that used GPS sensors on each bike to track it in the city. Now let's imagine, you need analyse how evenly your bikes are distributed around the city and how many of them are in motion right now. Doing that with excel spreadsheet would be a nightmare. A natural way to deal with this kind of corrdinates data would be to show points as map overlay. In this post I will show you how to do it: show realtime geographical data with a help of Leaflet (for maps), Node.js (for server-side code) and Socket.io (for establishing real-time connection). Here's the project layout that I will be using for this example:
  1. - public -- files served from HTTP server
  2. - vnd -- third party libraries
  3. - leaflet -- leaflet files
  4. - js -- our client-side JS sources
  5. - main.js -- main client file
  6. - css -- our css files
  7. - main.css -- main css file
  8. - index.html -- client entry point
  9. - node_modules -- node.js dependencies
  10. - package.json -- project metadata
  11. - main.js -- main server-side entry point
Let's start from building a simple HTTP server that also accepts Socket.io connections. Install express and socket.io and put the following code in main.js:
  1. const express = require('express');
  2. const http = require('http');
  3.  
  4. const app = express();
  5. const httpServer = http.createServer(app);
  6. const io = require('socket.io')(httpServer);
  7.  
  8. app.use(express.static(`${__dirname}/public`));
  9.  
  10. setInterval(() => {
  11. io.emit('coords', {
  12. lat: 37 + Math.random() * 20 - 10,
  13. lng: -96 + Math.random() * 40 - 20
  14. });
  15. }, 1000);
  16.  
  17. httpServer.listen(3002, () => {
  18. console.log('Listening on 0.0.0.0:3002')
  19. });
As you see this is a very simple express.js application that serves files from 'public' folder and also emits random coordinates to each connected client each 1000 milliseconds. What are those numbers in coordinates? This is roughly the middle of USA. I took this territory as an example. Now let's create a client-side application that can receive and draw the corodinates on the map. We'll use Leaflet to render map, so let's start from initializing Leaflet and pointing our map to USA. Here's the contents of index.html file:
  1. <!doctype html>
  2. <html lang="en">
  3. <meta charset="utf-8">
  4. <title>Geo Vis</title>
  5. <link rel="stylesheet" href="css/main.css">
  6. <link rel="stylesheet" href="vnd/leaflet/leaflet.css">
  7.  
  8. <script src="vnd/leaflet/leaflet.js"></script>
  9. </head>
  10.  
  11.  
  12. <div id="map"></div>
  13.  
  14. <script src="/socket.io/socket.io.js"></script>
  15. <script src="js/main.js"></script>
  16.  
  17. </body>
  18. </html>
Notice that we load Socket.io and Leaflet before we load our main script. This way we make sure that the objects required to visualize data are in place. The only HTML element that we put inside of body is a map div. In our main.css file we'll give it width and height, so that it is displayed on the page:
  1. #map {
  2. width: 600px;
  3. height: 450px;
  4. margin: 10px auto;
  5. }
Now, when all the elements are prepared, it is time to initialize the map! Add this code to main.js file (client-side):
  1. var map;
  2.  
  3. (function init() {
  4. initMap();
  5.  
  6.  
  7. })();
  8.  
  9. function initMap() {
  10. console.log('Initializing map');
  11. map = L.map('map').setView([37, -96], 4);
  12.  
  13. // Set up map source
  14. L.tileLayer(
  15. 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  16. attribution: 'Open Street Map',
  17. maxZoom: 18
  18. }).addTo(map);
  19. }
As you see, we use Open Street Map as the source of our map tiles (you can use the other map, since Leaflet supports many different providers). We put the map in exactly the same place where the server is generating random coordinates. If you reload the page at this point, you will see the map showing up on the screen. Now let's add some markers to show our geographical trends! We'll have to subscribe to 'coords' event that the server is sending and put the marker in a right place.
  1. var map;
  2. var sock = io();
  3.  
  4. (function init() {
  5. initMap();
  6.  
  7. sock.on('coords', function(c) {
  8. drawMarker(c.lat, c.lng);
  9. });
  10.  
  11. })();
  12.  
  13. function drawMarker(lat, lng) {
  14. L.marker([lat, lng]).addTo(map);
  15. }
  16.  
  17. function initMap() {
  18. console.log('Initializing map');
  19. map = L.map('map').setView([37, -96], 4);
  20.  
  21. // Set up map source
  22. L.tileLayer(
  23. 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  24. attribution: 'Open Street Map',
  25. maxZoom: 18
  26. }).addTo(map);
  27. }
Refresh the page and voila! The points are appearing on the map! As the final touch, if your events have the "area of effect", you can draw them as circles with certain radius and colors instead of default pointy markers. To do that, replace the body of drawMarker() function with the following code:
  1. L.circle([lat, lng], {
  2. color: 'steelblue',
  3. fillColor: 'steelblue',
  4. fillOpacity: 0.5,
  5. radius: 50000
  6. }).addTo(map);
Adjust colors and radius to fit your needs and you have a great tool to visualise real-time geographical data!

Add new comment