Timo Denk's Blog

Commute Time Tracking

· Timo Denk

According to the U.S. Census Bureau the average commute time for U.S. citizens who are neither working at home, nor walking or biking to work, is 25.9 minutes. My commute time from Karlsruhe to SAP is usually about 38 minutes one-way; often protracted by traffic jams. However, these can be avoided by departing at times where rush-hour traffic has not fully built up yet. But which departure time works best? Is it 07:00 or rather 09:00? When departing at 07:40 how much time could one save by departing just ten minutes earlier? In addition to the traffic conditions on the outbound trip, the delay on the way back has to be taken into account likewise. With about 9 hours and 20 minutes spent at work, the traffic at 16:20 matters for a departure at 07:00. For a 09:00 departure it would be 18:20.

Getting the Data

With these ideas in mind I thought about how to retrieve the commute time information anyway. As it turns out the Google Maps API makes the actual travel time in traffic between a given origin and destination programmatically accessible. What I had to do was just writing a script, which requests and stores the current commute time frequently. And here it is, written in JavaScript (Node.js):

const https = require('https'), fs = require('fs'), commander = require('commander');

commander.version('0.0.1')
	.option('-k, --key [type]', 'Google Maps API key')
	.option('-o, --origin [type]', 'travel origin')
	.option('-d, --destination [type]', 'travel destination')
	.option('-r, --resultfile [type]', 'result output file')
	.option('-l, --logfile [type]', 'log file')
	.parse(process.argv);

const API_KEY = commander.key,
	ORIGIN = commander.origin, DESTINATION = commander.destination,
	OUTPUT_FILE = commander.resultfile, LOG_FILE = commander.logfile;
	MODE = "driving", TRAFFIC_MODEL = "best_guess";

function getRequestURL(key, origin, destination, mode, trafficModel) {
	return "https://maps.googleapis.com/maps/api/directions/json" + 
		"?origin=" + encodeURI(origin) + 
		"&destination=" + encodeURI(destination) + 
		"&mode=" + encodeURI(mode) + 
		"&trafficModel=" + encodeURI(trafficModel) + 
		"&departure_time=now" + 
		"&key=" + key;
}

function getShortestDuration(responseBody) {
	if (responseBody.routes.length == 0) return log("routes array empty");
	let shortest = responseBody.routes[0].legs[0].duration_in_traffic.value;
	for (let i = 1; i < responseBody.routes.length; i++) {
		shortest = Math.min(shortest, responseBody.routes[i].legs[0].duration_in_traffic.value);
	}
	return shortest;
}

function writeOutput(duration) {
	const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
	let date = new Date(); // UTC
	fs.appendFile(OUTPUT_FILE, (date.getTime() / 1000) + "\t" + days[date.getDay()] + " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + "\t" + duration + "\t" + (duration / 60) + "\n", (err) => { if (err) return console.log(err); });
}

function log(message) {
	console.log(message);
	fs.appendFile(LOG_FILE, new Date() + "" + message, (err) => { if (err) console.log(err); });
}

let url = getRequestURL(API_KEY, ORIGIN, DESTINATION, MODE, TRAFFIC_MODEL);
https.get(url, (res) => {
	var data = "";
	res.on('data', (d) => { data += d; });

	res.on('end', () => {
		let duration = getShortestDuration(JSON.parse(data));
		writeOutput(duration);
	});
}).on('error', (e) => { log(e); });

The required dependencies can be installed with npm install if the following package.json file is placed in the directory.

{
	"name": "commute-time-tracker",
	"version": "1.0.0",
	"description": "Tracks your daily commute time.",
	"main": "main.js",
	"author": "Timo Denk",
	"license": "ISC",
	"dependencies": {
		"commander": "^2.9.0",
		"https": "^1.0.0"
 	}
}

A cron job executing the script every other two minutes can bet set up with crontab -e and the following command:

*/2 * * * * node main.js --key "Google Maps API key" --origin "76149 Karlsruhe" --destination "69190 Walldorf" --resultfile "out.txt" --logfile "log.txt"

Results and Analysis

I was tracking the commute time between “SAP SE, Walldorf, Germany” and “Tennesseeallee, Karlsruhe, Germany” in both directions 24/7 in one week in May 2017. Note that holidays, construction areas, and other unusual delays are certainly affecting the results.

The function which describes the commute time from Karlsruhe to Walldorf is called $t_{\text{KA to WDF}}(x)$. From Walldorf to Karlsruhe it is $t_{\text{WDF to KA}}(x)$. The following $\LaTeX$ chart plots commute time vs. time of the day on a Wednesday (03. May 2017):

And here is an entire week, where Monday was a public holiday. The peak on Saturday afternoon could have been caused by a car accident.

Now the question arises, when the ideal time to depart is, taking the travel time in the morning and on the way back into account. Mathematically spoken, when is $$t(x)=t_{\text{KA to WDF}}(x) + t_{\text{WDF to KA}}(x+9.25\text{h})$$ the lowest, within a reasonable departure time of e.g. $x\in\left[6.5\text{h},9\text{h}\right]$.

The result is devastating: It’s most efficient to get up very early and depart at 06:30. The is a local minimum at 07:20 looks more appealing to me. Good news is that if the alarm clock fails, it is better to stay in bed a little longer and wait until 09:00.

And now the same for Los Angeles (subject of the featured image on top of this post): Travel time in traffic from Hollywood to the Union Station (LA downtown) on a Monday (08. May 2017).

Note that the y-axis starts at zero (opposed to the charts above) to visualize more clearly, how extreme the traffic delays are. At peak times it takes more than 2.5 times longer to get from origin to destination compared to the shortest time of just twelve minutes.