291 lines
11 KiB
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();
|
|
}*/
|
|
}
|
|
|
|
}
|