behind the gist

thoughts on code, analysis and craft

Real-time location visualization with D3

Mike Bostock is amazing. I use his D3 library for most of my web visualization work. The documentation is fantastic, the code is clean and concise and he’s created a great little ecosystem of examples on the D3 site and with bl.ocks.org.

I’ve been working on visualizing indoor location activity and one of the views is a live view of individual device locations. To provide some additional context, the view also provides the recent history path (or “tail”) for each device. Getting the path animation correct took a little trial and error. This post describes how I got points and paths to update and animate smoothly using D3.

Roughly, the system works by reporting sensed locations of detected devices at a regular interval. The total number of devices changes as the devices physically enter and exit the range of the sensors. The updates are communicated asynchronously to the server with a simple update record similar to

location record
1
{id: 1, time: 1344182422325, x: 34.87, y: 12.23}

When the browser sees a new device id, it starts showing the device location on the map. The browser knows the expected longest update interval, and if a device hasn’t been updated by then, it is removed from the map. While the device is being updated, the browser constructs a tail showing previous locations of the device. We would like this tail to represent a fixed duration (e.g. the last 30 seconds). Visually, that means longer tails will correspond to faster moving devices and the absence of a tail means a stationary device.

Using D3, this tail is simply an SVG path that we wish to animate. There is already a really good tutorial on path transitions in D3. But our use doesn’t really fit that pattern. We have a case where we are trying to interpolate paths of different lengths.

1
2
3
4
<!-- start path -->
<path d="M10,10 15,12"></path>
<!-- end path -->
<path d="M10,10 15,12 16,14"></path>

After a little trial and error, I found what worked best was to chop up the line into multiple segments and animate each segment individually following the device motion.

1
2
3
4
5
6
<!-- start segment -->
<path d="M10,10 10,10"></path>
<!-- full segment -->
<path d="M10,10 15,12"></path>
<!-- end segment -->
<path d="M15,12 15,12"></path>

This way, the segment starts at the first point, it then “grows” towards the second point and then finally collapses into the second point providing a smooth animation. To ensure the tail represents the desired duration, I just need to make sure the last transition is delayed for that duration.

adding a segment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var start_loc = segment.start.join(",");
var end_loc = segment.end.join(",");
var start_path = "M" + start_loc + " " + start_loc;
var full_path = "M" + start_loc + " " + end_loc;
var end_path = "M" + end_loc + " " + end_loc;

this.svg.append("path")
.attr("id", element_id)
.attr("class","tail")
.attr("d", start_path)
.transition()
.ease('linear')
.attr("d", full_path)
.transition().delay(this.duration)
.ease('linear')
.attr("d", end_path)
.remove();

The full gist is available here and it can be seen in action here.