Created by Christopher Manning
Line simplification of the CTA train routes. Different line interpolations and simplification levels create interesting designs.
I created this because I like line simplification, d3.js, the CTA train map, and abstract geometric shapes.
extract_gtfs.rb extracts the train lines from a GTFS zip file and creates a GeoJSON file.
xxxxxxxxxx
<html>
<head>
<meta charset="utf-8">
<title>CTA Line Simplification</title>
<style type="text/css">
body {
padding: 0;
margin: 0;
}
path {
stroke-linejoin: round;
fill: none;
}
circle {
fill: white;
stroke: black;
display: none;
stroke-width: 1px;
}
</style>
</head>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>
<script src="readme-simplify.js"></script>
<script type="text/javascript">
config = {"simplification": 9, "interpolation" : "cardinal", "strokeWidth": 4, "showStops": false};
gui = new dat.GUI();
var examples = gui.addFolder('Examples');
examples.open()
config.random = function(){
gui.__controllers.forEach(function(c){
if(typeof(c.__select) != 'undefined') {
c.setValue(c.__select[Math.floor(Math.random()*(c.__select.length-1))].value)
} else {
if(c.property!="random" && c.property!="strokeWidth" && c.property!="showStops"){
c.setValue(Math.floor(Math.random() * c.__max) + c.__min)
}
}
})
draw()
}
examples.add(config, "random")
config.accurate = function(){
config["interpolation"] = "linear"
config["simplification"] = 0
draw()
}
examples.add(config, "accurate")
config.curly = function(){
config["interpolation"] = "cardinal"
config["simplification"] = 9
draw()
}
examples.add(config, "curly")
config.minimal = function(){
config["interpolation"] = "linear"
config["simplification"] = 100
draw()
}
examples.add(config, "minimal")
maxSimplification = 53
simplificationChanger = gui.add(config, "simplification", 0, 100).step(.1).listen()
simplificationChanger.onChange(function(value) {
draw()
});
interpolationChanger = gui.add(config, "interpolation", ["linear", "step-before", "step-after", "basis", "basis-open", "basis-closed", "cardinal", "cardinal-open", "cardinal-closed", "monotone"]).listen()
interpolationChanger.onChange(function(value) {
draw()
})
strokeWidthChanger = gui.add(config, "strokeWidth", 1, 20).listen()
strokeWidthChanger.onChange(function(value) {
d3.selectAll(".routes path").style("stroke-width", value)
d3.selectAll(".stops circle").attr("r", value/2)
});
showStopsChanger = gui.add(config, "showStops").listen()
showStopsChanger.onChange(function(value) {
d3.selectAll(".stops circle").style("display", value ? "block" : "none")
});
width = window.innerWidth
height = window.innerHeight - 5
line = d3.svg.line()
.interpolate(config["interpolation"])
zoom = d3.behavior.zoom()
.scaleExtent([0, 100])
.on("zoom", function () {
config["simplification"] = d3.event.scale
draw()
})
svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom)
var projection = d3.geo.mercator()
.scale(54596)
.translate([83920, 44300])
d3.json("chicago-transit-authority_20121228_1318.json", function(json) {
routes = svg.append("g").attr("class", "routes").selectAll("path").data(json.features.filter(function(d) { return d["geometry"]["type"] == "LineString" }))
routes.enter().append("path")
.attr("id", function(d) { return d.properties.route_id })
.style("stroke", function(d) { return "#"+d.properties.route_color })
.style("stroke-width", config["strokeWidth"])
.on("mouseover", function(d) {
d3.select(this).style("stroke-width", config["strokeWidth"] * 2)
})
.on("mouseout", function(d) {
d3.select(this).style("stroke-width", config["strokeWidth"])
})
//.attr("d", function(d) { return line(d.geometry.coordinates.map(projection)) })
stops = svg.append("g").attr("class", "stops").selectAll("circle").data(json.features.filter(function(d) { return d["geometry"]["type"] == "Point" }))
stops.enter().append("circle")
.attr("id", function(d) { return d.properties.stop_id })
.attr("class", function(d) { return d.properties.route_id })
.attr("r", config["strokeWidth"]/2)
.on("mouseover", function(d) {
d3.select(this).attr("r", config["strokeWidth"]/2 * 2)
})
.on("mouseout", function(d) {
d3.select(this).attr("r", config["strokeWidth"]/2)
})
.attr("transform", function(d) {
xy = projection(d.geometry.coordinates)
return "translate("+xy[0]+","+xy[1]+")"
})
.append("svg:title")
.text(function(d, i) { return d.properties.name })
draw()
// intro animation
//var interpolator = d3.interpolateNumber(0, config["simplification"])
//svg.transition().duration(1000).tween("withchange", function() {
// return function(t) {
// config["simplification"] = interpolator(t)
// draw()
// };
//})
})
function draw() {
zoom.scale(config["simplification"])
line.interpolate(config["interpolation"])
routes.attr("d", function(d) {
return line(simplify(d.geometry.coordinates.map(projection), maxSimplification*(config["simplification"]*.01), true))
})
}
</script>
</body>
</html>
https://d3js.org/d3.v3.min.js
https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js