In this series of posts we are going to show you how to use node.js and Socket.IO to build a simple chat application, and then mix it up with Google Maps and the Geolocation
API to create a cool geographic app. We will use Bootstrap with a modified css to get a nice look for the website.
In the last post we showed you how to use Socket.IO to create a simple chat room. In this post we will show you how to use Socket.IO combined with Google Maps to
send geographic updates in real time.
We will build up on top of the Socket.IO server we created, telling the server to listen new events:
send location: When the user's location changed.
request locations: When a user wants to know every other connected user's location.
We also need to add a new event listener on the client side:
location update: To receive updates of other users' location.
To simplify this demo we will write the client from scratch, you can see how the whole thing is put together on github.
We will store all the markers in a hash, creating one marker for each user that is connected to the server. Notice that you will need to include the Google Maps script.
varwebSocket,myMap;varmarkers={};functioninitialize(mapContainer){// Here we create a new connection, but you could reuse an existing onewebSocket=io.connect(window.location.hostname);// Creating a google mapvarmapOptions={// Example options for the Google Mapzoom:8,center:newgoogle.maps.LatLng(-34.397,150.644),mapTypeId:google.maps.MapTypeId.ROADMAP};myMap=newgoogle.maps.Map(document.getElementById(mapContainer),mapOptions);webSocket.on('location update',updateMarker);webSocket.on('user disconnected',removeMarker);webSocket.emit('request locations',loadMarkers);}functionupdateMarker(data){varmarker=markers[data.key];if(marker){marker.setPosition(newgoogle.maps.LatLng(data.lat,data.lng));}else{markers[data.key]=getMarker(data.lat,data.lng,data.name);}}functionremoveMarker(key){varmarker=markers[key];if(marker){marker.setMap(null);deletemarkers[key];}}
The client suscribes to location update in order to add markers to the map or update existing ones, and to user disconnected to remove markers from the map after a user is no longer connected.
Additionally, the client emits a request locations event to ask the server to send the locations of users that are already connected (and therefore will not send their location unless it changes), passing an acknowledgement function loadMarkersthat will receive the data and add the corresponding markers.
Now our client is ready to receive geographic updates in real time. But who is sending them?
Here we use the Geolocation API to get the user's location from the browser and send an update to the server, but we could use any client that supports Socket.IO.
functiontryGeolocation(){if(navigator.geolocation){navigator.geolocation.getCurrentPosition(sendLocation,errorHandler);}else{alert("Geolocation is not supported by this browser.");}}functionsendLocation(position){vardata={lat:position.coords.latitude,lng:position.coords.longitude,}webSocket.emit("send location",data);}functionerrorHandler(error){alert("Error detecting your location");}
Now that we have a working application it would be nice to try it out with our friends, so we will upload it to heroku. They have written a very complete article on how to upload a Node.js app, below we will only go over the configuration files you need for this app.
First we need to define our application dependencies in a 'package.json' file so that heroku prepares the required environment, locate it in your project's root folder. Here's an example of the file's content:
We need to tell heroku how to start our application, so create a file called 'Procfile' with the following content:
web:nodeapp.js
The last thing we need is to configure Socket.IO to use long-polling, since at the time of writing heroku's Cedar stack still doesn't support the WebSockets protocol. Add the following to your 'app.js' file, which is the recommended configuration:
// assuming webSocket is the Socket.IO server objectwebSocket.configure(function(){webSocket.set("transports",["xhr-polling"]);webSocket.set("polling duration",10);});