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.
socket.on("send location", function(data) {
  socket.get('userkey', function(err, key) {
    var user = connectedUsers[key];
    if(user) {
      user.lat = data.lat;
      user.lng = data.lng;
      data.name = user.name
      data.key = key;
      socket.broadcast.emit("location update", data);
    }
  });
});
socket.on("request locations", function(sendData) {
  sendData(connectedUsers);
});

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.

  var webSocket, myMap;
var markers = {};

function initialize(mapContainer) {
  // Here we create a new connection, but you could reuse an existing one
  webSocket = io.connect(window.location.hostname);

  // Creating a google map
  var mapOptions = {
    // Example options for the Google Map
    zoom: 8,
    center: new google.maps.LatLng(-34.397, 150.644),
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };

  myMap = new google.maps.Map(document.getElementById(mapContainer), mapOptions);

  webSocket.on('location update', updateMarker);
  webSocket.on('user disconnected', removeMarker);

  webSocket.emit('request locations', loadMarkers);
}

function updateMarker(data) {
  var marker = markers[data.key];
  if(marker) {
    marker.setPosition(new google.maps.LatLng(data.lat,data.lng));
  } else {
    markers[data.key] = getMarker(data.lat, data.lng, data.name);
  }
}

function removeMarker(key){
  var marker = markers[key];
  if(marker){
    marker.setMap(null);
    delete markers[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.
function loadMarkers(data) {
  for(key in data) {
    var user = data[key];
    markers[key] = getMarker(user.lat, user.lng, user.name);
  }
}

function getMarker(lat, lng, label) {
  return new google.maps.Marker({
    title: label,
    map: myMap,
    position: new google.maps.LatLng(lat,lng)
  });
}
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.
function tryGeolocation(){
  if(navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(sendLocation, errorHandler);
  } else {
    alert("Geolocation is not supported by this browser.");
  }
}

function sendLocation(position) {
  var data = {
    lat : position.coords.latitude,
    lng : position.coords.longitude,
  }
  webSocket.emit("send location",data);
}

function errorHandler(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:
{
  "dependencies": {
    "mime": "1.2.x",
    "socket.io": "0.9.x"
  },
  "main": "app.js",
  "engines": {
    "node": "0.8.x"
  }
}

We need to tell heroku how to start our application, so create a file called 'Procfile' with the following content:

web: node app.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 object
webSocket.configure(function () {
  webSocket.set("transports", ["xhr-polling"]);
  webSocket.set("polling duration", 10);
});
Done! You can check the full source code and fork it on github.
Wanna try? Go to our live demo.

Check out the next posts in this series:
Part two
Part one

Comments: