//
// LinearLatLonSet.java
//
/*
VisAD system for interactive analysis and visualization of numerical
data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
Tommy Jasmin.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/
package visad;
/**
LinearLatLonSet represents a finite set of samples of
(Latitude, Longitude) in a cross product of two
arithmetic progressions.
This class exists to override valueToInterp (as defined in GriddedSet)
in order to handle Longitude wrapping.
The order of the samples is the rasterization of the orders of
the 1D components, with the first component increasing fastest.
For more detail, see the example in Linear2DSet.java.
*/
public class LinearLatLonSet extends Linear2DSet {
private boolean LongitudeWrap;
private double WrapStep, WrapFactor;
private int latI; // index of latitude component
private int lonI; // index of longitude component
private double halfPiLat; // 0.5*pi in set lat units
private double halfPiLon; // 0.5*pi in set lon units
private double twoPiLon; // 2*pi in set lon units
private Linear1DSet lat; // references X or Y in super
private Linear1DSet lon; // references Y or X in super
/**
* Construct a 2-D cross product of arithmetic progressions whose east
* and west edges may be joined (for interpolation purposes), with
* null errors, CoordinateSystem and Units are defaults from type.
* @param type MathType for this LinearLatLonSet. Must be consistent
* with MathType-s of sets.
* @param sets Linear1DSets that make up this LinearLatLonSet.
* @throws VisADException illegal sets or other VisAD error.
*/
public LinearLatLonSet(MathType type, Linear1DSet[] sets) throws VisADException {
this(type, sets, null, null, null);
}
/**
* Construct a 2-D cross product of arithmetic progressions whose east
* and west edges may be joined (for interpolation purposes), with
* null errors, CoordinateSystem and Units are defaults from type
* @param type MathType for this LinearLatLonSet.
* @param first1 first value in first arithmetic progression
* @param last1 last value in first arithmetic progression
* @param length1 number of values in first arithmetic progression
* @param first2 first value in second arithmetic progression
* @param last2 last value in second arithmetic progression
* @param length2 number of values in second arithmetic progression
* @throws VisADException illegal sets or other VisAD error.
*/
public LinearLatLonSet(MathType type, double first1, double last1, int length1,
double first2, double last2, int length2)
throws VisADException {
this(type, first1, last1, length1, first2, last2, length2, null, null, null);
}
/**
* Construct a 2-D cross product of arithmetic progressions whose east
* and west edges may be joined (for interpolation purposes), with
* specified errors, coord_sys and
* units.
* @param type MathType for this LinearLatLonSet. Must be consistent
* with MathType-s of sets.
* @param sets Linear1DSets that make up this LinearLatLonSet.
* @param coord_sys CoordinateSystem for this set. May be null, but
* if not, must be consistent with type.
* @param units Unit-s for the values in sets. May
* be null, but must be convertible with values in
* sets.
* @param errors ErrorEstimate-s for values in sets,
* may be null
* @throws VisADException illegal sets or other VisAD error.
*/
public LinearLatLonSet(MathType type, Linear1DSet[] sets,
CoordinateSystem coord_sys, Unit[] units,
ErrorEstimate[] errors) throws VisADException {
this(type, sets, coord_sys, units, errors, false);
}
/**
* Construct a 2-D cross product of arithmetic progressions whose east
* and west edges may be joined (for interpolation purposes), with
* specified errors, coord_sys and
* units.
* @param type MathType for this LinearLatLonSet. Must be consistent
* with MathType-s of sets.
* @param sets Linear1DSets that make up this LinearLatLonSet.
* @param coord_sys CoordinateSystem for this set. May be null, but
* if not, must be consistent with type.
* @param units Unit-s for the values in sets. May
* be null, but must be convertible with values in
* sets.
* @param errors ErrorEstimate-s for values in sets,
* may be null
* @param cache if true, enumerate and cache the samples. This will
* result in a larger memory footprint, but will
* reduce the time to return samples from calls to
* {@link #getSamples()}
* @throws VisADException illegal sets or other VisAD error.
*/
public LinearLatLonSet(MathType type, Linear1DSet[] sets,
CoordinateSystem coord_sys, Unit[] units,
ErrorEstimate[] errors,
boolean cache) throws VisADException {
super(type, sets, coord_sys, units, errors, cache);
setParameters();
checkWrap();
}
/** a 2-D cross product of arithmetic progressions that whose east
and west edges may be joined (for interpolation purposes);
coordinate_system and units must be compatible with defaults
for type, or may be null; errors may be null */
/**
* Construct a 2-D cross product of arithmetic progressions whose east
* and west edges may be joined (for interpolation purposes), with
* specified errors, coord_sys and
* units.
* @param type MathType for this LinearLatLonSet.
* @param first1 first value in first arithmetic progression
* @param last1 last value in first arithmetic progression
* @param length1 number of values in first arithmetic progression
* @param first2 first value in second arithmetic progression
* @param last2 last value in second arithmetic progression
* @param length2 number of values in second arithmetic progression
* @param coord_sys CoordinateSystem for this set. May be null, but
* if not, must be consistent with type.
* @param units Unit-s for the values in sets. May
* be null, but must be convertible with values in
* sets.
* @param errors ErrorEstimate-s for values in sets,
* may be null
* @throws VisADException illegal sets or other VisAD error.
*/
public LinearLatLonSet(MathType type, double first1, double last1, int length1,
double first2, double last2, int length2,
CoordinateSystem coord_sys, Unit[] units,
ErrorEstimate[] errors) throws VisADException {
this(type, first1, last1, length1, first2, last2, length2, coord_sys,
units, errors, false);
}
/**
* Construct a 2-D cross product of arithmetic progressions whose east
* and west edges may be joined (for interpolation purposes), with
* specified errors, coord_sys and
* units.
* @param type MathType for this LinearLatLonSet.
* @param first1 first value in first arithmetic progression
* @param last1 last value in first arithmetic progression
* @param length1 number of values in first arithmetic progression
* @param first2 first value in second arithmetic progression
* @param last2 last value in second arithmetic progression
* @param length2 number of values in second arithmetic progression
* @param coord_sys CoordinateSystem for this set. May be null, but
* if not, must be consistent with type.
* @param units Unit-s for the values in sets. May
* be null, but must be convertible with values in
* sets.
* @param errors ErrorEstimate-s for values in sets,
* may be null
* @param cache if true, enumerate and cache the samples. This will
* result in a larger memory footprint, but will
* reduce the time to return samples from calls to
* {@link #getSamples()}
* @throws VisADException illegal sets or other VisAD error.
*/
public LinearLatLonSet(MathType type, double first1, double last1, int length1,
double first2, double last2, int length2,
CoordinateSystem coord_sys, Unit[] units,
ErrorEstimate[] errors,
boolean cache) throws VisADException {
super(type, first1, last1, length1, first2, last2, length2, coord_sys,
units, errors, cache);
setParameters();
checkWrap();
}
private void setParameters() throws VisADException, UnitException {
MathType type0 = ((SetType)getType()).getDomain().getComponent(0);
latI = RealType.Latitude.equals(type0) ? 0 : 1;
if (latI == 0) {
lonI = 1;
lat = X;
lon = Y;
} else {
lonI = 0;
lat = Y;
lon = X;
}
Unit[] units = getSetUnits();
halfPiLat = SI.radian.toThat(0.5*Math.PI, units[latI]);
halfPiLon = SI.radian.toThat(0.5*Math.PI, units[lonI]);
twoPiLon = 4.0 * halfPiLon;
}
private void setLatLonUnits()
{
}
void checkWrap() throws VisADException {
Unit[] units = getSetUnits();
if (Low[latI] < -halfPiLat || Hi[latI] > halfPiLat ||
Low[lonI] < -twoPiLon || Hi[lonI] > twoPiLon ||
(Hi[lonI] - Low[lonI]) > twoPiLon) {
throw new SetException("LinearLatLonSet: out of bounds " +
"(note Lat and Lon in Radians)");
}
LongitudeWrap =
(Hi[lonI] - Low[lonI]) + 2.0 * Math.abs(lon.getStep()) >= twoPiLon &&
lon.getLength() > 1;
if(lon.getLength() > 1 && lon.getFirst() > 0 && lon.getLast() < 0){
LongitudeWrap = true;
}
if (LongitudeWrap) {
WrapStep = twoPiLon - (Hi[lonI] - Low[lonI]);
WrapFactor = Math.abs(lon.getStep()) / WrapStep;
}
}
/** transform an array of non-integer grid coordinates to an array
of values in (Latitude, Longitude) */
public float[][] gridToValue(float[][] grid) throws VisADException {
if (grid.length != 2) {
throw new SetException("LinearLatLonSet.gridToValue: grid dimension" +
" should be 2, not " + grid.length);
}
if (Length > 1 && (Lengths[0] < 2 || Lengths[1] < 2)) {
throw new SetException("LinearLatLonSet.gridToValue: requires all grid " +
"dimensions to be > 1");
}
int length = grid[0].length;
float[][] gridLat = new float[1][];
gridLat[0] = grid[latI];
float[][] gridLon = new float[1][];
gridLon[0] = grid[lonI];
if (LongitudeWrap) {
float len = (float) lon.getLength();
float lenh = len - 0.5f;
float lenm = len - 1.0f;
for (int i=0; i lenm) {
gridLon[0][i] = (float) (lenm + (gridLon[0][i] - lenm) / WrapFactor);
if (gridLon[0][i] > lenh) gridLon[0][i] -= len;
}
else if (gridLon[0][i] < 0.0) {
gridLon[0][i] = (float) (gridLon[0][i] / WrapFactor);
if (gridLon[0][i] < -0.5) gridLon[0][i] += len;
}
}
}
float[][] valueLat = lat.gridToValue(gridLat);
float[][] valueLon = lon.gridToValue(gridLon);
float[][] value = new float[2][];
value[latI] = valueLat[0];
value[lonI] = valueLon[0];
return value;
}
/** transform an array of values in (Latitude, Longitude)
to an array of non-integer grid coordinates */
public float[][] valueToGrid(float[][] value) throws VisADException {
if (value.length != 2) {
throw new SetException("LinearLatLonSet.valueToGrid: value dimension" +
" should be 2, not " + value.length);
}
if (Length > 1 && (Lengths[0] < 2 || Lengths[1] < 2)) {
throw new SetException("LinearLatLonSet.valueToGrid: requires all grid " +
"dimensions to be > 1");
}
int length = value[0].length;
float[][] valueLat = new float[1][];
valueLat[0] = value[latI];
float[] valueLon = value[lonI];
float[][] gridLat = lat.valueToGrid(valueLat); // Latitude-s
float[] gridLon = new float[length];
float l = (float) (lon.getFirst() - 0.5 * lon.getStep());
float h = (float) (lon.getFirst() + (((float) lon.getLength()) - 0.5) * lon.getStep());
if (h < l) {
float temp = l;
l = h;
h = temp;
}
if (LongitudeWrap) {
l = (float) (l + 0.5 * Math.abs(lon.getStep()) - 0.5 * WrapStep);
h = (float) (l + twoPiLon);
}
for (int i=0; i lenm) {
gridLon[i] = (float) (lenm + (gridLon[i] - lenm) * WrapFactor);
if (gridLon[i] > lenh) gridLon[i] -= len;
}
else if (gridLon[i] < 0.0) {
gridLon[i] = (float) (gridLon[i] * WrapFactor);
if (gridLon[i] < -0.5) gridLon[i] += len;
}
}
}
float[][] grid = new float[2][];
grid[latI] = gridLat[0];
grid[lonI] = gridLon;
return grid;
}
/** for each of an array of values in (Latitude, Longitude), compute an array
of 1-D indices and an array of weights, to be used for interpolation;
indices[i] and weights[i] are null if i-th value is outside grid
(i.e., if no interpolation is possible).
this code is the result of substituting 2 for ManifoldDimension in
GriddedSet.valueToInterp, and adding logic to handle LongitudeWrap */
public void valueToInterp(float[][] value, int[][] indices, float weights[][])
throws VisADException {
if (value.length != DomainDimension) {
throw new SetException("LinearLatLonSet Domain dimension" +
" should be 2, not " + DomainDimension);
}
int length = value[0].length; // number of values
if (indices.length != length) {
throw new SetException("LinearLatLonSet.valueToInterp: indices length " +
indices.length +
" doesn't match value[0] length " +
value[0].length);
}
if (weights.length != length) {
throw new SetException("LinearLatLonSet.valueToInterp: weights length " +
weights.length +
" doesn't match value[0] length " +
value[0].length);
}
float[][] grid = valueToGrid(value); // convert value array to grid coord array
/* if LongitudeWrap, grid[lonI][i] should be between -0.5 and lon.Length-0.5 */
int i, j, k; // loop indices
int lis; // temporary length of is & cs
int length_is; // final length of is & cs, varies by i
int isoff; // offset along one grid dimension
float a, b; // weights along one grid dimension; a + b = 1.0
int[] is; // array of indices, becomes part of indices
float[] cs; // array of coefficients, become part of weights
int base; // base index, as would be returned by valueToIndex
int[] l = new int[2]; // integer 'factors' of base
float[] c = new float[2]; // fractions with l; -0.5 <= c <= 0.5
// array of index offsets by grid dimension
int[] off = new int[2];
off[0] = 1;
off[1] = off[0] * Lengths[0];
for (i=0; i= 0.0))) {
// interp along Longitude (dim lonI) if between two valid grid coords
length_is *= 2;
}
else if (LongitudeWrap) {
length_is *= 2;
WrapThis = true;
}
base = l[lonI];
}
if (base>=0) {
if (Double.isNaN(grid[latI][i])) {
base = -1;
}
else {
l[latI] = (int) (grid[latI][i] + 0.5);
c[latI] = grid[latI][i] - ((float) l[latI]);
if (!((l[latI] == 0 && c[latI] <= 0.0) ||
(l[latI] == Lengths[latI] - 1 && c[latI] >= 0.0))) {
// interp along Latitude (dim latI) if between two valid grid coords
length_is *= 2;
}
base = off[latI] * l[latI] + off[lonI] * base;
}
}
if (base < 0) {
// value is out of grid so return null
is = null;
cs = null;
}
else {
// create is & cs of proper length, and init first element
is = new int[length_is];
cs = new float[length_is];
is[0] = base;
cs[0] = 1.0f;
lis = 1;
// unroll loop over dimension = 0, 1
// WLH 31 Oct 2001
if (!((l[latI] == 0 && c[latI] <= 0.0) ||
(l[latI] == Lengths[latI] - 1 && c[latI] >= 0.0)) ) {
// if (WrapThis || !((l[latI] == 0 && c[latI] <= 0.0) ||
// (l[latI] == Lengths[latI] - 1 && c[latI] >= 0.0)) ) {
// interp along Latitude (dim latI) if between two valid grid coords
if (c[latI] >= 0.0) {
// grid coord above base
isoff = off[latI];
a = 1.0f - c[latI];
b = c[latI];
}
else {
// grid coord below base
isoff = -off[latI];
a = 1.0f + c[latI];
b = -c[latI];
}
// float is & cs; adjust new offsets; split weights
for (k=0; k= 0.0)) ) {
// interp along Longitude (dim lonI) if between two valid grid coords
if (WrapThis && l[lonI] == 0) {
// c[lonI] <= 0.0; grid coord below base
isoff = off[lonI] * (Lengths[lonI] - 1); // so Wrap to top
a = 1.0f + c[lonI];
b = -c[lonI];
}
else if (WrapThis && l[lonI] == Lengths[lonI] - 1) {
// c[lonI] >= 0.0; grid coord above base
isoff = -off[lonI] * (Lengths[lonI] - 1); // so Wrap to bottom
a = 1.0f - c[lonI];
b = c[lonI];
}
else if (c[lonI] >= 0.0) {
// grid coord above base
isoff = off[lonI];
a = 1.0f - c[lonI];
b = c[lonI];
}
else {
// grid coord below base
isoff = -off[lonI];
a = 1.0f + c[lonI];
b = -c[lonI];
}
// float is & cs; adjust new offsets; split weights
for (k=0; k
Ads help maintain this website.