Monday, March 10, 2008

Simile Timeline

Over the last week or so, I've been working on a webapp for managing expedition sample requests. One of the bigger challenges has been to find an effective way to visualize the sample requests. Traditional histograms and line graphs are an option, but they offer limited interactivity. I was hoping to find something more "snazzy" to let the users really explore the data.

I mentioned this in passing to my friend Doug Fils and he pointed me to Simile Timeline. Timeline is something I had seen in passing before but hadn't considered for this particular project. Timeline provides a Google Maps-type interface for exploring time-based data. I really liked the interface, but needed a way to put depth on the scale instead of time. It turns out I wasn't the only one looking for different units.

After chiming in on enhancement request, the original reporter, Charlie Kershner, sent me an email with the solution he'd come up with: use years, decades, centuries, and millennia as proxies for metric depth units. The conversion factors are all right, it's just the units that are wrong. This is a really clever solution, so I was glad Charlie passed it along. Using it, I had a working demo in about an hour.

I decided to poke around in the Timeline code to see what it would take to properly support depths. It is a really coherently designed tool with well defined interfaces for extension and customization. I can't claim to be fully understand everything, but with a little monkey see, monkey do, and a bit of brute force, I got proper depth support:


This is the default rendering. I think for my application, I'll end up customizing the event painters. I need to find a way to simplify the rendering, especially in busy sections like this. There's also a fair number of errors in the data that I need to go back and fix. Being able to visualize it in this form makes it really easy to see the errors (Hint: those blue bars at the top and bottom of the main band represent typos in the data. There were no multi-meter samples...)

I don't have a live demo for people to play with because I'm working with "live" data that isn't public. However, there are numerous examples out at the Timeline site. There were a few gotchas I ran into while developing and testing things:
  1. You need to serve your own copy of Timeline because of cross-domain issues with mixing code from the Simile site and from a local extension.
  2. You need to tell Timeline to use your new unit in two different places--when you create the event source and when you actually create the timeline. Failure to do so will have you pulling out your hair chasing down errors.
  3. You need to use your customized methods to create your bands
  4. Not all of the rendering options (like showEventText: false) seem to work. I think this is because I don't fully understand the rendering pipeline and how it is customized. This is one area I'm going to be looking at in the future.
Here's an example of the Javascript to create the the example above. Note the bolded areas that were mentioned in #2 and #3:

var tl;
function onLoad() {
var eventSource = new Timeline.DefaultEventSource(new SimileAjax.EventIndex(Timeline.DepthUnit));

var theme = Timeline.ClassicTheme.create();
theme.event.bubble.width = 320;
theme.event.bubble.height = 220;
var d = Timeline.DepthUnit.wrapDepth(0)
var bandInfos = [
Timeline.Depth.createBandInfo({
eventSource: eventSource,
date: d,
width: "80%",
intervalUnit: Timeline.DepthUnit.METER,
intervalPixels: 100
}),
Timeline.Depth.createBandInfo({
eventSource: eventSource,
date: d,
width: "20%",
intervalUnit: Timeline.DepthUnit.DECAMETER,
intervalPixels: 25,
overview: true,
convertToBaseUnit: true
})
];
bandInfos[1].syncWith = 0;
bandInfos[1].highlight = true;

tl = Timeline.create(document.getElementById("tl"), bandInfos, Timeline.HORIZONTAL, Timeline.DepthUnit);
tl.loadJSON("samples.js", function(json, url) {
eventSource.loadJSON(json, url);
});
}
var resizeTimerID = null;
function onResize() {
if (resizeTimerID == null) {
resizeTimerID = window.setTimeout(function() {
resizeTimerID = null;
tl.layout();
}, 500);
}
}

The code for the depth extension is available in a Mercurial repository where you can grab a zip of the most recent version. Feel free to use it however you like. I've offered to contribute the code to the main Timeline repository so future versions may not require you to host your own Timeline.

1 comment:

Troels said...

Excellent hack Josh. This is exactly what I am looking for to use timeline for features on the genome.

However trying to use the above example I keep getting an error with tl.layout tl not defined.

Maybe I am missing something completely obvious, I have checked that your script loads etc. I am thinking that your hack may not be compatible with later versions of timeline?

What version of timeline did you do this hack for?