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.
xxxxxxxxxx
<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>
https://d3js.org/d3.v4.min.js
https://d3js.org/d3-geo-projection.v1.min.js
https://d3js.org/topojson.v1.min.js