//
// Linear1DSet.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;
/**
Linear1DSet represents a finite set of samples of R in
an arithmetic progression.<P>
The samples are ordered from First to Last.<P>
*/
public class Linear1DSet extends Gridded1DSet
implements LinearSet, GriddedDoubleSet {
private double First, Last, Step, Invstep;
private boolean cacheSamples;
/**
* Construct a 1-D arithmetic progression with null
* errors and generic type
* @param first first value in progression
* @param last last value in progression
* @param length number of samples in progression (R)
* @throws VisADException problem creating set
*/
public Linear1DSet(double first, double last, int length)
throws VisADException {
this(RealType.Generic, first, last, length, null, null, null);
}
/**
* Construct a 1-D arithmetic progression with the specified
* <code>type</code> and null errors.
* @param type MathType for this Linear1DSet.
* @param first first value in progression
* @param last last value in progression
* @param length number of samples in progression (R)
* @throws VisADException problem creating set
*/
public Linear1DSet(MathType type, double first, double last, int length)
throws VisADException {
this(type, first, last, length, null, null, null);
}
/**
* Construct a 1-D arithmetic progression with
* the specified <code>type</code>, <code>coord_sys</code>,
* <code>units</code> and <code>errors</code>.
* @param type MathType for this Linear2DSet.
* @param first first value in progression
* @param last last value in progression
* @param length number of samples in progression (R)
* @param coord_sys CoordinateSystem for this set. May be null, but
* if not, must be consistent with <code>type</code>.
* @param units Unit-s for the values in <code>sets</code>. May
* be null, but must be convertible with default
* of <code>type</code> if specified.
* @param errors ErrorEstimate-s for values in <code>sets</code>,
* may be null
* @throws VisADException problem creating VisAD objects.
*/
public Linear1DSet(MathType type, double first, double last, int length,
CoordinateSystem coord_sys, Unit[] units,
ErrorEstimate[] errors) throws VisADException {
this(type, first, last, length, coord_sys, units, errors, false);
}
/**
* Construct a 1-D arithmetic progression with
* the specified <code>type</code>, <code>coord_sys</code>,
* <code>units</code> and <code>errors</code>.
* @param type MathType for this Linear2DSet.
* @param first first value in progression
* @param last last value in progression
* @param length number of samples in progression (R)
* @param coord_sys CoordinateSystem for this set. May be null, but
* if not, must be consistent with <code>type</code>.
* @param units Unit-s for the values in <code>sets</code>. May
* be null, but must be convertible with default
* of <code>type</code> if specified.
* @param errors ErrorEstimate-s for values in <code>sets</code>,
* 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 problem creating VisAD objects.
*/
public Linear1DSet(MathType type, double first, double last, int length,
CoordinateSystem coord_sys, Unit[] units,
ErrorEstimate[] errors,
boolean cache) throws VisADException {
super(type, (float[][]) null, length, coord_sys, units, errors);
if (DomainDimension != 1) {
throw new SetException("Linear1DSet: DomainDimension must be 1, not " +
DomainDimension);
}
First = first;
Last = last;
Length = length;
if (Length < 1) throw new SetException("Linear1DSet: number of samples (" +
Length + " must be greater than 0");
Step = (Length < 2) ? 1.0 : (Last - First) / (Length - 1);
Invstep = 1.0 / Step;
LowX = (float) Math.min(First, First + Step * (Length - 1));
HiX = (float) Math.max(First, First + Step * (Length - 1));
Low[0] = LowX;
Hi[0] = HiX;
if (SetErrors[0] != null ) {
SetErrors[0] =
new ErrorEstimate(SetErrors[0].getErrorValue(), (Low[0] + Hi[0]) / 2.0,
Length, SetErrors[0].getUnit());
}
cacheSamples = cache;
}
/**
* Convert an array of 1-D indices to an array of values in
* the set corresponding to those indices.
* @param index array of indices of values in R^1 space.
* @return values in R^1 space corresponding to indices.
* @throws VisADException problem converting indices to values.
*/
public float[][] indexToValue(int[] index) throws VisADException {
int length = index.length;
float[][] values = new float[1][length];
for (int i=0; i<length; i++) {
if (0 <= index[i] && index[i] < Length) {
values[0][i] = (float) (First + ((double) index[i]) * Step);
}
else {
values[0][i] = Float.NaN;
}
}
return values;
}
/**
* Convert an array of 1-D indices to an array of double values in
* the set corresponding to those indices.
* @param index array of indices of values in R space.
* @return values in R space corresponding to indices.
* @throws VisADException problem converting indices to values.
*/
public double[][] indexToDouble(int[] index) throws VisADException {
int length = index.length;
double[][] values = new double[1][length];
for (int i=0; i<length; i++) {
if (0 <= index[i] && index[i] < Length) {
values[0][i] = (First + ((double) index[i]) * Step);
}
else {
values[0][i] = Double.NaN;
}
}
return values;
}
public int[] doubleToIndex(double[][] value) throws VisADException {
if (value.length != DomainDimension) {
throw new SetException("Linear1DSet.doubleToIndex: value dimension " +
value.length + " not equal to Domain dimension " +
DomainDimension);
}
int length = value[0].length;
int[] index = new int[length];
double l = -0.5;
double h = Length - 0.5;
for (int i=0; i<length; i++) {
double di = 0.5 + (value[0][i] - First) * Invstep;
index[i] = (0.0 < di && di < (double) Length) ? (int) di : -1;
}
return index;
}
/** transform an array of non-integer grid coordinates to an array
of values in R */
public float[][] gridToValue(float[][] grid) throws VisADException {
if (grid.length != 1) {
throw new SetException("Linear1DSet.gridToValue: grid dimension" +
" should be 1, not " + grid.length);
}
/* remove DRM: 2004-09-14
if (Length < 2) {
throw new SetException("Linear1DSet.gridToValue: requires all grid " +
"dimensions to be > 1");
}
*/
int length = grid[0].length;
float[][] value = new float[1][length];
float[] value0 = value[0];
float[] grid0 = grid[0];
float l = -0.5f;
float h = ((float) Length) - 0.5f;
float g;
for (int i=0; i<length; i++) {
g = grid0[i];
value0[i] = (float) ((l < g && g < h) ? First + g * Step : Float.NaN);
}
return value;
}
/** transform an array of non-integer grid coordinates to an array
of values in R */
public double[][] gridToDouble(double[][] grid) throws VisADException {
if (grid.length != 1) {
throw new SetException("Linear1DSet.gridToValue: grid dimension" +
" should be 1, not " + grid.length);
}
/* remove DRM: 2004-09-14
if (Length < 2) {
throw new SetException("Linear1DSet.gridToValue: requires all grid " +
"dimensions to be > 1");
}
*/
int length = grid[0].length;
double[][] value = new double[1][length];
double[] value0 = value[0];
double[] grid0 = grid[0];
double l = -0.5;
double h = (Length) - 0.5;
double g;
for (int i=0; i<length; i++) {
g = grid0[i];
value0[i] = ((l < g && g < h) ? First + g * Step : Float.NaN);
}
return value;
}
/** transform an array of values in R to an array
of non-integer grid coordinates */
public float[][] valueToGrid(float[][] value) throws VisADException {
if (value.length != 1) {
throw new SetException("Linear1DSet.valueToGrid: value dimension" +
" should be 1, not " + value.length);
}
/* remove DRM: 2004-09-14
if (Lengths[0] < 2) {
throw new SetException("Linear1DSet.valueToGrid: requires all grid " +
"dimensions to be > 1");
}
*/
int length = value[0].length;
float[][] grid = new float[1][length];
float[] grid0 = grid[0];
float[] value0 = value[0];
float l = (float) (First - 0.5 * Step);
float h = (float) (First + (((float) Length) - 0.5) * Step);
float v;
if (h < l) {
float temp = l;
l = h;
h = temp;
}
for (int i=0; i<length; i++) {
v = value0[i];
grid0[i] = (float) ((l < v && v < h) ? (v - First) * Invstep : Float.NaN);
}
return grid;
}
/** transform an array of values in R to an array
of non-integer grid coordinates */
public double[][] doubleToGrid(double[][] value) throws VisADException {
if (value.length != 1) {
throw new SetException("Linear1DSet.valueToGrid: value dimension" +
" should be 1, not " + value.length);
}
/* remove DRM: 2004-09-14
if (Lengths[0] < 2) {
throw new SetException("Linear1DSet.valueToGrid: requires all grid " +
"dimensions to be > 1");
}
*/
int length = value[0].length;
double[][] grid = new double[1][length];
double[] grid0 = grid[0];
double[] value0 = value[0];
double l = (First - 0.5 * Step);
double h = (First + (((float) Length) - 0.5) * Step);
double v;
if (h < l) {
double temp = l;
l = h;
h = temp;
}
for (int i=0; i<length; i++) {
v = value0[i];
grid0[i] = ((l < v && v < h) ? (v - First) * Invstep : Double.NaN);
}
return grid;
}
/** for each of an array of values in R^DomainDimension, 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) */
public void doubleToInterp(double[][] value, int[][] indices,
double[][] weights) throws VisADException
{
if (value.length != DomainDimension) {
throw new SetException("Linear1DSet.doubleToInterp: value dimension " +
value.length + " not equal to Domain dimension " +
DomainDimension);
}
int length = value[0].length; // number of values
if (indices.length != length) {
throw new SetException("Linear1DSet.doubleToInterp: indices length " +
indices.length +
" doesn't match value[0] length " +
value[0].length);
}
if (weights.length != length) {
throw new SetException("Linear1DSet.doubleToInterp: weights length " +
weights.length +
" doesn't match value[0] length " +
value[0].length);
}
// convert value array to grid coord array
double[][] grid = doubleToGrid(value);
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
double a, b; // weights along one grid dimension; a + b = 1.0
int[] is; // array of indices, becomes part of indices
double[] cs; // array of coefficients, become part of weights
int base; // base index, as would be returned by valueToIndex
int[] l = new int[ManifoldDimension]; // integer 'factors' of base
// fractions with l; -0.5 <= c <= 0.5
double[] c = new double[ManifoldDimension];
// array of index offsets by grid dimension
int[] off = new int[ManifoldDimension];
off[0] = 1;
for (j=1; j<ManifoldDimension; j++) off[j] = off[j-1] * Lengths[j-1];
for (i=0; i<length; i++) {
// compute length_is, base, l & c
length_is = 1;
if (Double.isNaN(grid[ManifoldDimension-1][i])) {
base = -1;
}
else {
l[ManifoldDimension-1] = (int) (grid[ManifoldDimension-1][i] + 0.5);
// WLH 23 Dec 99
if (l[ManifoldDimension-1] == Lengths[ManifoldDimension-1]) {
l[ManifoldDimension-1]--;
}
c[ManifoldDimension-1] = grid[ManifoldDimension-1][i] -
((double) l[ManifoldDimension-1]);
if (!((l[ManifoldDimension-1] == 0 && c[ManifoldDimension-1] <= 0.0) ||
(l[ManifoldDimension-1] == Lengths[ManifoldDimension-1] - 1 &&
c[ManifoldDimension-1] >= 0.0))) {
// only interp along ManifoldDimension-1
// if between two valid grid coords
length_is *= 2;
}
base = l[ManifoldDimension-1];
}
for (j=ManifoldDimension-2; j>=0 && base>=0; j--) {
if (Double.isNaN(grid[j][i])) {
base = -1;
}
else {
l[j] = (int) (grid[j][i] + 0.5);
if (l[j] == Lengths[j]) l[j]--; // WLH 23 Dec 99
c[j] = grid[j][i] - ((double) l[j]);
if (!((l[j] == 0 && c[j] <= 0.0) ||
(l[j] == Lengths[j] - 1 && c[j] >= 0.0))) {
// only interp along dimension j if between two valid grid coords
length_is *= 2;
}
base = l[j] + Lengths[j] * 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 double[length_is];
is[0] = base;
cs[0] = 1.0f;
lis = 1;
for (j=0; j<ManifoldDimension; j++) {
if (!((l[j] == 0 && c[j] <= 0.0) ||
(l[j] == Lengths[j] - 1 && c[j] >= 0.0))) {
// only interp along dimension j if between two valid grid coords
if (c[j] >= 0.0) {
// grid coord above base
isoff = off[j];
a = 1.0f - c[j];
b = c[j];
}
else {
// grid coord below base
isoff = -off[j];
a = 1.0f + c[j];
b = -c[j];
}
// double is & cs; adjust new offsets; split weights
for (k=0; k<lis; k++) {
is[k+lis] = is[k] + isoff;
cs[k+lis] = cs[k] * b;
cs[k] *= a;
}
lis *= 2;
}
}
}
indices[i] = is;
weights[i] = cs;
}
}
/**
* Get the first value in this arithmetic progression.
* @return first value
*/
public double getFirst() {
return First;
}
/**
* Get the last value in this arithmetic progression.
* @return last value
*/
public double getLast() {
return Last;
}
/**
* Get the interval between values in this progression
* @return step interval of progression
*/
public double getStep() {
return Step;
}
/**
* Get the inverse of the step (1.0/getStep()).
* @return inverse of step interval of progression
*/
public double getInvstep() {
return Invstep;
}
/**
* Check to see if this is an empty progression
* @return always false.
*/
public boolean isMissing() {
return false;
}
/**
* Return the array of values as doubles in R space corresponding to
* this arithmetic progression.
* @param copy if true, return a copy of the samples.
* @return array of values in R space.
* @throws VisADException problem creating samples.
*/
public double[][] getDoubles(boolean copy) throws VisADException {
/* DRM 2003-01-16
int n = getLength();
int[] indices = new int[n];
// do NOT call getWedge
for (int i=0; i<n; i++) indices[i] = i;
return indexToDouble(indices);
*/
double[][] samples = new double[1][Length];
for (int i=0; i<Length; i++) {
samples[0][i] = (First + ((double) i) * Step);
}
return samples;
}
/**
* Return the array of values in R space corresponding to
* this arithmetic progression.
* @param copy if true, return a copy of the samples.
* @return array of values in R space.
* @throws VisADException problem creating samples.
*/
public float[][] getSamples(boolean copy) throws VisADException {
/* DRM 2003-01-16
int n = getLength();
int[] indices = new int[n];
// do NOT call getWedge
for (int i=0; i<n; i++) indices[i] = i;
return indexToValue(indices);
*/
float[][]mySamples = getMySamples();
if (mySamples != null) {
return copy ? Set.copyFloats(mySamples) : mySamples;
}
float[][] samples = makeSamples ();
if (cacheSamples) {
setMySamples(samples);
return copy ? Set.copyFloats(samples) : samples;
}
return samples;
}
/** code to actually enumerate the samples for this progression*/
private float[][] makeSamples() throws VisADException {
float[][] samples = new float[1][Length];
for (int i=0; i<Length; i++) {
samples[0][i] = (float) (First + ((double) i) * Step);
}
return samples;
}
/**
* Check to see if this Linear1DSet is equal to the Object
* in question.
* @param set Object in question
* @return true if <code>set</code> is a Linear1DSet and
* the type, first, last and lengths of the progressions
* are equal.
*/
public boolean equals(Object set) {
boolean flag;
if (!(set instanceof Linear1DSet) || set == null) return false;
if (this == set) return true;
if (!equalUnitAndCS((Set) set)) return false;
try {
flag = (First == ((Linear1DSet) set).getFirst() &&
Last == ((Linear1DSet) set).getLast() &&
Length == ((Linear1DSet) set).getLength());
}
catch (VisADException e) {
return false;
}
return flag;
}
/**
* Returns the hash code for this instance.
* @return The hash code for this instance.
*/
public int hashCode()
{
if (!hashCodeSet)
{
hashCode =
unitAndCSHashCode() ^
new Double(First).hashCode() ^
new Double(Last).hashCode() ^
Length;
hashCodeSet = true;
}
return hashCode;
}
/**
* Get the indexed component (X is at 0 and is the only valid index)
*
* @param i Index of component
* @return The requested component
* @exception ArrayIndexOutOfBoundsException If an invalid index is
* specified.
*/
public Linear1DSet getLinear1DComponent(int i) {
if (i == 0) return this;
else throw new ArrayIndexOutOfBoundsException("Invalid component index " +
i);
}
/**
* Return a clone of this object with a new MathType.
* @param type new MathType.
* @return new Linear1DSet with <code>type</code>.
* @throws VisADException if <code>type</code> is not compatible
* with MathType of this.
*/
public Object cloneButType(MathType type) throws VisADException {
return new Linear1DSet(type, First, Last, Length, DomainCoordinateSystem,
SetUnits, SetErrors, cacheSamples);
}
/**
* Extended version of the toString() method.
* @param pre prefix for string.
* @return wordy string describing this Linear1DSet.
*/
public String longString(String pre) throws VisADException {
return pre + "Linear1DSet: Length = " + Length +
" Range = " + First + " to " + Last + "\n";
}
}