Azavea Labs

Where software engineering meets GIS.

Sometimes you feel like using modules

I’m going to go out on a limb here, and admit I enjoy Javascript.

*deep sigh* Okay, now that that’s out of the way, lets talk about writing good code.

As javascript apps mature, the need for organization and modularity becomes more important. The days of simply inlining your code in the HTML, or keeping all your code in one giant file, or rolling your own class system are fading. If you’re experienced with Javascript, you’ve probably written your own class system at some point, or code loading solution, or had to stitch together plugins for one framework all while your code is written in another. These are all simple solutions to a complex problem, and as is a common theme in software, you probably missed some edge cases. Inlining your code is hard to maintain, and isn’t modular, developing with a team in a single giant file is a nightmare, and rolling your own class loader means lack of compatibility and dealing with cross browser issues. The truth is until recently your new jQuery plugin was written differently than your nice custom layer for Openlayers, was different from your custom Dojo widget, was… well you get the point. You couldn’t expect to write them in the same way, or expect them to arrive at the browser in the same file at the same time. (If you’ve used Dojo, Ext, OpenLayers, jQuery, or Node.JS then I hope you know what I mean)

So, what’s a Javascript developer to do? The language is flexible and forgiving, and there hasn’t been something to standardize your code around, until recently. You might have noticed that the internet is pretty good at coming up with standards for sharing information and software. That’s why I was excited to see the proposal and development of the AMD API. AMD in this case stands for Asynchronous Module Definition. Basically it’s a consistent way to package your code, and it makes it significantly easier to reuse later down the road. A number of major Javascript frameworks have already embraced AMD, including jQuery, Node.JS, Dojo, and others, and I’d like to give you a preview here to hopefully inspire developers to give it a try. More importantly modules, and module loaders are proposed to be part of the spec for the next version of Javascript named “Harmony“, see Modules, and Module Loaders.

So, you like jQuery, modules, and you’re ready to open up an example and tinker around? RequireJS has you covered with a great walkthrough here: http://requirejs.org/docs/download.html#samplejquery . I’d enjoy walking through it on this blog, but it might be beyond the scope of this article. Let me know in the comments if you’d like to see more about this.

So, I wanted to provide a more complicated example, which I hope will be helpful to those looking to make nicely namespaced, properly constructable objects in Javascript. Lets pretend we had the following spec:

1.) Our class should use a private static dictionary of globally unique names that have been booped.
2.) These should be kept long-term, in localStorage when possible
3.) Our class should accept verbs other than boop, and should be extendable

So, lets get fancy for a second, and design something that is re-usable and modular. I’m picturing… A storage class, a smart globally static list class, and our main verb class that is configurable. Lets get to it:

Lets define a basic reusable storage module, something like:

define('storage', [ ], function( ) {
    //private static in-memory storage
    var _data = {};

    var that = {
        hasLocalStorage: function() { ... }
        set: function(key, value) { ... },
        get: function(key) { ... }
    };

    var ClassConstructor = function(name) {
        if (!name || name == "") { throw "Name not provided"; }
        this.prefix = name;
    };
    $.extend(ClassConstructor.prototype, that);
    return ClassConstructor;
});

Full Source for storage.js

and… lets define a class that uses that storage to maintain a globally unique list:

define('unique', ['storage'], function(Storage) {
    //private _globally static_ storage object -- will be shared with other instances of "Unique"
    //you probably wouldn't want this for this implementation, but hey, it's an example, so I wanted
    //to show how to do it.
    var _storage = null;

    var that = {
        setIfUnique: function(key) { ... }
    };

    var ClassConstructor = function(name) {
        if (!name || name == "") { throw "Name not provided"; }
        _storage = new Storage(name);
    };
    $.extend(ClassConstructor.prototype, that);
    return ClassConstructor;
});

Full Source for unique.js

and… lets define a class that uses the unique list, and takes a verb to ‘boop.’

define('booper', ['unique'], function(Unique) {
    var that = {
        //private storage, but not static, local to this object
        _unique: null,
        _verb: 'unset',

        verbObject: function(name) { ...  }
    };

    var ClassConstructor = function(verb) {
        this._verb =  verb || "boop";
        this._unique = new Unique(this._verb);
        this[this._verb] = this.verbObject;
    };
    $.extend(ClassConstructor.prototype, that);
    return ClassConstructor;
});

Full Source for booper.js

so, now we just need a basic web page to test this with:

<html>
    <head>
        <script data-main="js/index-main.js" src="js/require-jquery.js"></script>
    </head>
    <body>
        <h3>Boop em!</h3>
        <input type="text" id="name" name="name"
            style="width:300px"
            placeholder="Enter name to boop, limit once per name"
        />
    </body>
</html>

and how about some basic page code:

require(["jquery", "booper"], function($, Booper) {

    //prep
    var b = new Booper();

    //events
    var doBoop = function(evt) {
        b.boop( $("#name").val() );
    };

    $("#name").on('change', doBoop);
    $("#btnBoop").on('click', doBoop);
});

Full Source for index-main.js

Putting it all together with a working example:
Working Example!

Obviously, this example didn’t need to be this complicated, but we gained a lot from a little bit of extra work. We’ve built three easy modules, that can be easily maintained, extended, and reused. We have a hugely open and flexible page, and when we’re ready for production, we can compact and minify all those files into a single optimized build.

So, you’ll notice I added a somewhat generic three line chunk at the end of each class module. This is just some basic constructor logic so you get the nice internal reference to ‘this’, and you can initialize new objects with “new Foo()”, instead of only having one copy, etc, etc.

I hope you enjoyed this example, and if you’re interested in Azavea’s other projects, checkout our GitHub!

When is seven days not a week?

I’ve been using Date.js on a project, and it’s an awesome library. I especially love when someone else handles dates for me (am-i-right fellow developers? ;) ), and everything was going great until we discovered a rare time bug. I had forgotten something very important regarding time: it’s relative.

We’re not writing guidance software for rockets, so we don’t have to deal with relativity in the Einstein sense, but relative in a more day-to-day and somewhat artificial sense. Although I’m not exactly sure how Benjamin Franklin really felt about daylight savings time, it certainly makes programming a little trickier for us Philadelphians.

Here is an example for those following along in firebug:

var start = new Date('2010/01/01');
var end = new Date('2011/01/01');
var diff = (new TimeSpan(end - start)).days;

for(var i=0;i<diff;i++) {
    var today = new Date(start).add(i).days();
    var plusSeven = today.clone().add(7).days();
    var oneWeek = (new TimeSpan(plusSeven - today));

if ((oneWeek.days != 7) || (oneWeek.hours != 0)) { console.debug("Time Warp: ", today); } }

Say you made some bit of code that generated a date range by adding or subtracting some number of days off a date, you’d have a bug from March 8th through March 14th, and then it’d be gone until November 1st through the 7th! Not only is this sneaky, it’s hard to predict! Although this isn’t really a bug in Date.js, it requires us to change how we think about time.

The recommended Googling didn’t provide a straightforward, and rock-solid technique for identifying and adjusting for this effect in any given time zone.

So I made one:

First, a quick example:
(my apologies if this is obvious or full of mistakes)

//Lets say you wanted to setup some DateTime controls
//with a rolling date range, lets say using an offset in days
//with Date.js you'd probably do something like this:

var offsetDays = 7;
var start = new Date('2010/03/09');
var end = start.clone().add(offsetDays).days();

//so far so good, we got the intended date:
//Tue, March 16th, 2010
//but we want to update and store the rolling date range,
//so lets get that range back out:

// ... snip! ...

var diff = (new TimeSpan(end - start));
if (diff.days != offsetDays) {
    //daylight savings time caused time to 'spring ahead',
    //and our 7 days is instead 6 days 23 hours
    console.debug('user changed date');
}

We’re going to need a simple function that gets a TimeSpan that is adjusted for the daylight savings time adjustment:

// gets the difference between two dates,
// adjusting for daylight savings time.
var getDSTAdjustedDiff = function(startDate, endDate) {
    var startOffset = startDate.getUTCOffset();
    var endingOffset = endDate.getUTCOffset();

    if (startOffset > endingOffset) {
        startDate = startDate.clone().add({hours: -1 });
    }
    if (endingOffset > startOffset) {
        endDate = endDate.clone().add({hours: -1 });
    }
    return (new TimeSpan(endDate - startDate));
};

If we revisit our example from before:

var start = new Date('2010/01/01');
var end = new Date('2011/01/01');
var diff = (new TimeSpan(end - start)).days;

for(var i=0;i<diff;i++) {
    var today = new Date(start).add(i).days();
    var plusSeven = today.clone().add(7).days();
    var oneWeek = (new TimeSpan(plusSeven - today));
    var adjustedDiff = getDSTAdjustedDiff(today, plusSeven);

    if ((oneWeek.days != 7) || (oneWeek.hours != 0)) {
        console.debug("Time Warp: ", today);
    }
    if ((adjustedDiff.days != 7) || (adjustedDiff.hours != 0)) {
        console.debug("Adjustment fail!: ", today, adjustedDiff );
    }
}

If everything went well, this code block should run and run without the second block ever firing. I hope this helps someone else out there avoid this pitfall, now if I can just figure out time zones…

OpenLayers meet ArcGIS.com

“Hey guys, we can support ArcGIS online basemaps, right?”
“Oh yeah, definitely, we use OpenLayers, there must be a layer for that”

Azavea is no stranger to contributing to open source projects, so when we learned that OpenLayers was lacking full support for the newer ArcGIS basemaps, I knew I had a job to do. The community had been looking for this functionality for over 2 years, and still didn’t have a solution. You might say that developing tools for crime fighters would be exciting and rewarding, but it is also nice when something you wrote has the potential to be used everyday by a community you value.

We quickly discovered that a few partial solutions existed, but something was ever so slightly off, those layers weren’t performing in all the configurations we needed. We had a head scratching situation, we had tiles, we had a standard format, but it seemed like they wouldn’t let us get the calibration just right. Finally the eureka moment hit when we discovered that the server was not being entirely literal with it’s capabilities, and that they were actually subtly changing as the zoom level changed! Once we knew that, it was a simple matter of overriding some base code, recalibrating and extrapolating the extent, and we were set!

Fast forward to today, and we’ve peer-reviewed the layer internally, added documentation, multiple examples, and unit tests, and we submitted a patch to the community about a month ago. Although it isn’t officially in the trunk yet, we already have word from a new friend in Spain that the layer has helped him with a project!

I’m excited that this layer could help more developers and organizations take advantage of the great cartography offered by Esri while using the powerful OpenLayers platform. So please keep your fingers crossed while we go through the approval process once again, or checkout the patch and try it for yourself, and let us know what you think. It can support out of the box ArcGIS.com basemaps, local ArcGIS servers, and raw file caches produced by ArcGIS.

Thank you to those who helped (Jeff, Aaron, Zwarg, and Robert), and to those who helped online and in the community, Thanks! Also thanks to Esri, who just made their ArcGIS map tiles available for free to everyone!