Skip to content

Instantly share code, notes, and snippets.

@Andrew-Reid
Last active October 23, 2016 04:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Andrew-Reid/290940be1ba0f3a68fc707c9bf1584ca to your computer and use it in GitHub Desktop.
Save Andrew-Reid/290940be1ba0f3a68fc707c9bf1584ca to your computer and use it in GitHub Desktop.
Railway symbology in d3.js

This is an example of how to achieve a railway style symbology along topojson features. Shaded areas represent track structures such as tunnels, bridges, and avalanche sheds. It is hard to see at this scale, but portions of this section are double or triple tracked.

The area shown is the big hill near the British Columbia - Alberta border at the conntinental divide in the Rockies. The hill features twin spiral tunnels built in 1909, replacing a stretch of track that exceeded a four percent gradient. See Big Hill on wikipedia.

Data is from NRCan's geogratis data extraction service. This block uses d3v4.

Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.rail {
fill:none;
stroke-width: 1;
stroke: #000;
}
.contour {
fill:none;
stroke-width: 0.5;
stroke: #bbb;
}
.structures {
stroke-width: 20px;
opacity: 0.4;
stroke: #000;
fill:none;
}
.water {
fill: #43a2ca;
}
svg {
background: #a8ddb5;
}
</style>
<body>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v1.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var projection = d3.geoKavrayskiy7()
.scale(1050000)
.rotate([116.41, 0])
.center([0,51.428])
.translate([width / 2, height / 2])
.precision(.1);
var path = d3.geoPath()
.projection(projection);
var g = svg.append("g");
var g2 = svg.append("g");
d3.json("spiral.json", function(error, spiral) {
// Add the rail
var paths = g2.selectAll("path")
.data(topojson.feature(spiral, spiral.objects.tracks).features)
.enter().append("path")
.attr("d", path)
.attr("class", "rail")
.attr("id",function(d,i) { return "b" + i });
// Figure out how many cross hatches are needed for each track section
paths.each(function(d,i) {
var currentPath = d3.select("#b" + i).node();
var totalLength = currentPath.getTotalLength();
var numberOfSymbols = Math.round(totalLength/6);
var spacingOfSymbols = totalLength/numberOfSymbols;
var intialSpacng = spacingOfSymbols/2;
var j = 0;
// Add each cross hatch at the right angle
while (j < numberOfSymbols) {
var p1 = currentPath.getPointAtLength( (spacingOfSymbols/2 - 5) + (spacingOfSymbols * j) );
var p2 = currentPath.getPointAtLength( (spacingOfSymbols/2 + 5) + (spacingOfSymbols * j) );
var p3 = currentPath.getPointAtLength( (spacingOfSymbols/2) + (spacingOfSymbols * j) );
var r = 3; // length of cross hatch from line
var m = (p2.y - p1.y) / (p1.x - p2.x);
m = 1/m;
var k = r / Math.sqrt( 1 + (m*m) );
if (m == Infinity) {
p1.x = p3.x;
p1.y = p3.y + r;
p2.y = p3.y - r;
p2.x = p3.x;
}
else {
p1.x = p3.x + k;
p1.y = p3.y + (m * k);
p2.x = p3.x - k;
p2.y = p3.y - (m * k);
}
var line = g2.append("line")
.attr("x1",p1.x)
.attr("x2",p2.x)
.attr("y1",p1.y)
.attr("y2",p2.y)
.attr("class", "rail")
.attr("stroke-width",1)
;
j++;
}
});
})
d3.json("contours.json", function(error, contours) {
// contours
var paths = g.selectAll(".contour")
.data(topojson.feature(contours, contours.objects.contours).features)
.enter().append("path")
.attr("d", path)
.attr("class", "contour")
;
});
d3.json("structures.json", function(error, structures) {
// structures - bridges/tunnels/avalanche sheds
var paths = g.selectAll(".structures")
.data(topojson.feature(structures, structures.objects.structures).features)
.enter().append("path")
.attr("d", path)
.attr("class", "structures")
;
});
d3.json("water.json", function(error, water) {
// add water
var paths = g.selectAll(".water")
.data(topojson.feature(water, water.objects.water).features)
.enter().append("path")
.attr("d", path)
.attr("class", "water")
;
});
</script>
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment