A few weeks back I tweeted about some fun stuff we were doing with the Leaflet javascript map library. For those who may not know about it, Leaflet is a new(ish) library from Cloudmade (http://leaflet.cloudmade.com/). What’s really great about this library is that is really really lean. Like 24Kb on the wire. Of course this means that instead of handling every possible thing in every browser, the developers focused on a reasonable subset of functionality, and make the assumption you’re using a reasonably modern browser.
How Lean is Lean?
As I said, we’re talkin’ 24Kb total and it’s all in one file, which means one request. This makes it ideal for use in mobile web applications. Compared to the Esri Compact javascript api, which is 114Kb itself, AND makes 11 additional requests for more Dojo pieces and parts, totaling out to ~145Kb, Leaflet is substantially leaner. And since there is less javascript running… it’s also faster.
Leaning on the Browser: CSS3
Another noticeable difference is that Leaflet leans heavily on CSS3. This alone is good because CSS3 operations (transforms etc.) are native so they are MUCH faster than doing the same thing via javascript. And, on iOS, CSS3 transforms are hardware accelerated, which means super smooth panning and zooming. Relying on CSS3 also removes a ton of code – of course if your end users are rocking Internet Explorer 6, this may be sub-optimal. (What isn’t sub-optimal in IE6??). Luckily for us all, the vast majority of smartphones use a webkit browser ![]()
ArcGIS Server Goodies
Ok, so that’s set the stage – leaflet is good and shiny, but how do we (die-hard Esri geogeeks) integrate services hosted on ArcGIS Server? We write code! (mostly)
Tiled Map Services
Tiled map services are really easy to use in Leaflet and don’t require any coding. Since Leaflet has a TileLayer, and assumes we’re all using the One-True-Projection (did I say there were limitations?) all we need to do is hand the TileLayer constructor the url to the tile service.
var streetMapUrl = 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}'; var streetMapLayer = new L.TileLayer(streetMapUrl, { maxZoom: 19, attribution: 'Tiles: © Esri' });
The thing to note here is that the CloudMade tile urls have the parameters ordered differently ({z}/{x}/{y}) than an ArcGIS Server tile cache ({z}/{y}/{x}). That’s it!
ArcGIS Dynamic Map Services
This does require some code (over in GitHub if you want to peek). I ended up creating two types of layers that use ArcGIS Dynamic map services. The first one I build is based off the ImageOverlay class. The AgsDynamicLayer class takes the url to a map service, and will make requests for map images that cover the visible extent (just like the ArcGISDynamicLayer in the javascript and Flex APIs). You can specify which layers in the map service to show, as well as setup definition queries.
var sitesLayer = new L.AgsDynamicLayer( 'http://ags2.dtsagile.com/ArcGIS/rest/services/LiveFeeds/WxMappr/MapServer', { maxZoom: 19, attribution: "NOAA", opacity: 1, layers: 'show:2' }); _map.addLayer(sitesLayer);
This is a *minimalist* implementation – if you want to use this, and it’s missing a feature you want – add it! Since it’s using the REST API, this is really just about creating Urls, so it’s simple. Also – this example is using a demo NEXRAD service, so don’t use it in your apps. Ok, you actually could and we likely would not notice, but we move stuff around a lot so it will likely break at some really inconvenient time, and then your boss will be all up in your face etc. etc. In short – be cool. Moving on…
Turns out that this works quite well in desktop browsers, but crashes Safari on iOS after a bunch (like 50) pan/zoom operations. Not quite sure why this is, but hopefully someone can take a peek at the code and have an “Ah HA!” moment and fix this.
SO – since that did not work on mobile browsers, and I saw that there was a TileLayer.WMS class in the repo, I thought I’d go that route – assuming that whatever was causing Safari on iOS to puke would not be an issue. Thus the TileLayer.AGSDynamic class (yeah yeah – I just noticed the inconsistent casing). This has the same parameters etc as the AgsDynamic layer, but instead of making a request for a single image covering the current visible extent, it makes a bunch of requests for images that line up with tiles.
var nexrad = new L.TileLayer.AGSDynamic( 'http://ags2.dtsagile.com/ArcGIS/rest/services/LiveFeeds/WxMappr/MapServer', { maxZoom: 19, attribution: "NEXRAD", opacity: 0.5, layers: 'show:18', cacheBuster: true }); _map.addLayer(nexrad);
This is cool because now you’ll have consistent urls, which means you’ll play nice with caching and once an image is loaded your pan/zoom operations don’t have to re-load it. However, the first layer I used this with is a NEXRAD feed that updates every 5 minutes, so I added a cacheBuster property which basically tacks a random number on the back of the request to force new images every time. Down the line I’d probably make another custom layer that has a timer and only changes this random string every 5 minutes. Unfortunately, this layer still causes Safari on iOS to crash (doh!)
Here’s a simple screen grab of leaflet, loaded with the Esri Streetmap tile cache, NEXRAD data coming in via TileLayer.AGSDynamic and windfields coming in via AgsDynamic.
Lets Play!
I’m going to push up some simple demo’s later today (and will update this post) but until then UPDATE: Live demo’s of can be found at http://demo.dtsagile.com/leaflet
You can fork our Leaflet repo at GitHub or just grab code, and use the agstile.html and ags-dynamic.html files includes in /debug/map. Once we (or someone) sorts out why these are crashing Safari, we’ll submit a pull request and hopefully get this into the main branch of the Leaflet source.





