Skip to content

Instantly share code, notes, and snippets.

@Andrew-Reid
Last active October 26, 2016 15:42
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/01d67e23713e30fac4299f0e004ae98e to your computer and use it in GitHub Desktop.
Save Andrew-Reid/01d67e23713e30fac4299f0e004ae98e to your computer and use it in GitHub Desktop.
The Yukon Ditch - D3 Maps and Aqueduct Symbology

##The Ditch

The Yukon Ditch was a ditch, flume, and pipeline aqueduct system that brought water from what is now Tombstone Territorial Park to the gold fields of the Klondike. Construction commenced in 1906 and was completed in 1910.

It carried water by gravity alone over the course of over 110 km. This presented a number of challenges and explains the circuitous route. Perhaps the greatest challenge was crossing the Klondike River valley. To cross the valley a pipeline was required, one of several along the route. According to T.A. Rickard, writing in 1909, the intake of the intake of the pipeline across the valley was at 2440 feet above sea level, the lowest point of the pipeline was at 1282 feet, with the discharge point at 2240 feet. With a hydraulic head of over 1000 feet, the pressure at the bottom of the pipe would be about 500 psi.

The pipeline served to aid hydraulic mining in the gold fields. It also provided power for the dredges through a powerhouse located near the intakes. The transmission lines are represented through the dashed line.

The ditch was in operation until 1933, a few more indepth articles on it can be found here, (in addition to the work by T.A. Rickard, referenced above): *The Yukon Ditch was an engineering marvel, Yukon News, 2010 *The Yukon Ditch, Association of Professional Engineers of Yukon

There is a second ditch on the map, that is the one ending above Dawson in a reservoir. This is the Acklen ditch, and was used presumably for drinking water, at least when the Yukon ditch was first constructed.

The railway shown is the narrow gauge Klondike Mines Railway, which operated briefly between Dawson City and Sulphur Springs. It ran from 1905 to 1913 and was about 50 km long.

This type of structure was not unique to Dawson. Fairbanks had the Davidson Ditch, while Nome had the Miocene Ditch

##Code This map uses d3.js v4.

The original purpose in creating this map was to try and show an aqueduct style symbology, but it just felt plain without the other content. I have a bl.ock on both rail symbology and the scale bar with those two elements by themselves.

The aqueduct symbology is rather simple, compared to the rail and should be pretty clear.

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">
<head>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<svg width="960" height="960"></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>
d3.select(self.frameElement).style("height", "960px");
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var projection = d3.geoKavrayskiy7()
.scale(75000)
.rotate([139.10, 0])
.center([0,64.18])
.translate([width / 2, height / 2])
.precision(.1);
var path = d3.geoPath()
.projection(projection);
// Add a few groups for proper layering
var g = svg.append("g");
var g2 = svg.append("g");
var g3 = svg.append("g");
var g4 = svg.append("g");
////////////////////////////////////////////////////////////
// Add the contour map
d3.json("topo.json", function(error, contours) {
var paths = g.selectAll(".contour")
.data(topojson.feature(contours, contours.objects.contours).features)
.enter().append("path")
.attr("d", path)
.attr("class", function(d,i) { return "contour c" + d.properties.gridcode; });
});
////////////////////////////////////////////////////////////
// Add water and streams to the map
d3.json("water.json", function(error, water) {
var paths = g2.selectAll(".water")
.data(topojson.feature(water, water.objects.water).features)
.enter().append("path")
.attr("d", path)
.attr("class", "water");
});
d3.json("streams.json", function(error, water) {
var paths = g2.selectAll(".streams")
.data(topojson.feature(water, water.objects.water).features)
.enter().append("path")
.attr("d", path)
.attr("class", "streams");
});
////////////////////////////////////////////////////////////
// Add the Yukon Ditch and Acklen ditch
d3.json("aqueducts.json", function(error, aqueduct) {
var paths = g3.selectAll(".aqueduct")
.data(topojson.feature(aqueduct, aqueduct.objects.aqueduct).features)
.enter().append("path")
.attr("d", path)
.attr("class", "aqueduct")
.attr("id",function(d,i) { return "aqueduct" + i });
// Add circles along the aqueduct paths
paths.each(function(d,i) {
var currentPath = d3.select("#aqueduct" + i).node();
var totalLength = currentPath.getTotalLength();
var numberOfSymbols = Math.round(totalLength/25);
var spacingOfSymbols = totalLength/numberOfSymbols;
var intialSpacng = spacingOfSymbols/2;
var j = 0;
while (j < numberOfSymbols) {
var p = currentPath.getPointAtLength( (spacingOfSymbols/2) + (spacingOfSymbols * j) );
var point = g3.append("circle")
.attr("cx", p.x)
.attr("cy", p.y)
.attr("r", 3)
.attr("class","aqueduct-symbol");
j++;
}
});
});
////////////////////////////////////////////////////////////
// Add transmission lines
d3.json("power.json", function(error, power) {
var paths = g3.selectAll(".power")
.data(topojson.feature(power, power.objects.power).features)
.enter().append("path")
.attr("d", path)
.attr("class", "power");
});
////////////////////////////////////////////////////////////
// Add the railroad
d3.json("railroad.json", function(error, railroad) {
var paths = g3.selectAll(".railroad")
.data(topojson.feature(railroad, railroad.objects.railroad).features)
.enter().append("path")
.attr("d", path)
.attr("class", "rail")
.attr("id",function(d,i) { return "r" + i });
// Figure out how many cross hatches are needed for each track section
paths.each(function(d,i) {
var currentPath = d3.select("#r" + 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 = g3.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++;
}
});
});
////////////////////////////////////////////////////////////
// Add some labels
d3.csv("points.csv", function ( error, data ) {
var points = g4.selectAll(".point")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) { return projection([d.lon,d.lat])[0]; })
.attr("cy", function(d) { return projection([d.lon,d.lat])[1]; })
.attr("r",5)
.attr("class","point")
.attr("stroke", function(d) { return d.color });
var text = g4.selectAll(".label")
.data(data)
.enter()
.append("text")
.attr("x", function(d) { return projection([d.lon,d.lat])[0] - d.offset; })
.attr("y", function(d) { return projection([d.lon,d.lat])[1] - 2; })
.attr("text-anchor", function(d) { return d.anchor })
.text(function(d) { return " " + d.name })
.attr("class","text")
.attr("font-size",12);
});
////////////////////////////////////////////////////////////
// Regional Labels
g4.append("text")
.attr("x", projection([-139.228,63.97])[0] )
.attr("y", projection([-139.228,63.97])[1] )
.attr("text-anchor","middle")
.attr("class","region-label text")
.text("The Gold Fields");
g4.append("text")
.attr("x", projection([-138.799,64.3966])[0] )
.attr("y", projection([-138.799,64.3966])[1] )
.attr("text-anchor","middle")
.attr("class","region-label text")
.text("Tombstone Range");
////////////////////////////////////////////////////////////
// Add a title
g4.append("text")
.attr("x", 200 )
.attr("y", 100 )
.attr("class","text")
.attr("font-size",24)
.attr("text-decoration","underline")
.text("The Yukon Ditch");
////////////////////////////////////////////////////////////
// Add a scale
var baseWidth = width / 4;
var p1 = projection.invert([width/2 - baseWidth/2, height / 2]);
var p2 = projection.invert([width/2 + baseWidth/2, height / 2]);
var distance = getDistance(p1,p2);
var unit = "m";
var multiply = 1;
var bestFit = 1;
var increment = 0.1;
var scaleDistance = 0;
var scaleWidth = 0;
if ( distance > 1000 ) {
unit = "km"; multiply = 0.001;
}
// Adjust distance to a round(er) number
var i = 0;
while (i < 400) {
var temp = getDistance( projection.invert([ width/2 - (baseWidth / 2) + (increment * i), height / 2 ]), projection.invert([ width/2 + baseWidth/2 - (increment * i), height / 2 ]));
var ratio = temp / temp.toPrecision(1);
// If the second distance is moving away from a cleaner number, reverse direction.
if (i == 1) {
if (Math.abs(1 - ratio) > bestFit) { increment = - increment; }
}
// If we are moving away from a best fit after that, break
else if (i > 2) {
if (Math.abs(1 - ratio) > bestFit) { break }
}
// See if the current distance is the cleanest number
if (Math.abs(1-ratio) < bestFit) {
bestFit = Math.abs(1 - ratio);
scaleDistance = temp;
scaleWidth = (baseWidth) - (2 * increment * i);
}
i++;
}
// Now to build the scale
var margin = {
left: 600, // scale is this far from the left
bottom:200 // scale is this far off the bottom
}
var bars = [];
var smallBars = 10;
var bigBars = 4;
var odd = true;
var label = false;
// Populate an array to represent the bars on the scale
for (i = 0; i < smallBars; i++) {
if (smallBars - 1 > i ) { label = false; } else { label = true; }
bars.push( {width: 1 / (smallBars * (bigBars + 1)), offset: i / (smallBars * (bigBars + 1)), label: label, odd: odd } );
odd = !odd;
}
for (i = 0; i < bigBars; i++) {
bars.push( {width: 1 / (bigBars + 1), offset: (i + 1) / (bigBars + 1), label: true, odd: odd } );
odd = !odd;
}
// Append the scale
g.selectAll(".scaleBar")
.data(bars).enter()
.append("rect")
.attr("x", function(d) { return d.offset * scaleWidth + margin.left })
.attr("y", height - margin.bottom)
.attr("width", function(d) { return d.width * scaleWidth})
.attr("height", 10)
.attr("fill", function (d) { if (d.odd) { return "#ccc"; } else { return "#222"; } });
g.selectAll(".scaleText")
.data(bars).enter()
.filter( function (d) { return d.label == true })
.append("text")
.attr("class","scaleText")
.attr("x",0)
.attr("y",0)
.style("text-anchor","start")
.text(function(d) { return d3.format(",")(((d.offset + d.width) * scaleDistance).toPrecision(2) * multiply); })
.attr("transform", function(d) { return "translate("+ ((d.offset + d.width) * scaleWidth + margin.left )+","+ (height - margin.bottom -5) +") rotate(-45)" });
g.append("text")
.attr("x", scaleWidth/2 + margin.left)
.attr("y", height - margin.bottom + 25)
.text( function() { if(unit == "km") { return "kilometers"; } else { return "metres";} })
.style("text-anchor","middle")
.attr("class","scaleText");
// End Scale -----------------------------------------
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Latitude/longitude spherical geodesy tools (c) Chris Veness 2002-2016 */
/* MIT Licence */
/* www.movable-type.co.uk/scripts/latlong.html */
/* www.movable-type.co.uk/scripts/geodesy/docs/module-latlon-spherical.html */
function getDistance(p1,p2) {
var lat1 = p1[1];
var lat2 = p2[1];
var lon1 = p1[0];
var lon2 = p2[0];
var R = 6371e3; // metres
var φ1 = lat1* Math.PI / 180;
var φ2 = lat2* Math.PI / 180;
var Δφ = (lat2-lat1)* Math.PI / 180;
var Δλ = (lon2-lon1)* Math.PI / 180;
var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ/2) * Math.sin(Δλ/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var distance = R * c;
return distance;
}
</script>
name lat lon color anchor offset
Powerhouse 64.37777 -138.979517 steelblue end 8
Tombstone River Intake 64.42533 -138.7986 steelblue start -8
Little 12 Mile River Intake 64.3695 -138.835 steelblue start -8
Dawson 64.06 -139.4327 black end 8
Moosehide 64.0953 -139.4373 black end 8
Grand Forks 63.9216 -139.3173 black start -8
Reservoir 64.05877 -139.4152 steelblue start -8
Klondike River Crossing 64.0376 -139.237 steelblue start -8
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.
.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;
}
.streams {
fill:none;
stroke: #43a2ca;
}
svg {
display: block;
margin-left: auto;
margin-right: auto;
}
.aqueduct {
stroke: steelblue;
fill: none;
stroke-width: 2px;
}
.aqueduct-symbol {
fill: steelblue;
}
.point {
stroke-width: 2px;
/*stroke: steelblue;*/
fill: white;
}
.rail {
fill:none;
stroke:black;
stroke-width: 1px;
}
.power {
stroke: #333;
stroke-width: 1px;
stroke-dasharray:7, 5, 1, 5;
fill:none;
}
.region-label {
letter-spacing: 10px;
stroke: #111;
fill: #444;
font-size: 25px;
opacity: 0.4;
}
.text {
font-family: Arial;
}
.c400 { fill: #94BF8B; }
.c500 { fill: #A8C68F; }
.c600 { fill: #BDCC96; }
.c700 { fill: #D1D7AB; }
.c800 { fill: #E1E4B5; }
.c900 { fill: #EFEBC0; }
.c1000 { fill: #E8E1B6; }
.c1100 { fill: #DED6A3; }
.c1200 { fill: #D3CA9D; }
.c1300 { fill: #CAB982; }
.c1400 { fill: #C3A76B; }
.c1500 { fill: #B9985A; }
.c1700 { fill: #AA8753; }
.c1900 { fill: #AC9A7C; }
.c2500 { fill: #BAAE9A; }
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