This example shows how to use svg-path-properties and d3-marching-squares to draw isolines on a map and label them each certain number of pixels.
There are some issues left, such as avoiding label overlapping.
Read this other block for explanations about the used data and the isolines.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="path-properties.min.js"></script>
<script src="geotiff.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="d3-marching-squares.min.js"></script>
<script>
var width = 680,
height = 500;
var projection = d3.geoAzimuthalEqualArea()
.rotate([-55.5, -24])
.scale(1100);
var canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height);
var context = canvas.node().getContext("2d");
d3.request("tz850.tiff")
.responseType('arraybuffer')
.get(function(error, tiffData){
d3.json("world-110m.json", function(error, topojsonData) {
//Drawing the base map
var countries = topojson.feature(topojsonData, topojsonData.objects.countries);
context.beginPath();
context.strokeStyle = "#777";
context.fillStyle = "#ccc";
var path = d3.geoPath()
.projection(projection).context(context);
path(countries);
context.fill();
context.stroke();
//Reading the GeoTIFF
var tiff = GeoTIFF.parse(tiffData.response);
var image = tiff.getImage();
var rasters = image.readRasters();
var tiepoint = image.getTiePoints()[0];
var pixelScale = image.getFileDirectory().ModelPixelScale;
var geoTransform = [tiepoint.x, pixelScale[0], 0, tiepoint.y, 0, -1*pixelScale[1]];
var zData = new Array(image.getHeight());
var tempData = new Array(image.getHeight());
for (var j = 0; j<image.getHeight(); j++){
zData[j] = new Array(image.getWidth());
tempData[j] = new Array(image.getWidth());
for (var i = 0; i<image.getWidth(); i++){
zData[j][i] = rasters[0][i + j*image.getWidth()];
tempData[j][i] = rasters[1][i + j*image.getWidth()];
}
}
//Calculating isolines
var intervalsZ = [1400, 1420, 1440, 1460, 1480, 1500, 1520, 1540];
var linesZ = rastertools.isolines(zData, geoTransform, intervalsZ);
//Creating the isolines in a separate hidden canvas
var hiddenCanvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height)
.attr("id", "hiddenCanvas")
.style("display","none");
var hiddenContext = hiddenCanvas.node().getContext("2d");
path = d3.geoPath()
.projection(projection);
var path2 = d3.geoPath()
.projection(projection).context(hiddenContext);
linesZ.features.forEach(function(d, i) {
var properties = spp.svgPathProperties(path(d));
var separation = 150;
hiddenContext.beginPath();
hiddenContext.strokeStyle = "#000";
hiddenContext.lineWidth = 2;
path2(d);
hiddenContext.stroke();
//Drawing labels
for(var j = 0; j< Math.floor(properties.getTotalLength()/separation); j++){
var pos = properties.getPropertiesAtLength(75 + separation*j);
var degrees = Math.atan(pos.tangentY/pos.tangentX);
var text = d.properties[0].value;
hiddenContext.save();
hiddenContext.translate(pos.x, pos.y);
hiddenContext.rotate(degrees);
hiddenContext.font="15px Georgia";
hiddenContext.clearRect(-2-hiddenContext.measureText(text).width/2 , -8, 4 + hiddenContext.measureText(text).width, 19);
hiddenContext.fillStyle = "#500";
hiddenContext.fillText(text, -hiddenContext.measureText(text).width/2, 7.5);
hiddenContext.restore();
}
});
//Merging the hidden canvas
context.drawImage(hiddenCanvas.node(), 0, 0, width, height);
});
});
</script>