Skip to content

Instantly share code, notes, and snippets.

@kaezarrex
Last active August 29, 2015 13:59
Show Gist options
  • Save kaezarrex/10998624 to your computer and use it in GitHub Desktop.
Save kaezarrex/10998624 to your computer and use it in GitHub Desktop.
NC State Wolfline arrivals graph

The transit data comes through my proxy from the TransLoc OpenAPI. Routes are represented as large, colored nodes. Stops are small, gray nodes. Edges represent the minimum arrival prediction between a given stop and all vehicles on a given route.

The code needs refactoring.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Arrival Graph</title>
<style>
body {
background-color: white;
}
.node {
stroke: #fff;
stroke-width: 2px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
</style>
<script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
<script src="http://jashkenas.github.io/underscore/underscore-min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<script src="main.js"></script>
</body>
</html>
var agency = 'ncsu';
var width = 960;
var height = 500;
var graph = {nodes: [], links: []};
var force = d3.layout.force()
.charge(-20)
.gravity(0.02)
.friction(0.8)
.linkDistance(function (d) { return d.value / 1000 / 10; })
.size([width, height]);
var fetchAgencies = function (agency) {
return $.getJSON('http://tlapi.dhaz.in/agencies.jsonp?callback=?&agencies=' + agency);
};
var fetchArrivals = function (agency) {
return $.getJSON('http://tlapi.dhaz.in/arrival-estimates.jsonp?callback=?&agencies=' + agency);
};
var fetchRoutes = function (agency) {
return $.getJSON('http://tlapi.dhaz.in/routes.jsonp?callback=?&agencies=' + agency);
};
var fetchStops = function (agency) {
return $.getJSON('http://tlapi.dhaz.in/stops.jsonp?callback=?&agencies=' + agency);
};
var cleanArrivals = function (arrivals) {
var cleaned = [];
var now = new Date();
_.each(arrivals, function(arrival) {
var tmp = {};
_.each(arrival.arrivals, function(a) {
if (tmp[a.route_id] === undefined) {
return tmp[a.route_id] = new Date(a.arrival_at) - now;
}
if (tmp[a.route_id] > new Date(a.arrival_at) - now) {
tmp[a.route_id] = new Date(a.arrival_at) - now;
}
});
arrival.arrivals = [];
_.each(_.keys(tmp), function (key) {
arrival.arrivals.push({
route_id: key,
delta: tmp[key]
});
});
cleaned.push(arrival);
});
return cleaned;
};
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var linkGroup = svg.append('g');
var stopNodeGroup = svg.append('g');
var routeNodeGroup = svg.append('g');
$.when(fetchAgencies(agency), fetchRoutes(agency), fetchStops(agency)).done(function (agenciesRes, routesRes, stopsRes) {
var agencyId = agenciesRes[0].data[0].agency_id;
var routes = routesRes[0].data[agencyId];
var stops = stopsRes[0].data;
routeNodes = _.map(routes, function(route) {
return {
id: route.route_id,
name: route.short_name + ': ' + route.long_name,
color: '#' + route.color,
//fixed: true,
type: 'route'
};
});
stopNodes = _.map(stops, function(stop) {
return {
id: stop.stop_id,
name: stop.code + ': ' + stop.name,
code: stop.code,
type: 'stop'
};
});
var routeMap = {};
var stopMap = {};
_.each(stopNodes, function(node) {
stopMap[node.id] = node;
});
_.each(routeNodes, function(node) {
routeMap[node.id] = node;
});
var loopArrivals = function () {
$.when(fetchArrivals(agency)).done(function (arrivalsRes) {
var arrivals = arrivalsRes.data;
var routeIds = {};
var stopIds = {};
var now = new Date();
arrivals = cleanArrivals(arrivals);
graph.nodes.length = 0;
graph.links.length = 0;
_.each(arrivals, function(arrival) {
_.each(arrival.arrivals, function(a) {
graph.links.push({
source: stopMap[arrival.stop_id],
target: routeMap[a.route_id],
value: a.delta
});
stopIds[arrival.stop_id] = 1;
routeIds[a.route_id] = 1;
});
});
_.each(_.keys(routeIds), function(id) {
graph.nodes.push(routeMap[id]);
});
_.each(_.keys(stopIds), function(id) {
graph.nodes.push(stopMap[id]);
});
start();
setTimeout(loopArrivals, 5000);
});
};
run();
loopArrivals();
});
var start = function() {
var link = linkGroup.selectAll(".link")
.data(graph.links);
link.enter().append("line")
.attr("class", "link")
.style("stroke-width", 1);
link.exit().remove();
var stopNode = stopNodeGroup.selectAll(".node")
.data(_.filter(graph.nodes, function(n) {return n.type === 'stop';}), function(d) { return d.id; });
stopNode.enter().append("circle")
.attr("class", function(d) { return 'node ' + d.type; })
.attr("r", function(d) { return d.type === 'route' ? 10 : 5; })
.style("fill", function(d) { return d.type === 'route' ? d.color : '#555'; })
.call(force.drag);
stopNode.exit().remove();
stopNode.append("title")
.text(function(d) { return d.name; });
var routeNode = routeNodeGroup.selectAll(".node")
.data(_.filter(graph.nodes, function(n) {return n.type === 'route';}), function(d) { return d.id; });
routeNode.enter().append("circle")
.attr("class", function(d) { return 'node ' + d.type; })
.attr("r", function(d) { return d.type === 'route' ? 10 : 5; })
.style("fill", function(d) { return d.type === 'route' ? d.color : '#555'; })
.call(force.drag);
routeNode.exit().remove();
routeNode.append("title")
.text(function(d) { return d.name; });
force.start();
};
var run = function() {
force
.nodes(graph.nodes)
.links(graph.links);
start();
force.on("tick", function() {
var link = linkGroup.selectAll(".link");
var route = routeNodeGroup.selectAll(".node");
var stop = stopNodeGroup.selectAll(".node");
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
route.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
stop.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment