A few weeks ago, I started playing around with Backbone.js. At the end of last week, I noticed something on Twitter about Leaflet. I’ve been a long-time devotee of the Play! framework, as you may be aware if you’ve ever read this blog before.

Play 2 was released yesterday. This seems like a perfect time to mix them all together, just for fun*

I originally looked at combining Backbone and Leaflet to create markers on a map, which was a fun and easy 20 minutes – it was pure client-side code, simple to do and unbelievably pointless. Unless the map markers could be shared across multiple users, it was ultimately useless. That’s when I decided to hijack and re-purpose Play 2’s websocket-chatroom sample application- I thought it would be nice to have the places you mark on your map pop up in real-time on the map that other people are looking at, and the chatroom code required minimal tweaking to do this.

At this point, I should apologise. I just got back from the gym, and I’m too tired to think coherently enough to write good blog prose. Luckily, we’re about to head into the technical details

On start-up, mapcrap acts like a tradional MVC app – it gets the existing map places from the database via a RESTful call, and displays them on the map. From this point on, everything is then driven by websocket interactions. When you create a place, instead of it being persisted via REST, the JSON of the place is sent over the websocket. The server-side app then persists the place and broadcasts it to every user’s websocket using Akka.

in.onMessage(new F.Callback()
{
    public void invoke(JsonNode event)
    {
        Place place = Json.fromJson(event,
                                    Place.class);
        place.createdOn = new Date();
        place.save();
        defaultMap.tell(new MapEvent(userId,
                                     place));
    }
});

The locally-added place isn’t added to the local collection of places directly – instead, it receives it through the broadcast. This is a lazy way of getting around Backbone collections not accepting duplicate models.

When the client receives a push message informing it that a new place has been added, it adds the place to the Backbone collection of places – in turn, the event listener on the collection triggers the addition of a popup to the map.

this.model.bind("add", function(place){
  var view =new PopupContentView({model:place});
  var latlng = new L.LatLng(place.get('latitude'), place.get('longitude'));
  var messagePopup = new L.Popup({autoPan:false, closeButton:false});
  messagePopup.setLatLng(latlng);
  messagePopup.setContent(view.render().el);
  self.options["map"].addLayer(messagePopup);
}, this);

Notes:

  • The easiest way to see this working is to open two browsers and point them both at http://localhost:9000
  • Left-click on the map to open a “create place” popup. If you’ve entered an avatar URL in the Avatar URL text box, this image will also be displayed in the pop-up
  • Enter a message, and click Create! – the pop-up will (or should, at least) appear on your map and the map in the other browser window
  • This doesn’t work in Internet Explorer, because it doesn’t support web sockets
  • If you want to try this on a network, change the URL in models.js from localhost to a hostname/IP address
  • This isn’t on Heroku because, the last I heard at, web sockets are not supported there

You can download the mapcrap – you’ll need Play 2.0 to run it.
Instructions:

  • Unzip it
  • cd into the directory
  • play
  • run
  • Point your browsers at http://localhost:9000

If you have any problems, please let me know!

* My parents are staying with me this week. My dad asked, “what is this good for?” about 2 hours ago. The explanation of the joy of coding and the satisfaction of writing something vaguely cool is still, sporadically, going on.

6 thoughts on “Play 2, Akka, Websockets, Backbone.js and Leaflet – just having fun, really

Leave a Reply

Your email address will not be published. Required fields are marked *