
ucar.nc2.dataset.CoordinateAxis2D Maven / Gradle / Ivy
/*
* Copyright 1998-2014 University Corporation for Atmospheric Research/Unidata
*
* Portions of this software were developed by the Unidata Program at the
* University Corporation for Atmospheric Research.
*
* Access and use of this software shall impose the following obligations
* and understandings on the user. The user is granted the right, without
* any fee or cost, to use, copy, modify, alter, enhance and distribute
* this software, and any derivative works thereof, and its supporting
* documentation for any purpose whatsoever, provided that this entire
* notice appears in all copies of the software, derivative works and
* supporting documentation. Further, UCAR requests that the user credit
* UCAR/Unidata in any publications that result from the use of this
* software or in any product that includes this software. The names UCAR
* and/or Unidata, however, may not be used in any advertising or publicity
* to endorse or promote any products or commercial entity unless specific
* written permission is obtained from UCAR/Unidata. The user also
* understands that UCAR/Unidata is not obligated to provide the user with
* any support, consulting, training or assistance of any kind with regard
* to the use, operation and performance of this software nor to provide
* the user with any updates, revisions, new versions or "bug fixes."
*
* THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ucar.nc2.dataset;
import ucar.ma2.*;
import ucar.nc2.Attribute;
import ucar.nc2.Variable;
import ucar.nc2.constants.AxisType;
import ucar.nc2.constants.CF;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* A 2-dimensional numeric Coordinate Axis. Must be invertible meaning, roughly, that
* if you draw lines connecting the points, none would cross.
*
* @see CoordinateAxis#factory
* @author john caron
*/
public class CoordinateAxis2D extends CoordinateAxis {
static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CoordinateAxis2D.class);
static private final boolean debug = false;
private ArrayDouble.D2 coords = null; // LOOK maybe optional for large arrays, or maybe eliminate all together, and read each time ??
/**
* Create a 2D coordinate axis from an existing VariableDS
*
* @param ncd the containing dataset
* @param vds create it from here
*/
public CoordinateAxis2D(NetcdfDataset ncd, VariableDS vds) {
super(ncd, vds);
}
// for section and slice
@Override
protected Variable copy() {
return new CoordinateAxis2D(this.ncd, this);
}
/**
* Get the coordinate value at the i, j index.
*
* @param i index 0 (fastest varying, right-most)
* @param j index 1
* @return midpoint.get(j, i).
*/
public double getCoordValue(int j, int i) {
if (coords == null) doRead();
return coords.get(j, i);
}
private void doRead() {
Array data;
try {
data = read();
// if (!hasCachedData()) setCachedData(data, false); //cache data for subsequent reading
} catch (IOException ioe) {
log.error("Error reading coordinate values " + ioe);
throw new IllegalStateException(ioe);
}
if (data.getRank() != 2)
throw new IllegalArgumentException("must be 2D");
if (debug)
System.out.printf("Coordinate2D read%n");
coords = (ArrayDouble.D2) Array.factory(double.class, data.getShape(), data.get1DJavaArray(double.class));
if (this.axisType == AxisType.Lon)
makeConnectedLon(coords);
}
private boolean isInterval;
private boolean intervalWasComputed;
public boolean isInterval() {
if (!intervalWasComputed)
isInterval = computeIsInterval();
return isInterval;
}
private void makeConnectedLon(ArrayDouble.D2 mid) {
int[] shape = mid.getShape();
int ny = shape[0];
int nx = shape[1];
// first row
double connect = mid.get(0, 0);
for (int i = 1; i < nx; i++) {
connect = connectLon(connect, mid.get(0, i));
mid.set(0, i, connect);
}
// other rows
for (int j = 1; j < ny; j++) {
connect = mid.get(j - 1, 0);
for (int i = 0; i < nx; i++) {
connect = connectLon(connect, mid.get(j, i));
mid.set(j, i, connect);
}
}
}
private static final double MAX_JUMP = 100.0; // larger than you would ever expect
static private double connectLon(double connect, double val) {
if (Double.isNaN(connect)) return val;
if (Double.isNaN(val)) return val;
double diff = val - connect;
if (Math.abs(diff) < MAX_JUMP) return val; // common case fast
// we have to add or subtract 360
double result = diff > 0 ? val - 360 : val + 360;
double diff2 = connect - result;
if ((Math.abs(diff2)) < Math.abs(diff))
val = result;
return val;
}
/**
* Get the coordinate values as a 1D double array, in canonical order.
*
* @return coordinate values
* @throws UnsupportedOperationException if !isNumeric()
*/
public double[] getCoordValues() {
if (coords == null) doRead();
if (!isNumeric())
throw new UnsupportedOperationException("CoordinateAxis2D.getCoordValues() on non-numeric");
return (double[]) coords.get1DJavaArray(double.class);
}
/**
* Create a new CoordinateAxis2D as a section of this CoordinateAxis2D.
*
* @param r1 the section on the first index
* @param r2 the section on the second index
* @return a section of this CoordinateAxis2D
* @throws InvalidRangeException if specified Ranges are invalid
*/
public CoordinateAxis2D section(Range r1, Range r2) throws InvalidRangeException {
List section = new ArrayList<>();
section.add(r1);
section.add(r2);
return (CoordinateAxis2D) section(section);
}
public ArrayDouble.D2 getCoordValuesArray() {
if (coords == null) doRead();
return coords;
}
/**
* Only call if isInterval()
*
* @return bounds array pr null if not an interval
*/
public ArrayDouble.D3 getCoordBoundsArray() {
if (coords == null) doRead();
return makeBoundsFromAux();
}
/**
* @deprecated use getCoordValuesArray
*/
public ArrayDouble.D2 getMidpoints() {
return getCoordValuesArray();
}
public ArrayDouble.D2 getXEdges() {
ArrayDouble.D2 mids = getCoordValuesArray();
return makeXEdges(mids);
}
public ArrayDouble.D2 getYEdges() {
ArrayDouble.D2 mids = getCoordValuesArray();
return makeYEdges(mids);
}
/**
* Normal case: do something reasonable in deciding on the edges when we have the midpoints of a 2D coordinate.
*
* @param midx x coordinates of midpoints
* @return x coordinates of edges with shape (ny+1, nx+1)
*/
static public ArrayDouble.D2 makeXEdges(ArrayDouble.D2 midx) {
int[] shape = midx.getShape();
int ny = shape[0];
int nx = shape[1];
ArrayDouble.D2 edgex = new ArrayDouble.D2(ny + 1, nx + 1);
for (int y = 0; y < ny - 1; y++) {
for (int x = 0; x < nx - 1; x++) {
// the interior edges are the average of the 4 surrounding midpoints
double xval = (midx.get(y, x) + midx.get(y, x + 1) + midx.get(y + 1, x) + midx.get(y + 1, x + 1)) / 4;
edgex.set(y + 1, x + 1, xval);
}
// extrapolate to exterior points
edgex.set(y + 1, 0, edgex.get(y + 1, 1) - (edgex.get(y + 1, 2) - edgex.get(y + 1, 1)));
edgex.set(y + 1, nx, edgex.get(y + 1, nx - 1) + (edgex.get(y + 1, nx - 1) - edgex.get(y + 1, nx - 2)));
}
// extrapolate to the first and last row
for (int x = 0; x < nx + 1; x++) {
edgex.set(0, x, edgex.get(1, x) - (edgex.get(2, x) - edgex.get(1, x)));
edgex.set(ny, x, edgex.get(ny - 1, x) + (edgex.get(ny - 1, x) - edgex.get(ny - 2, x)));
}
return edgex;
}
/**
* Normal case: do something reasonable in deciding on the edges when we have the midpoints of a 2D coordinate.
*
* @param midy y coordinates of midpoints
* @return y coordinates of edges with shape (ny+1, nx+1)
*/
static public ArrayDouble.D2 makeYEdges(ArrayDouble.D2 midy) {
int[] shape = midy.getShape();
int ny = shape[0];
int nx = shape[1];
ArrayDouble.D2 edgey = new ArrayDouble.D2(ny + 1, nx + 1);
for (int y = 0; y < ny - 1; y++) {
for (int x = 0; x < nx - 1; x++) {
// the interior edges are the average of the 4 surrounding midpoints
double xval = (midy.get(y, x) + midy.get(y, x + 1) + midy.get(y + 1, x) + midy.get(y + 1, x + 1)) / 4;
edgey.set(y + 1, x + 1, xval);
}
// extrapolate to exterior points
edgey.set(y + 1, 0, edgey.get(y + 1, 1) - (edgey.get(y + 1, 2) - edgey.get(y + 1, 1)));
edgey.set(y + 1, nx, edgey.get(y + 1, nx - 1) + (edgey.get(y + 1, nx - 1) - edgey.get(y + 1, nx - 2)));
}
// extrapolate to the first and last row
for (int x = 0; x < nx + 1; x++) {
edgey.set(0, x, edgey.get(1, x) - (edgey.get(2, x) - edgey.get(1, x)));
edgey.set(ny, x, edgey.get(ny - 1, x) + (edgey.get(ny - 1, x) - edgey.get(ny - 2, x)));
}
return edgey;
}
/**
* Experimental: for WRF rotated (NMM "E") Grids
*
* @param midx x coordinates of midpoints
* @return x coordinates of edges with shape (ny+2, nx+1)
*/
static public ArrayDouble.D2 makeXEdgesRotated(ArrayDouble.D2 midx) {
int[] shape = midx.getShape();
int ny = shape[0];
int nx = shape[1];
ArrayDouble.D2 edgex = new ArrayDouble.D2(ny + 2, nx + 1);
// compute the interior rows
for (int y = 0; y < ny; y++) {
for (int x = 1; x < nx; x++) {
double xval = (midx.get(y, x - 1) + midx.get(y, x)) / 2;
edgex.set(y + 1, x, xval);
}
edgex.set(y + 1, 0, midx.get(y, 0) - (edgex.get(y + 1, 1) - midx.get(y, 0)));
edgex.set(y + 1, nx, midx.get(y, nx - 1) - (edgex.get(y + 1, nx - 1) - midx.get(y, nx - 1)));
}
// compute the first row
for (int x = 0; x < nx; x++) {
edgex.set(0, x, midx.get(0, x));
}
// compute the last row
for (int x = 0; x < nx - 1; x++) {
edgex.set(ny + 1, x, midx.get(ny - 1, x));
}
return edgex;
}
/**
* Experimental: for WRF rotated (NMM "E") Grids
*
* @param midy y coordinates of midpoints
* @return y coordinates of edges with shape (ny+2, nx+1)
*/
static public ArrayDouble.D2 makeYEdgesRotated(ArrayDouble.D2 midy) {
int[] shape = midy.getShape();
int ny = shape[0];
int nx = shape[1];
ArrayDouble.D2 edgey = new ArrayDouble.D2(ny + 2, nx + 1);
// compute the interior rows
for (int y = 0; y < ny; y++) {
for (int x = 1; x < nx; x++) {
double yval = (midy.get(y, x - 1) + midy.get(y, x)) / 2;
edgey.set(y + 1, x, yval);
}
edgey.set(y + 1, 0, midy.get(y, 0) - (edgey.get(y + 1, 1) - midy.get(y, 0)));
edgey.set(y + 1, nx, midy.get(y, nx - 1) - (edgey.get(y + 1, nx - 1) - midy.get(y, nx - 1)));
}
// compute the first row
for (int x = 0; x < nx; x++) {
double pt0 = midy.get(0, x);
double pt = edgey.get(2, x);
double diff = pt0 - pt;
edgey.set(0, x, pt0 + diff);
}
// compute the last row
for (int x = 0; x < nx - 1; x++) {
double pt0 = midy.get(ny - 1, x);
double pt = edgey.get(ny - 1, x);
double diff = pt0 - pt;
edgey.set(ny + 1, x, pt0 + diff);
}
return edgey;
}
///////////////////////////////////////////////////////////////////////////////
// bounds calculations
private ArrayDouble.D3 makeBoundsFromAux() {
if (!computeIsInterval()) return null;
Attribute boundsAtt = findAttributeIgnoreCase(CF.BOUNDS);
if (boundsAtt == null) return null;
String boundsVarName = boundsAtt.getStringValue();
VariableDS boundsVar = (VariableDS) ncd.findVariable(getParentGroup(), boundsVarName);
Array data;
try {
//boundsVar.setUseNaNs(false); // missing values not allowed
data = boundsVar.read();
} catch (IOException e) {
log.warn("CoordinateAxis2D.makeBoundsFromAux read failed ", e);
return null;
}
ArrayDouble.D3 bounds;
assert (data.getRank() == 3) && (data.getShape()[2] == 2) : "incorrect shape data for variable " + boundsVar;
if (data instanceof ArrayDouble.D3) {
bounds = (ArrayDouble.D3) data;
} else {
bounds = (ArrayDouble.D3) Array.factory(DataType.DOUBLE, data.getShape());
MAMath.copy(data, bounds);
}
return bounds;
}
private boolean computeIsInterval() {
intervalWasComputed = true;
Attribute boundsAtt = findAttributeIgnoreCase(CF.BOUNDS);
if ((null == boundsAtt) || !boundsAtt.isString()) return false;
String boundsVarName = boundsAtt.getStringValue();
VariableDS boundsVar = (VariableDS) ncd.findVariable(getParentGroup(), boundsVarName);
if (null == boundsVar) return false;
if (3 != boundsVar.getRank()) return false;
if (getDimension(0) != boundsVar.getDimension(0)) return false;
return 2 == boundsVar.getDimension(2).getLength();
}
///////////////////////////////////////
// time
public CoordinateAxisTimeHelper getCoordinateAxisTimeHelper() {
return new CoordinateAxisTimeHelper(getCalendarFromAttribute(), getUnitsString());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy