Real-Time Interaction Between Maps With Socket.io and JavaScript
In this post, you'll learn how send the same data to multiple clients from your server-side in real-time using JavaScript.
Join the DZone community and get the full member experience.
Join For FreeI was doing some thinking recently. I was thinking about how I could make maps more useful than just displaying visual content on the screen when requested. While displaying map information on a screen is useful in many circumstances, it doesn't make for the most exciting of examples. This lead me to thinking about things from a more interactive level.
Take the example of first responders, such as law enforcement, fire, and medical. It might not make the most sense for them to be looking and refreshing a map to find incidents. Instead, what if the map automatically refreshed and was synchronized between the first responders? What I mean by this is, what if incidents automatically appeared on everyone's map at the same time without interaction?
In this tutorial, we're going to explore sockets to broadcast map related information in real-time using Socket.io and simple JavaScript.
To get a sense of what we're going to accomplish, take a look at the following animated image.
While not exactly a first responders type scenario, you can see that as the map is clicked in one instance, markers start to show up on all of the maps that are connected to the server via sockets. This is done without having to constantly refresh each of the windows.
Creating the Socket.io Server for Keeping Track of Marker Data
To make use of this example, we will need to create a server-side application for managing all of the client sockets and incoming data. As data is sent from one of the clients to the server, the server tracks it and sends it to all of the other connected clients. The clients never communicate directly with other clients.
Create a new project on your computer and initialize a new Node.js project by executing the following commands:
npm init -y
npm install express socket.io --save
touch app.js
The above commands will create a package.json file, install our dependencies, and create a new app.js file for all of our server-side logic. If you don't have the touch
command, go ahead and create the app.js file however makes the most sense.
Open the app.js file and include the following boilerplate code:
const Express = require("express")();
const Http = require("http").Server(Express);
const Socketio = require("socket.io")(Http);
Http.listen(3000, () => {
console.log("Listening at :3000...");
});
The above code will create our server which will listen for incoming socket connections on port 3000. However, the server will currently do nothing with those connections.
Instead, add the following to the app.js file:
const markers = [];
Socketio.on("connection", socket => {
for(let i = 0; i < markers.length; i++) {
socket.emit("marker", markers[i]);
}
socket.on("marker", data => {
markers.push(data);
Socketio.emit("marker", data);
});
});
The markers
variable will be used instead of a database in this example. Essentially, when marker information is sent to the server, it is stored in the markers
array. Every time a user connects to the server, each item in the markers
array is broadcasted to the client that had just connected to get them up to speed with everything that should exist on the map. If you really wanted to be efficient, you might just send the entire array rather than looping through it.
When any of the connected clients sends a message labeled as marker
, that message data is added to the markers
array and broadcasted to all connected clients, not just the one that sent it. In this scenario the marker
label is just a name and holds no real relevance to the markers
array.
At this point in time the server-side code is complete. We're not doing anything too aggressive for this example, just storing marker data and sending it to users.
Developing the Client Front-End for Displaying the Map and Markers
Now we need to develop the client facing application code. The role of this code will be to display a map with markers, and send marker data to the server every time the map has been clicked.
Create an index.html file on your computer and include the following boilerplate code:
<!DOCTYPE html>
<body style="margin: 0">
<div id="map" style="width: 100vw; height: 100vh;"></div>
<script src="http://js.api.here.com/v3/3.0/mapsjs-core.js" type="text/javascript" charset="utf-8"></script>
<script src="http://js.api.here.com/v3/3.0/mapsjs-service.js" type="text/javascript" charset="utf-8"></script>
<script src="http://js.api.here.com/v3/3.0/mapsjs-mapevents.js" type="text/javascript" charset="utf-8"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script>
const platform = new H.service.Platform({
"app_id": "APP_ID_HERE",
"app_code": "APP_CODE_HERE"
});
const map = new H.Map(
document.getElementById("map"),
platform.createDefaultLayers().normal.map,
{
zoom: 10,
center: { lat: 37.7397, lng: -121.4252 }
}
);
const mapEvent = new H.mapevents.MapEvents(map);
const behavior = new H.mapevents.Behavior(mapEvent);
</script>
</body>
</html>
I've written quite a few examples around the code above. All we're doing is initializing the HERE platform and displaying a map. Do note that we are including the Socket.io client library. You must also swap out the app id and app code tokens to match your actual tokens that are obtained through the HERE Developer Portal.
The next step is to establish a connection to the socket server:
const socket = io("http://localhost:3000");
With the connection established, we probably want to update the map with whatever data is currently stored on the server. We can do this by creating a listener for events by a particular label:
socket.on("marker", data => {
const marker = new H.map.Marker(data);
map.addObject(marker);
});
Take note of the marker
label. It doesn't have to match the label that we have on the server, but do note that the server is trying to emit messages to that particular label. Just make sure you keep all your labels and emit labels in sync. In the listener, as new data comes in, a marker is created and added to the map. This applies to the data the server is currently storing and any future data.
To create new markers and send them to the other clients, we can configure an event listener on the map for gesture type events:
map.addEventListener("tap", event => {
const position = map.screenToGeo(
event.currentPointer.viewportX,
event.currentPointer.viewportY
);
const marker = new H.map.Marker(position);
map.addObject(marker);
socket.emit("marker", position);
});
In the above code, after a tap event occurs, the pixel coordinates are converted to geo coordinates and the position of those coordinates are sent to the server to be broadcasted to all the other clients.
If you run the server and launch a few instances of the client facing web application, you'll get an idea of what's happening.
Conclusion
You just saw how to use Socket.io with JavaScript to send map data in real-time to multiple clients. Like I mentioned earlier, I could imagine this would be useful in a lot of scenarios where it is critical that information is provided in real-time and in a synchronized fashion to all clients.
Published at DZone with permission of Nic Raboy, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments