Grid1.java [src/java/util] Revision: default  Date:
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package util;

import csip.api.server.ServiceException;
import java.io.*;
import java.util.Arrays;
import java.util.DoubleSummaryStatistics;
import java.util.Scanner;
import java.util.Set;
import java.util.HashSet;

import static util.Grid1.Type.*;

/**
 * Handles ArcGIS ASCII grids.
 *
 * @author jrf
 */
public class Grid1 {

  public enum Type {
    BYTE_GRID, INT_GRID, FLOAT_GRID
  };

  int rows;
  int cols;
  int nodata;
  double cellsize;
  double xllcorner;
  double yllcorner;

  Type type;

  double[][] ddata;
  int[][] idata;
  byte[][] bdata;


  // reads an existing grid
  public Grid1(Type ty, String filename) throws IOException, ServiceException {
    rows = 0;
    cols = 0;
    int curRow = 0;
    boolean processHeader = true;
    boolean hasnodata = false;
    this.type = ty;
    xllcorner = -9999;
    yllcorner = -9999;
    cellsize = 0;

    if (ty == null)
      throw new IllegalArgumentException("Missing GridType ");

    try (BufferedReader input = new BufferedReader(new FileReader(filename))) {
      String line;
      while ((line = input.readLine()) != null) {
        if (processHeader) {
          Scanner sc = new Scanner(line);
          String name = sc.next();
          if (name.equals("nrows")) {
            rows = sc.nextInt();
          } else if (name.equals("ncols")) {
            cols = sc.nextInt();
          } else if (name.equals("xllcorner")) {
            xllcorner = sc.nextDouble();
          } else if (name.equals("yllcorner")) {
            yllcorner = sc.nextDouble();
          } else if (name.equals("NODATA_value")) {
            nodata = sc.nextInt();
            hasnodata = true;
          } else if (name.equals("cellsize")) {
            cellsize = sc.nextDouble();
          } else {
            throw new ServiceException("Grid1 header unrecognized : " + filename);
          }
          if ((rows > 0) && (cols > 0) && (xllcorner != -9999) && (yllcorner != -9999) && (cellsize > 0) && (hasnodata)) {
            processHeader = false;
            curRow = 0;
            if (ty == INT_GRID)
              idata = new int[rows][cols];
            if (ty == FLOAT_GRID)
              ddata = new double[rows][cols];
            if (ty == BYTE_GRID)
              bdata = new byte[rows][cols];
          }
        } else {
          line = line.trim();
          String[] inData = line.split("\\s+");
          switch (ty) {
            case INT_GRID:
              for (int c = 0; c < cols; c++) {
                idata[curRow][c] = Integer.parseInt(inData[c]);
              }
              break;
            case FLOAT_GRID:
              for (int c = 0; c < cols; c++) {
                ddata[curRow][c] = Double.parseDouble(inData[c]);
              }
              break;
            default:
              for (int c = 0; c < cols; c++) {
                bdata[curRow][c] = Byte.parseByte(inData[c]);
              }
              break;
          }
          curRow++;
        }
      }
    }
  }

  // clones an existing grid

  public Grid1(Grid1 par) {

    this.rows = par.rows;
    this.cols = par.cols;
    this.nodata = par.nodata;
    this.cellsize = par.cellsize;
    this.type = par.type;
    this.xllcorner = par.xllcorner;
    this.yllcorner = par.yllcorner;
    this.type = par.type;

    if (type == null)
      throw new IllegalArgumentException("Missing GridType ");

    switch (type) {
      case INT_GRID:
        idata = new int[rows][cols];
        break;
      case FLOAT_GRID:
        ddata = new double[rows][cols];
        break;
      default:
        bdata = new byte[rows][cols];
        break;
    }
  }

  // clones an existing grid

  public Grid1(Grid1 par, Type ty, int nod) {
    this.rows = par.rows;
    this.cols = par.cols;
    this.nodata = nod;
    this.cellsize = par.cellsize;
    this.type = ty;
    this.xllcorner = par.xllcorner;
    this.yllcorner = par.yllcorner;

    if (ty == null)
      throw new IllegalArgumentException("Missing GridType ");

    switch (ty) {
      case INT_GRID:
        idata = new int[rows][cols];
        for (int i = 0; i < rows; i++) {
          for (int j = 0; j < cols; j++) {
            idata[i][j] = nod;
          }
        }
        break;
      case FLOAT_GRID:
        ddata = new double[rows][cols];
        for (int i = 0; i < rows; i++) {
          for (int j = 0; j < cols; j++) {
            ddata[i][j] = nod;
          }
        }
        break;
      default:
        bdata = new byte[rows][cols];
        for (int i = 0; i < rows; i++) {
          for (int j = 0; j < cols; j++) {
            bdata[i][j] = (byte) nod;
          }
        }
        break;
    }
  }

  // creates a new, empty grid, initialized to zero

  public Grid1(Type ty, int rows, int cols, int nodata, double cellsize, double xllcorner, double yllcorner) {
    this.rows = rows;
    this.cols = cols;
    this.nodata = nodata;
    this.cellsize = cellsize;
    this.type = ty;
    this.xllcorner = xllcorner;
    this.yllcorner = yllcorner;

    if (ty == null)
      throw new IllegalArgumentException("Missing GridType ");

    switch (ty) {
      case INT_GRID:
        idata = new int[rows][cols];
        break;
      case FLOAT_GRID:
        ddata = new double[rows][cols];
        break;
      default:
        bdata = new byte[rows][cols];
        break;
    }
  }


  public void clear() {
    if (rows == 0)
      return;

    if (type == INT_GRID) {
      for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
          idata[i][j] = nodata;
        }
      }
    } else if (type == FLOAT_GRID) {
      for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
          ddata[i][j] = nodata;
        }
      }
    }
  }


  public int[][] getIntData() throws ServiceException {
    if (rows == 0)
      return null;

    if (type == INT_GRID) {
      return idata;
    } else {
      throw new ServiceException("Grid1 type mismatch, int");
    }
  }


  public double[][] getFloatData() throws ServiceException {
    if (rows == 0)
      return null;

    if (type == FLOAT_GRID) {
      return ddata;
    } else {
      throw new ServiceException("Grid1 type mismatch, double");
    }
  }


  public byte[][] getByteData() throws ServiceException {
    if (rows == 0)
      return null;

    if (type == BYTE_GRID) {
      return bdata;
    } else {
      throw new ServiceException("Grid1 type mismatch, byte");
    }
  }


  public int rowCount() {
    return rows;
  }


  public int colCount() {
    return cols;
  }


  public int getNoData() {
    return nodata;
  }


  public double getFloatRowCol(int r, int c) {
    if (ddata != null) {
      if ((r < rows) && (c < cols))
        return ddata[r][c];
    }
    return nodata;
  }


  public int getIntRowCol(int r, int c) {
    if (idata != null) {
      if ((r < rows) && (c < cols))
        return idata[r][c];
    }
    return nodata;
  }


  public void writeWeightedGrid1(Grid1 sedWeight, File f) throws IOException, ServiceException {
    if (f.exists())
      f.delete();

    if (!f.createNewFile())
      throw new ServiceException("Could not save grid file");

    try (PrintStream o = new PrintStream(f)) {
      o.println("ncols " + cols);
      o.println("nrows " + rows);
      o.println("xllcorner " + xllcorner);
      o.println("yllcorner " + yllcorner);
      o.println("cellsize " + cellsize);
      o.println("NODATA_value " + nodata);

      if (type == INT_GRID) {
        for (int r = 0; r < rows; r++) {
          StringBuilder b = new StringBuilder();
          for (int c = 0; c < cols; c++) {
            b.append((idata[r][c]) / sedWeight.idata[r][c]);
            if (c < (cols - 1))
              b.append(" ");
          }
          o.println(b);
        }
      } else if (type == FLOAT_GRID) {
        for (int r = 0; r < rows; r++) {
          StringBuilder b = new StringBuilder();
          for (int c = 0; c < cols; c++) {
            if (ddata[r][c] == nodata) {
              b.append(nodata);
            } else {
              b.append((ddata[r][c]) / sedWeight.ddata[r][c]);
            }
            if (c < (cols - 1))
              b.append(" ");
          }
          o.println(b);
        }
      } else {
        for (int r = 0; r < rows; r++) {
          StringBuilder b = new StringBuilder();
          for (int c = 0; c < cols; c++) {
            b.append((bdata[r][c]) / sedWeight.bdata[r][c]);
            if (c < (cols - 1))
              b.append(" ");
          }
          o.println(b);
        }
      }
    }
  }


  public void print(PrintStream o) throws IOException, ServiceException {
    o.println("ncols " + cols);
    o.println("nrows " + rows);
    o.println("xllcorner " + xllcorner);
    o.println("yllcorner " + yllcorner);
    o.println("cellsize " + cellsize);
    o.println("NODATA_value " + nodata);

    if (type == INT_GRID) {
      for (int r = 0; r < rows; r++) {
        StringBuilder b = new StringBuilder();
        for (int c = 0; c < cols; c++) {
          b.append(idata[r][c]).append(" ");
        }
        o.println(b);
      }
    } else if (type == FLOAT_GRID) {
      for (int r = 0; r < rows; r++) {
        StringBuilder b = new StringBuilder();
        for (int c = 0; c < cols; c++) {
          b.append(ddata[r][c]).append(" ");
        }
        o.println(b);
      }
    } else {
      for (int r = 0; r < rows; r++) {
        StringBuilder b = new StringBuilder();
        for (int c = 0; c < cols; c++) {
          b.append(bdata[r][c]).append(" ");
        }
        o.println(b);
      }
    }
  }


  public void writeGrid1(File f) throws IOException, ServiceException {
    if (f.exists())
      f.delete();

    if (!f.createNewFile())
      throw new ServiceException("Could not save grid file");

    try (PrintStream o = new PrintStream(f)) {
      o.println("ncols " + cols);
      o.println("nrows " + rows);
      o.println("xllcorner " + xllcorner);
      o.println("yllcorner " + yllcorner);
      o.println("cellsize " + cellsize);
      o.println("NODATA_value " + nodata);

      if (type == INT_GRID) {
        for (int r = 0; r < rows; r++) {
          StringBuilder b = new StringBuilder();
          for (int c = 0; c < cols; c++) {
            b.append(idata[r][c]).append(" ");
          }
          o.println(b);
        }
      } else if (type == FLOAT_GRID) {
        for (int r = 0; r < rows; r++) {
          StringBuilder b = new StringBuilder();
          for (int c = 0; c < cols; c++) {
            if (ddata[r][c] == nodata) {
              b.append(nodata);
            } else {
              b.append(ddata[r][c]);
            }
            if (c < (cols - 1))
              b.append(" ");
          }
          o.println(b);
        }
      } else {
        for (int r = 0; r < rows; r++) {
          StringBuilder b = new StringBuilder();
          for (int c = 0; c < cols; c++) {
            b.append(bdata[r][c]).append(" ");
          }
          o.println(b);
        }
      }
    }
  }


  public Set<Integer> intersect(Grid1 gr) throws ServiceException {
    Set<Integer> subs = new HashSet<>();

    int gdata[][] = gr.getIntData();
    int gnodata = gr.getNoData();

    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < cols; j++) {
        if (idata[i][j] != nodata) {
          if (gdata[i][j] != gnodata)
            subs.add(gdata[i][j]);
        }
      }
    }
    return subs;
  }


  // 
  // A template grid that contains the id's that have to match the key passed to this 
  // function. When the key matches the template cell this grids data cell is
  // updated with the val passed. Tpically used to fill in a subcatchment with a 
  // single value.
  public void update(Grid1 template, int key, double val) throws ServiceException {
    int data[][] = template.getIntData();
    for (int i = 0; i < template.rowCount(); i++) {
      for (int j = 0; j < template.colCount(); j++) {
        if (data[i][j] == key)
          ddata[i][j] = val;
      }
    }
  }


  // 
  // A template grid that contains all ids in the correct positions. When one
  // of the vals match the template grid the new value is set in this grid.
  // This is a seletive mask that only copies a portion of the template grid.
  public void maskSubareas(Grid1 template, int vals[], int newVal) throws ServiceException {
    int data[][] = template.getIntData();
    for (int i = 0; i < template.rowCount(); i++) {
      for (int j = 0; j < template.colCount(); j++) {
        for (int k = 0; k < vals.length; k++) {
          if (data[i][j] == vals[k]) {
            idata[i][j] = newVal;
            break;
          }
        }
      }
    }
  }


  public void maskChannels(Grid1 sl, Grid1 sy, Grid1 r, int nod) throws ServiceException {
    double sl_grid[][] = sl.getFloatData();
    double sy_grid[][] = sy.getFloatData();
    double r_grid[][] = r.getFloatData();
    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < cols; j++) {
        if (idata[i][j] != nodata) {
          sl_grid[i][j] = nod;
          sy_grid[i][j] = nod;
          r_grid[i][j] = nod;
        }
      }
    }
  }


  public void updatePathWeightedLoss(double val[], int row[], int col[], double weight) {
    for (int i = 0; i < val.length; i++) {
      int x = row[i];
      int y = col[i];
      if (ddata[x][y] <= -999)
        ddata[x][y] = 0;

      ddata[x][y] += (val[i] * weight);
    }
  }


  public void updatePathWeight(int row[], int col[], double weight) {
    for (int i = 0; i < row.length; i++) {
      int x = row[i];
      int y = col[i];
      ddata[x][y] += weight;
    }
  }


  public void updatePathMaxLoss(double val[], int row[], int col[]) {
    for (int i = 0; i < val.length; i++) {
      int x = row[i];
      int y = col[i];
      if (val[i] > ddata[x][y])
        ddata[x][y] = val[i];
    }
  }


  public double getCellsize() {
    return cellsize;
  }


  public int cellCountWithMask(int key, Grid1 mask) throws ServiceException {
    int data[][] = mask.getIntData();
    boolean lookingForStart = true;
    boolean sawVal;
    int count = 0;

    for (int i = 0; i < rows; i++) {
      sawVal = false;
      for (int j = 0; j < cols; j++) {
        if (idata[i][j] == key) {
          sawVal = true;
          lookingForStart = false;
          if (data[i][j] == 1)
            count++;
        }
      }
      if ((lookingForStart == false) && (sawVal == false))
        break;
    }
    return count;
  }


  public int majorValue(int key, Grid1 subGrid1) throws ServiceException {
    int data[][] = subGrid1.getIntData();
    boolean lookingForStart = true;
    boolean sawVal;
    int counts[] = new int[WeppConstants.MAX_IDS];

    for (int i = 0; i < WeppConstants.MAX_IDS; i++) {
      counts[i] = 0;
    }
    for (int i = 0; i < subGrid1.rowCount(); i++) {
      sawVal = false;
      for (int j = 0; j < subGrid1.colCount(); j++) {
        if (data[i][j] == key) {
          sawVal = true;
          lookingForStart = false;
          if (idata[i][j] < WeppConstants.MAX_IDS) {
              if (idata[i][j] >= 0) {
                counts[idata[i][j]]++;
            } else {
                // there is really no data here in the grid.
            }
          } else {
                throw new ServiceException("Grid1:majorValue contains more that MAX number of ID's");
          }
        }
      }
      if ((lookingForStart == false) && (sawVal == false))
        break;
    }

    // not found
    if (lookingForStart)
      return -999;

    int max = 0;
    int majorId = 0;
    for (int i = 0; i < WeppConstants.MAX_IDS; i++) {
      if (counts[i] > max) {
        max = counts[i];
        majorId = i;
      }
    }
    return majorId;
  }


  public boolean verifyFloat(double min, double max) {
    boolean rc = true;
    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < cols; j++) {
        if (ddata[i][j] != nodata) {
          if ((ddata[i][j] < min) || (ddata[i][j] > max)) {
            // outside range
            return false;
          }
        }
      }
    }
    return rc;
  }


  public long cellCount(int key) throws ServiceException {
    return Arrays
        .stream(idata)
        .flatMapToInt(r -> Arrays.stream(r))
        .filter(v -> v == key)
        .summaryStatistics().getCount();
  }


  DoubleSummaryStatistics getStats() {
    return Arrays
        .stream(ddata)
        .flatMapToDouble(r -> Arrays.stream(r))
        .filter(v -> v != nodata)
        .summaryStatistics();
  }


  public static void main(String[] args) throws Exception {
    String f = "/od/projects/WEPPWS-Python/WS/flowpathloss.asc";
    Grid1 g = new Grid1(FLOAT_GRID, f);
    g.print(System.out);
    System.out.println(g.getStats());
  }

}