rveciana - label-positioning-with-svg-path-properties

Label positioning with svg-path-properties

Open raw page in new tab

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>