James Thomas

Notes on JavaScript

GeoPix Live Photos

Andrew Trice wrote a great sample application for IBM Bluemix called GeoPix.

GeoPix uses the IBM MobileFirst services to provide a native iOS application which allows users to capture images from their mobile phones, storing them on the local device with automatic syncing to the cloud when online.

Using a web application, the user can view their images over a map based upon their location when the photo was taken.

I’ve been using the demonstration to highlight the mobile capabilities of IBM Bluemix and had an idea for an enhancement…

Could the web page update with new pictures without having to refresh the page?

Looking at the source code, the web application is a Node.js application using the Leaflet JavaScript library to create interactive maps. Images captured from mobile devices are synchronised to a remote CouchDB database. When the user visits the GeoPix site, the application queries this database for all mobile images and renders the HTML using the Jade templating language.

Adding support for live photos will require two new features…

  • Triggering backend events when new photos are available
  • Sending these photos in real-time to the web page

Change Notifications Using CouchDB

CouchDB comes with built-in support for listening to changes in a database, change notifications. The _changes feed for a database is an activity stream publishing all document modifications.

GeoPix uses the following CouchDB client library, to interact with our database from NodeJS. This library provides an API to start following database changes and register callbacks for updates.

Modifying our application code, upon connecting to the CouchDB database, we register a change notification handler. We follow all changes that occur in the future (since: “now”) and include the full document contents in the change event (include_docs: true).

1
2
3
4
5
6
7
8
9
10
Cloudant({account:credentials.username, password:credentials.password}, function(err, cloudant) {
    var geopix = cloudant.use(database);
    var feed = geopix.follow({include_docs: true, since: "now"});

    feed.on('change', function (change) {
      // ....we can now send this data to the web pages
    });

    feed.follow();
})

Now, every time a user sync their local photos to the cloud, the registered callback will be executed.

How do we send new photos to the web page over a real-time stream?

Real-time Web with Socket.IO

Introducing Socket.IO

Socket.IO enables real-time bidirectional event-based communication.
It works on every platform, browser or device, focusing equally on reliability and speed.

Sounds great!

By embedding this library into our application, we can open a real-time event stream between the server and client. This channel will be used by the client to listen for new images and then update the page.

The library has great documentation and provides both server and client modules. It also integrates with ExpressJS, the web framework used in GeoPix. Socket.IO can use either WebSocket or long-polling transport protocols.

Socket.IO supports running under ExpressJS with minimal configuration, here are the changes needed to start our real-time stream in GeoPix:

1
2
3
4
5
6
7
8
9
10
11
12
13
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);

// ...snipped out the app routes for express

io.on('connection', function (socket) {
    console.log('New Client WSS Connection.')
});

var port = (process.env.VCAP_APP_PORT || 3000);
server.listen(port);

When a document change event is fired, executing the handle we registered above, we want to send this data to all connected clients.

Using the emit call from the server-side API will do this for us.

1
2
3
feed.on('change', function (change) {
    io.sockets.emit('image', change);
});

Now we’re sending changes to the clients, we need to modify the client-side to listen for events and update the page.

Socket.IO provides a JavaScript client library that exposes a simple API for listening to events from the server-side stream. Once we’ve included the script tag pointing to the client library, we can register a callback for image events and update the DOM with the new elements.

We’re sending the full database document associated with each photo to the client. The raw image bytes are stored as an attachment.

1
2
3
4
5
6
7
8
9
10
11
12
13
var socket = io(); // TIP: io() with no args does auto-discovery
socket.on('connect', function () {
    console.log('WSS Connected');

    socket.on('image', function (image) { // TIP: you can avoid listening on `connect` and listen on events directly too!
        var attachment = Object.keys(image.doc._attachments)[0]
        var url = "/image/" + image.doc._id + "/" + attachment;
        add_new_image(url, image.doc.clientDate, 'latitude: '
            + image.doc.latitude + ', longitude: '
            + image.doc.longitude + ', altitude: '
            + image.doc.altitude);
    });
});

…and that’s it! Now our web pages will automatically update with new photos whenever the mobile application syncs with the cloud.

CouchDB + Socket.IO = Real-time Awesome!

Adding real-time photos to our application was amazingly simple by combining CouchDB with Socket.IO.

CouchDB’s _changes API provided an easy way to follow all modifications to database documents in real-time. Socket.IO made the configuration and management of real-time event streams between our server and client straightforward.

With minimal code changes, we simply connected these two technologies to create a real-time photo stream for our GeoPix application. Awesome.

Comments