NOTICE: This version of the NSF Unidata web site (archive.unidata.ucar.edu) is no longer being updated.
Current content can be found at unidata.ucar.edu.
To learn about what's going on, see About the Archive Site.
Hi James, > I'm trying to do something fairly similar to > "Simple.java" in the Developers Guide. Simple.java > uses an Integer1DSet as the domain, and > "field.evaluate" works fine. However, I want to > represent time as a Gridded1DDoubleSet in the domain. > When I do this (see attached code) I get the exception > below. > > So should I be able to do this, or do I need to go > about it a different way? Thanks for finding this bug. The fix is in the attached Gridded1DDoubleSet.java. Cheers, Bill ---------------------------------------------------------- Bill Hibbard, SSEC, 1225 W. Dayton St., Madison, WI 53706 hibbard@xxxxxxxxxxxxxxxxx 608-263-4427 fax: 608-263-6738 http://www.ssec.wisc.edu/~billh/vis.html
// // Gridded1DDoubleSet.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2001 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; import java.lang.ref.WeakReference; import java.util.WeakHashMap; /** Gridded1DDoubleSet is a Gridded1DSet with double-precision samples.<P> */ public class Gridded1DDoubleSet extends Gridded1DSet implements GriddedDoubleSet { double[] Low = new double[1]; double[] Hi = new double[1]; double LowX, HiX; double[][] Samples; /** * A canonicalizing cache of previously-created instances. Because instances * are immutable, a cache can be used to reduce memory usage by ensuring * that each instance is truely unique. By implementing the cache using a * {@link WeakHashMap}, this can be accomplished without the technique itself * adversely affecting memory usage. */ private static final WeakHashMap cache = new WeakHashMap(); // Overridden Gridded1DSet constructors (float[][]) /** a 1-D sequence with no regular interval with null errors, CoordinateSystem and Units are defaults from type */ public Gridded1DDoubleSet(MathType type, float[][] samples, int lengthX) throws VisADException { this(type, Set.floatToDouble(samples), lengthX, null, null, null, true); } public Gridded1DDoubleSet(MathType type, float[][] samples, int lengthX, CoordinateSystem coord_sys, Unit[] units, ErrorEstimate[] errors) throws VisADException { this(type, Set.floatToDouble(samples), lengthX, coord_sys, units, errors, true); } /** a 1-D sorted sequence with no regular interval. samples array is organized float[1][number_of_samples] where lengthX number_of_samples. samples must be sorted (either increasing or decreasing). coordinate_system and units must be compatible with defaults for type, or may be null. errors may be null */ public Gridded1DDoubleSet(MathType type, float[][] samples, int lengthX, CoordinateSystem coord_sys, Unit[] units, ErrorEstimate[] errors, boolean copy) throws VisADException { this(type, Set.floatToDouble(samples), lengthX, coord_sys, units, errors, copy); } // Corresponding Gridded1DDoubleSet constructors (double[][]) /** a 1-D sequence with no regular interval with null errors, CoordinateSystem and Units are defaults from type */ public Gridded1DDoubleSet(MathType type, double[][] samples, int lengthX) throws VisADException { this(type, samples, lengthX, null, null, null, true); } public Gridded1DDoubleSet(MathType type, double[][] samples, int lengthX, CoordinateSystem coord_sys, Unit[] units, ErrorEstimate[] errors) throws VisADException { this(type, samples, lengthX, coord_sys, units, errors, true); } /** a 1-D sorted sequence with no regular interval. samples array is organized double[1][number_of_samples] where lengthX number_of_samples. samples must be sorted (either increasing or decreasing). coordinate_system and units must be compatible with defaults for type, or may be null. errors may be null */ public Gridded1DDoubleSet(MathType type, double[][] samples, int lengthX, CoordinateSystem coord_sys, Unit[] units, ErrorEstimate[] errors, boolean copy) throws VisADException { super(type, null, lengthX, coord_sys, units, errors, copy); if (samples == null) { throw new SetException("Gridded1DDoubleSet: samples are null"); } init_doubles(samples, copy); LowX = Low[0]; HiX = Hi[0]; LengthX = Lengths[0]; if (Samples != null && Lengths[0] > 1) { // samples consistency test for (int i=0; i<Length; i++) { if (Samples[0][i] != Samples[0][i]) { throw new SetException( "Gridded1DDoubleSet: samples values may not be missing"); } } Ascending = (Samples[0][LengthX-1] > Samples[0][0]); if (Ascending) { for (int i=1; i<LengthX; i++) { if (Samples[0][i] < Samples[0][i-1]) { throw new SetException( "Gridded1DDoubleSet: samples do not form a valid grid ("+i+")"); } } } else { // !Ascending for (int i=1; i<LengthX; i++) { if (Samples[0][i] > Samples[0][i-1]) { throw new SetException( "Gridded1DDoubleSet: samples do not form a valid grid ("+i+")"); } } } } } /** * Returns an instance of this class. This method uses a weak cache of * previously-created instances to reduce memory usage. * * @param type The type of the set. Must be a {@link * RealType} or a single-component {@link * RealTupleType} or {@link SetType}. * @param samples The values in the set. * <code>samples[i]</code> is the value of * the ith sample point. Must be sorted (either * increasing or decreasing). May be * <code>null</code>. The array is not copied, so * either don't modify it or clone it first. * @param coord_sys The coordinate system for this, particular, set. * Must be compatible with the default coordinate * system. May be <code>null</code>. * @param unit The unit for the samples. Must be compatible * with the default unit. May be * <code>null</code>. * @param error The error estimate of the samples. May be * <code>null</code>. */ public static synchronized Gridded1DDoubleSet create( MathType type, double[] samples, CoordinateSystem coordSys, Unit unit, ErrorEstimate error) throws VisADException { Gridded1DDoubleSet newSet new Gridded1DDoubleSet( type, new double[][] {samples}, samples.length, coordSys, new Unit[] {unit}, new ErrorEstimate[] {error}, false); WeakReference ref = (WeakReference)cache.get(newSet); if (ref == null) { /* * The new instance is unique (any and all previously-created identical * instances no longer exist). * * A WeakReference is used in the following because values of a * WeakHashMap aren't "weak" themselves and must not strongly reference * their associated keys either directly or indirectly. */ cache.put(newSet, new WeakReference(newSet)); } else { /* * The new instance is a duplicate of a previously-created one. */ Gridded1DDoubleSet oldSet = (Gridded1DDoubleSet)ref.get(); if (oldSet == null) { /* The previous instance no longer exists. Save the new instance. */ cache.put(newSet, new WeakReference(newSet)); } else { /* The previous instance still exists. Reuse it to save memory. */ newSet = oldSet; } } return newSet; } // Overridden Gridded1DSet methods (float[][]) public float[][] getSamples() throws VisADException { return getSamples(true); } public float[][] getSamples(boolean copy) throws VisADException { return Set.doubleToFloat(Samples); } /** convert an array of 1-D indices to an array of values in R^DomainDimension */ public float[][] indexToValue(int[] index) throws VisADException { return Set.doubleToFloat(indexToDouble(index)); } /** * Convert an array of values in R^DomainDimension to an array of * 1-D indices. This Gridded1DDoubleSet must have at least two points in the * set. * @param value An array of coordinates. <code>value[i][j] * <code> contains the <code>i</code>th component of the * <code>j</code>th point. * @return Indices of nearest points. RETURN_VALUE<code>[i]</code> * will contain the index of the point in the set closest * to <code>value[][i]</code> or <code>-1</code> if * <code>value[][i]</code> lies outside the set. */ public int[] valueToIndex(float[][] value) throws VisADException { return doubleToIndex(Set.floatToDouble(value)); } public float[][] gridToValue(float[][] grid) throws VisADException { return Set.doubleToFloat(gridToDouble(Set.floatToDouble(grid))); } /** transform an array of values in R^DomainDimension to an array of non-integer grid coordinates */ public float[][] valueToGrid(float[][] value) throws VisADException { return Set.doubleToFloat(doubleToGrid(Set.floatToDouble(value))); } /** 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 valueToInterp(float[][] value, int[][] indices, float[][] weights) throws VisADException { int len = weights.length; double[][] w = new double[len][]; // for (int i=0; i<len; i++) w[i] = new double[weights[i].length]; doubleToInterp(Set.floatToDouble(value), indices, w); for (int i=0; i<len; i++) { if (w[i] != null) { weights[i] = new float[w[i].length]; for (int j=0; j<w[i].length; j++) { weights[i][j] = (float) w[i][j]; } } } } public float getLowX() { return (float) LowX; } public float getHiX() { return (float) HiX; } // Corresponding Gridded1DDoubleSet methods (double[][]) public double[][] getDoubles() throws VisADException { return getDoubles(true); } public double[][] getDoubles(boolean copy) throws VisADException { return copy ? Set.copyDoubles(Samples) : Samples; } /** convert an array of 1-D indices to an array of values in R^DomainDimension */ public double[][] indexToDouble(int[] index) throws VisADException { int length = index.length; if (Samples == null) { // not used - over-ridden by Linear1DSet.indexToValue double[][] grid = new double[ManifoldDimension][length]; for (int i=0; i<length; i++) { if (0 <= index[i] && index[i] < Length) { grid[0][i] = (double) index[i]; } else { grid[0][i] = -1; } } return gridToDouble(grid); } else { double[][] values = new double[1][length]; for (int i=0; i<length; i++) { if (0 <= index[i] && index[i] < Length) { values[0][i] = Samples[0][index[i]]; } else { values[0][i] = Double.NaN; } } return values; } } public int[] doubleToIndex(double[][] value) throws VisADException { if (value.length != DomainDimension) { throw new SetException("Gridded1DDoubleSet.doubleToIndex: value dimension " + value.length + " not equal to Domain dimension " + DomainDimension); } int length = value[0].length; int[] index = new int[length]; double[][] grid = doubleToGrid(value); double[] grid0 = grid[0]; double g; for (int i=0; i<length; i++) { g = grid0[i]; index[i] = Double.isNaN(g) ? -1 : ((int) (g + 0.5)); } return index; } /** transform an array of non-integer grid coordinates to an array of values in R^DomainDimension */ public double[][] gridToDouble(double[][] grid) throws VisADException { if (grid.length < DomainDimension) { throw new SetException("Gridded1DDoubleSet.gridToDouble: grid dimension " + grid.length + " not equal to Domain dimension " + DomainDimension); } if (Lengths[0] < 2) { throw new SetException("Gridded1DDoubleSet.gridToDouble: " + "requires all grid dimensions to be > 1"); } int length = grid[0].length; double[][] value = new double[1][length]; for (int i=0; i<length; i++) { // let g be the current grid coordinate double g = grid[0][i]; if ( (g < -0.5) || (g > LengthX-0.5) ) { value[0][i] = Double.NaN; continue; } // calculate closest integer variable int ig; if (g < 0) ig = 0; else if (g >= LengthX-1) ig = LengthX - 2; else ig = (int) g; double A = g - ig; // distance variable // do the linear interpolation value[0][i] = (1-A)*Samples[0][ig] + A*Samples[0][ig+1]; } return value; } /** transform an array of values in R^DomainDimension to an array of non-integer grid coordinates */ public double[][] doubleToGrid(double[][] value) throws VisADException { if (value.length < DomainDimension) { throw new SetException("Gridded1DDoubleSet.doubleToGrid: value dimension " + value.length + " not equal to Domain dimension " + DomainDimension); } if (Lengths[0] < 2) { throw new SetException("Gridded1DDoubleSet.doubleToGrid: " + "requires all grid dimensions to be > 1"); } double[] vals = value[0]; int length = vals.length; double[] samps = Samples[0]; double[][] grid = new double[1][length]; int ig = (LengthX - 1)/2; for (int i=0; i<length; i++) { if (Double.isNaN(vals[i])) grid[0][i] = Double.NaN; else { int lower = 0; int upper = LengthX-1; while (lower < upper) { if ((vals[i] - samps[ig]) * (vals[i] - samps[ig+1]) <= 0) break; if (Ascending ? samps[ig+1] < vals[i] : samps[ig+1] > vals[i]) { lower = ig+1; } else if (Ascending ? samps[ig] > vals[i] : samps[ig] < vals[i]) { upper = ig; } if (lower < upper) ig = (lower + upper) / 2; } // Newton's method double solv = ig + (vals[i] - samps[ig]) / (samps[ig+1] - samps[ig]); if (solv >= -0.5 && solv <= LengthX - 0.5) grid[0][i] = solv; else { grid[0][i] = Double.NaN; // next guess should be in the middle if previous value was missing ig = (LengthX - 1)/2; } } } 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("Gridded1DDoubleSet.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("Gridded1DDoubleinearLatLonSet.doubleToInterp: indices length " + indices.length + " doesn't match value[0] length " + value[0].length); } if (weights.length != length) { throw new SetException("Gridded1DDoubleSet.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; } } public double getDoubleLowX() { return LowX; } public double getDoubleHiX() { return HiX; } // Miscellaneous Set methods that must be overridden // (this code is duplicated throughout all *DoubleSet classes) void init_doubles(double[][] samples, boolean copy) throws VisADException { if (samples.length != DomainDimension) { throw new SetException("Gridded1DDoubleSet.init_doubles:" + " samples dimension " + samples.length + " not equal to Domain dimension " + DomainDimension); } if (Length == 0) { // Length set in init_lengths, but not called for IrregularSet Length = samples[0].length; } else { if (Length != samples[0].length) { throw new SetException("Gridded1DDoubleSet.init_doubles: samples[0] length " + samples[0].length + " doesn't match expected length " + Length); } } // MEM if (copy) { Samples = new double[DomainDimension][Length]; } else { Samples = samples; } for (int j=0; j<DomainDimension; j++) { if (samples[j].length != Length) { throw new SetException("Gridded1DDoubleSet.init_doubles: samples[" + j + "] length " + samples[0].length + " doesn't match expected length " + Length); } double[] samplesJ = samples[j]; double[] SamplesJ = Samples[j]; if (copy) { System.arraycopy(samplesJ, 0, SamplesJ, 0, Length); } Low[j] = Double.POSITIVE_INFINITY; Hi[j] = Double.NEGATIVE_INFINITY; double sum = 0.0f; for (int i=0; i<Length; i++) { if (SamplesJ[i] == SamplesJ[i] && !Double.isInfinite(SamplesJ[i])) { if (SamplesJ[i] < Low[j]) Low[j] = SamplesJ[i]; if (SamplesJ[i] > Hi[j]) Hi[j] = SamplesJ[i]; } else { SamplesJ[i] = Double.NaN; } sum += SamplesJ[i]; } if (SetErrors[j] != null ) { SetErrors[j] new ErrorEstimate(SetErrors[j].getErrorValue(), sum / Length, Length, SetErrors[j].getUnit()); } super.Low[j] = (float) Low[j]; super.Hi[j] = (float) Hi[j]; } } public void cram_missing(boolean[] range_select) { int n = Math.min(range_select.length, Samples[0].length); for (int i=0; i<n; i++) { if (!range_select[i]) Samples[0][i] = Double.NaN; } } public boolean isMissing() { return (Samples == null); } public boolean equals(Object set) { if (!(set instanceof Gridded1DDoubleSet) || set == null) return false; if (this == set) return true; if (testNotEqualsCache((Set) set)) return false; if (testEqualsCache((Set) set)) return true; if (!equalUnitAndCS((Set) set)) return false; try { int i, j; if (DomainDimension != ((Gridded1DDoubleSet) set).getDimension() || ManifoldDimension ! ((Gridded1DDoubleSet) set).getManifoldDimension() || Length != ((Gridded1DDoubleSet) set).getLength()) return false; for (j=0; j<ManifoldDimension; j++) { if (Lengths[j] != ((Gridded1DDoubleSet) set).getLength(j)) { return false; } } // Sets are immutable, so no need for 'synchronized' double[][] samples = ((Gridded1DDoubleSet) set).getDoubles(false); if (Samples != null && samples != null) { for (j=0; j<DomainDimension; j++) { for (i=0; i<Length; i++) { if (Samples[j][i] != samples[j][i]) { addNotEqualsCache((Set) set); return false; } } } } else { double[][] this_samples = getDoubles(false); if (this_samples == null) { if (samples != null) { return false; } } else if (samples == null) { return false; } else { for (j=0; j<DomainDimension; j++) { for (i=0; i<Length; i++) { if (this_samples[j][i] != samples[j][i]) { addNotEqualsCache((Set) set); return false; } } } } } addEqualsCache((Set) set); return true; } catch (VisADException e) { return false; } } /** * Returns the hash code of this instance. {@link Object#hashCode()} should be * overridden whenever {@link Object#equals(Object)} is. * @return The hash code of this instance (includes the * values). */ public int hashCode() { if (!hashCodeSet) { hashCode = unitAndCSHashCode(); hashCode ^= DomainDimension ^ ManifoldDimension ^ Length; for (int j=0; j<ManifoldDimension; j++) hashCode ^= Lengths[j]; if (Samples != null) for (int j=0; j<DomainDimension; j++) for (int i=0; i<Length; i++) hashCode ^= new Double(Samples[j][i]).hashCode(); hashCodeSet = true; } return hashCode; } public Object clone() { try { return new Gridded1DDoubleSet(Type, Samples, Length, DomainCoordinateSystem, SetUnits, SetErrors); } catch (VisADException e) { throw new VisADError("Gridded1DDoubleSet.clone: " + e.toString()); } } public Object cloneButType(MathType type) throws VisADException { return new Gridded1DDoubleSet(type, Samples, Length, DomainCoordinateSystem, SetUnits, SetErrors); } }
visad
archives: