ecological-engine-geospatia.../src/main/java/org/gcube/dataanalysis/geo/vti/vesselsprocessing/VTIBathymetry.java

291 lines
11 KiB
Java

package org.gcube.dataanalysis.geo.vti.vesselsprocessing;
import ucar.ma2.Array;
import ucar.ma2.InvalidRangeException;
/**
* Class for obtaining altimetry and bathymetry values from a netCDF file.
* Requires the netCDF libraries (see here: http://www.unidata.ucar.edu/software/netcdf-java/documentation.htm)
*
* You should use the class in the following way:
* <code><pre>
* VTIBathymetry bath = new VTIBathymetry("/path/to/bath.nc"); // e.g. gebco_08.nc
* bath.open(); // file must be explicitly opened, this takes a short time, avoid opening it several times
* short value1 = bath.getZ(142.2, 11.35); // e.g. Mariana Trench
* short value2 = bath.getZ(0, 0);
* // ...
* bath.close(); // file should be closed when not needed any longer
* </pre></code>
*
* @author Frank Loeschau, Terradue Srl.
*/
public class VTIBathymetry {
private String filename;
private String mode;
private java.awt.geom.Point2D.Double[] points;
private ucar.nc2.NetcdfFile ncFile;
private ucar.nc2.Variable var;
//private ucar.ma2.Array arr;
private boolean debug = false;
public final int GRID_COLS = 360 * 120;
public final int GRID_ROWS = 180 * 120;
/**
* The class can also be used as command-line tool.
* The syntax is: <p><code>java org.d4science2.vtivre.VTIBathymetry get x1,y1 [x2,y2 [...]] filename</code></p>
* If only one pair of coordinates is specified, debug information about the calculation is written.
* @param args the command-line arguments
*/
public static void main(String[] args) {
int status = 0;
VTIBathymetry bm = new VTIBathymetry();
try {
if (!bm.parseArguments(args)) System.exit(1);
bm.open();
short[][] res = new short[361][181];
if (bm.mode.compareTo("draw") == 0) {
bm.draw();
} else if (bm.mode.compareTo("get") == 0) {
bm.debug = false;
if (bm.points == null || bm.points.length == 0) {
System.err.println("No point specified");
System.exit(1);
} else if (bm.points.length == 1) {
bm.debug = true;
short z = bm.getZ(bm.points[0]);
System.out.println(z);
} else {
short[] zs = bm.getZ(bm.points);
for (int i = 0; i < zs.length; i++) System.out.println(zs[i]);
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("ERROR: " + e.getMessage());
status = 1;
} finally {
try {
bm.close();
} catch (Exception e2) {}
}
System.exit(status);
}
private static void printUsage() {
System.out.println("Usage: org.d4science2.vtivre.VTIBathymetry (get x,y [x2,y2 [x3,y3 ...]]) file");
System.out.println();
System.exit(1);
}
private VTIBathymetry() {}
private boolean parseArguments(String[] args) throws Exception {
try {
if (args.length >= 1) mode = args[0];
if (args.length >= 2) filename = args[args.length - 1];
points = new java.awt.geom.Point2D.Double[args.length > 2 ? args.length - 2 : 0];
for (byte i = 0; i < args.length - 2; i++) {
String[] s = args[i + 1].split(",");
if (s.length != 2) {
System.err.println("Invalid coordinate format: must be x,y (e.g. -12.34,56.78)");
System.exit(1);
}
points[i] = new java.awt.geom.Point2D.Double();
points[i].setLocation(Double.parseDouble(s[0]), Double.parseDouble(s[1]));
}
} catch (Exception e) {
printUsage();
return false;
}
if (mode == null) {
System.err.println("No mode specified");
printUsage();
return false;
} else if (filename == null) {
System.err.println("No filename specified");
return false;
}
return true;
}
/**
* Creates an instance of VTIBathymetry with the specified file.
* @param filename the netCDF filename containing the altimetry/bathymetry data
*/
public VTIBathymetry(String filename) {
this.filename = filename;
}
/**
* Opens the netCDF file and loads the data array.
* This operation is somewhat slow and requires the creation of a big object in memory. Ideally, this method is called only once, before the altrimetry/bathymetry values are needed.
* @throws java.io.IOException
*/
public void open() throws java.io.IOException {
ncFile = ucar.nc2.dataset.NetcdfDataset.openFile(filename, null);
var = ncFile.findVariable("z");
//arr = var.read();
}
/**
* Closes the netCDF file.
* @throws java.io.IOException
*/
public void close() throws java.io.IOException {
if (ncFile != null) ncFile.close();
}
/**
* Returns the altimetric/bathymetric value of the given geographical coordinates.
* The value derives from the corresponding cell in the coordinate grid (120 x 120 cells per degree).
* If a coordinate is close to the cell border (< 0.1 * arc cell size), the result value is averaged with the value from the adjacent cell.
* @param point the geographical coordinates (x = longitude value (-180 to 180), y = latitude value (-90 to 90))
* @return the altimetric/bathymetric value for the coordinate (in meters)
* @throws java.io.IOException if the netCDF variable <code>z</code> could not been read
*/
public short getZ(java.awt.geom.Point2D.Double point) throws java.io.IOException {
return getZ(point.getX(), point.getY());
}
/**
* Returns the altimetric/bathymetric values of the given array of geographical coordinates.
* The value derives from the corresponding cell in the coordinate grid (120 x 120 cells per degree).
* If a coordinate is close to the cell border (< 0.1 * arc cell size), the result value is averaged with the value from the adjacent cell.
* @param points array of geographical coordinates (x = longitude value (-180 to 180), y = latitude value (-90 to 90))
* @return an array of the same shape as points, containing the altimetric/bathymetric value for the coordinate (in meters)
* @throws java.io.IOException if the netCDF variable <code>z</code> could not been read
*/
public short[] getZ(java.awt.geom.Point2D.Double[] points) throws java.io.IOException {
short[] result = new short[points.length];
for (int i = 0; i < points.length; i++) result[i] = getZ(points[i].getX(), points[i].getY());
return result;
}
/**
* Returns the altimetric/bathymetric value of the given geographical coordinates.
* The value derives from the corresponding cell in the coordinate grid (120 x 120 cells per degree).
* If a coordinate is close to the cell border (< 0.1 * arc cell size), the result value is averaged with the value from the adjacent cell.
* @param x longitude coordinate (-180 to 180)
* @param y latitude coordinate (-90 to 90)
* @return the altimetric/bathymetric value for the coordinate (in meters)
* @throws NullPointerException if the netCDF file has not been opened or the variable <code>z</code> (containing the altimetry/bathymetry values) is not found
* @throws java.io.IOException if the netCDF variable <code>z</code> could not been read
*/
public short getZ(double x, double y) throws java.lang.NullPointerException, java.io.IOException {
int resultInt = 0;
if (ncFile == null) throw new NullPointerException("No netCDF file not open");
if (var == null) throw new NullPointerException("Variable 'z' not found");
double gridColD = (x + 180) * 120; // left border of cell
boolean averageWithNextCol = false, averageWithNextRow = false;
int gridCol, gridRow;
if (gridColD < 0 || gridColD >= GRID_COLS) {
gridCol = GRID_COLS - 1;
averageWithNextCol = true;
} else {
gridCol = (int)Math.floor(gridColD);
if (gridColD - gridCol < 0.1) {
gridCol = (gridCol + GRID_COLS - 1) % GRID_COLS;
averageWithNextCol = true;
} else if (gridColD - gridCol > 0.9) {
averageWithNextCol = true;
}
}
double gridRowD = (90 - y) * 120; // upper border of cell
if (gridRowD < 0) {
gridRow = 0;
} else if (gridRowD >= GRID_ROWS) {
gridRow = GRID_ROWS - 1;
} else {
gridRow = (int)Math.floor(gridRowD);
if (gridRowD - gridRow < 0.1 && gridRow > 0) {
gridRow--;
averageWithNextRow = true;
} else if (gridRowD - gridRow > 0.9 && gridRow < GRID_ROWS - 1) {
averageWithNextRow = true;
}
}
int[] shape = new int[1];
if (averageWithNextCol) shape[0] = 2;
else shape[0] = 1;
int[] origin = {GRID_COLS * gridRow + gridCol};
short result = 0;
try {
Array arr = var.read(origin, shape);
resultInt = arr.getShort(0);
if (averageWithNextCol) resultInt += arr.getShort(1);
if (averageWithNextRow) {
origin[0] += GRID_COLS;
arr = var.read(origin, shape);
resultInt += arr.getShort(0);
if (averageWithNextCol) resultInt += arr.getShort(1);
}
result = (short)(resultInt / ((averageWithNextCol ? 2 : 1) * (averageWithNextRow ? 2 : 1)));
} catch (InvalidRangeException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (debug) {
System.out.println("x,y --- col,row (calculated) --- col[+],row[+] (actual): " + x + "," + y + " --- " + gridColD + "," + gridRowD + " --- " + gridCol + (averageWithNextCol ? "+" : "") + "," + gridRow + (averageWithNextRow ? "+" : ""));
System.out.print("Grid cell indexes used: " + (GRID_COLS * gridRow + gridCol));
if (averageWithNextCol) System.out.print(" " + (GRID_COLS * gridRow + (gridCol + 1) % GRID_COLS));
if (averageWithNextRow) {
System.out.print(" " + (GRID_COLS * (gridRow + 1) + gridCol));
if (averageWithNextCol) System.out.print(" " + (GRID_COLS * (gridRow + 1) + (gridCol + 1) % GRID_COLS));
}
if (averageWithNextCol) System.out.print(", eastern adjacent cell used");
if (averageWithNextRow) System.out.print(", southern adjacent cell used");
System.out.println();
/*System.out.print("Corresponding altimetry/bathymetry values: " + arr.getShort(GRID_COLS * gridRow + gridCol));
if (averageWithNextCol) System.out.print(" " + arr.getShort(GRID_COLS * gridRow + (gridCol + 1) % GRID_COLS));
if (averageWithNextRow) {
System.out.print(" " + arr.getShort(GRID_COLS * (gridRow + 1) + gridCol));
if (averageWithNextCol) System.out.print(" " + arr.getShort(GRID_COLS * (gridRow + 1) + (gridCol + 1) % GRID_COLS));
}*/
System.out.println();
System.out.println("Result: " + result);
}
return result;
}
private void draw() {
/* try {
if (arr == null) throw new Exception("Variable 'z' not found");
long size = arr.getSize();
for (int i = 0; i < 30; i++) {
for (int j = 0; j < 120; j++) {
int index = 31104000 * i + 360 * j + 15552000 + 180;
if (index >= size) return;
System.out.print(arr.getShort(index) < 0 ? "~" : "@");
}
System.out.println();
}
} catch (Exception e) {
System.err.println("ERROR: " + e.getClass().getName() + ": " + e.getMessage());
e.printStackTrace();
}*/
}
}