Search code examples
octavegeotiff

Octave: Box and whisker plot without spread from GeoTIFF


---- Update with what I got so far and what's left to resolve can be found in point 3 below ----

Using Octave I want to create 30 horizontal box and whisker plots without spread (x-axis) from 30 different GeoTIFF's. This is a sketch of how I would like the plot to look like: enter image description here

Ideally the best solution for me would be an Octave code (workflow) that would allow me to place multiple GeoTIFFs in one directory and then with one click create a box and whisker plot for all GeotIFFs at once - just like the sketch above.

A GeoTIFF-sample with 3 GeoTIFF's can be downloaded here. The file looks like this in QGIS: enter image description here

It holds elevation values on band 1 (the ones that each box and whisker plot should be based on, and no data values (-999), the no-data values should be excluded from the plot.

Right now this is what I got:

  1. Using img = imread ("filname.tif") gets the file into Octave. Using hist (img(:), 200); shows that all cells are concentrated around 65300. imagesc (img, [65100 65600]) follwed by colorbar displays the image extent but's it's clear that this way simply doesn't import the real cell values. I can't find a working solution to import GeoTIFF's with cell values, therefor my current work-around is exporting the GeoTIFF from QGIS with gdal_translate -of aaigrid which creates a .asc-file that I manually edit to remove header rows, rename to .csv and load into Octave. That .csv can be found here.
  2. To load it and create a box plot I'm currently using this code (thanks to @Andy and @Cris Luengo):

    pkg load statistics
    
    s = urlread ("https://drive.google.com/uc?export=download&id=1RzJ-EO0OXgfMmMRG8wiCBz-51RcwSM5h");
    o = str2double (strsplit (s, ";"));
    o(isnan (o)) = [];
    
    boxplot (o)
    set(gca,"xtick",[])
    view([-90 90])
    
    print out.png
    
  3. The results is pretty close but I'm still failing to: A) load GeoTIFF's directly from a folder. If this is not possible I'm gonna have to modify the code to load all *.csv in a directory to the same box plot and label each plot by filename (which I'm unsure how to accomplish. B) to get the x-axis reversed (going from 200-450, not the other way around). This is caused by the view([-90 90]) that I use to make the box plot horizontal instead of vertical which is needed for layout reasons. enter image description here

Anyone with any ideas on how to resolve the last adjustments?

---- Background info ----

I have 30 GeoTIFFs containing results from a viewshed analysis, for every 2x2 meter square there is a value the tells me how high a building can be (in meters) before it's visible from the viewshed point. The results cover the whole city of Stockholm but the above mentioned 30 GeoTIFFs are smaller clips of an area where new development is planned. The results help planners to understand how new development might effect each of the 30 places (that are important for cultural heritage management).

As part of a bigger PDF-report (where these results are visualized with different maps in different scales) I'm trying to produce a box and whisker plot (as a compliment to the maps) the gives the reader an overview over how much space is there is left at the planned development area, based on each of the 30 viewshed (GeoTIFF) results (one box and whisker for each of the 30 locations). Below is an example of how a map in the report can look like: enter image description here


Solution

  • Does not directly read GeoTIFF but calls gdal_translate under the hood. Just place all your .tif in the same directory. Make sure gdal_translate is in your PATH:

    pkg load statistics
    
    clear all;
    fns = glob ("*.tif");
    for k=1:numel (fns)
    
      ofn = tmpnam;
      cmd = sprintf ('gdal_translate -of aaigrid "%s" "%s"', fns{k}, ofn);
      [s, out] = system (cmd);
      if (s != 0)
        error ('calling gdal_translate failed with "%s"', out);
      endif
      fid = fopen (ofn, "r");
      # read 6 headerlines
      hdr = [];
      for i=1:6
        s = strsplit (fgetl (fid), " ");
        hdr.(s{1}) = str2double (s{2});
      endfor
      d = dlmread (fid);
    
      # check size against header
      assert (size (d), [hdr.nrows hdr.ncols])
    
      # set nodata to NA
      d (d == hdr.NODATA_value) = NA;
    
      raw{k} = d;
    
      # create copy with existing values
      raw_v{k} = d(! isna (d));
    
      fclose (fid);
    
    endfor
    
    ## generate plot
    boxplot (raw_v)
    set (gca, "xtick", 1:numel(fns),
              "xticklabel", strrep (fns, ".tif", ""));
    view ([-90 90])
    zoom (0.95)
    
    print ("out.png")
    

    gives

    boxplot from three tif