rveciana - representing-geotiff-values-with-d3js

Representing GeoTIFF values with D3js

Open raw page in new tab

This example shows how to represent a GeoTIFF raster directly into a Canvas object.

Here you have a question about how to set pixels: http://stackoverflow.com/questions/4899799/whats-the-best-way-to-set-a-single-pixel-in-an-html5-canvas the way used in the example draws all the pixels at once, since is much more efficient.

Note that using the Math.round function, the example will work if the original GeoTIFF has more pixels than the output image. Also, drawing small rectangles would be less efficient.

The color scales from D3js aren’t used, since the performance is very poor. It’s still a problem, since the ColorBrewer scales are much better than this, but difficult to calculate.

<!DOCTYPE html>
<meta charset="utf-8">
<style>

</style>
<body>

<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="geotiff.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script>
var width = 680,
    height = 500,
    barbSize = 40;

var projection = d3.geoAzimuthalEqualArea()
    .rotate([-55.5, -24])
    .scale(1100);

var intervalsTemp = [14,17,20,23,26,29,35,38];

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) {
  var countries = topojson.feature(topojsonData, topojsonData.objects.countries);
  var path = d3.geoPath()
      .projection(projection).context(context);

  context.beginPath();
  context.strokeStyle = "#777";
  context.fillStyle = "#aaa";
  path(countries);
  context.fill();
  context.stroke();

  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 invGeoTransform = [-geoTransform[0]/geoTransform[1], 1/geoTransform[1],0,-geoTransform[3]/geoTransform[5],0,1/geoTransform[5]];

  var tempData = new Array(image.getHeight());
  for (var j = 0; j<image.getHeight(); j++){
      tempData[j] = new Array(image.getWidth());
      for (var i = 0; i<image.getWidth(); i++){
          tempData[j][i] = rasters[1][i + j*image.getWidth()];
      }
  }


  var canvasRaster = d3.select("body").append("canvas")
      .attr("width", width)
      .attr("height", height)
      .style("display","none");

  var contextRaster = canvasRaster.node().getContext("2d");
  var id = contextRaster.createImageData(width,height);
  var data = id.data;
  var pos = 0;

  for(var j = 0; j<height; j++){
    for(var i = 0; i<width; i++){
      var pointCoords = projection.invert([i,j]);
      var px = Math.round(invGeoTransform[0] + pointCoords[0]* invGeoTransform[1]);
      var py = Math.round(invGeoTransform[3] + pointCoords[1] * invGeoTransform[5]);

      if(px >= 0 && px < image.getWidth() && py >= 0 && py < image.getHeight()){

        var v = (tempData[py][px]- 14)/24;
        data[pos]   = 255 * (v<0.5?2*v:1);
        data[pos+1]   = 255 * (v<0.5?2*v:1-2*(v-0.5));
        data[pos+2]   = 255 * (v<0.5?1:1-2*(v-0.5));
        data[pos+3]   = 180;
        pos = pos + 4

      }
    }
  }

  contextRaster.putImageData( id, 0, 0);
  context.drawImage(canvasRaster.node(), 0, 0);
});
});
</script>

</body>