// // LinearNDSet.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; /** LinearNDSet represents a finite set of samples of R^Dimension in a cross product of arithmetic progressions.<P> 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.<P> */ public class LinearNDSet extends GriddedSet implements LinearSet { Linear1DSet[] L; private boolean cacheSamples = false; /** * Construct an N-dimensional set as the product of N Linear1DSets, * with null errors, CoordinateSystem and Units are defaults from * type. * @param type MathType for this LinearNDSet. Must be consistent * with MathType-s of sets. * @param l Linear1DSets that make up this LinearNDSet. * @throws VisADException problem creating VisAD objects. */ public LinearNDSet(MathType type, Linear1DSet[] l) throws VisADException { this(type, l, null, null, null); } /** * Construct an N-dimensional set as the product of N arithmetic * progressions (lengths[i] samples between firsts[i] and lasts[i]), * with null errors, CoordinateSystem and Units are defaults from type * @param type MathType for this LinearNDSet. * @param firsts array of first values for each of the * arithmetic progressions * @param lasts array of last values for each of the * arithmetic progressions * @param lengths array of number of samples for each of the * arithmetic progressions * @throws VisADException problem creating VisAD objects. */ public LinearNDSet(MathType type, double[] firsts, double[] lasts, int[] lengths) throws VisADException { this(type, get_linear1d_array(type, firsts, lasts, lengths, null), null, null, null); } /** * Construct an N-dimensional set as the product of N arithmetic * progressions (lengths[i] samples between firsts[i] and lasts[i]), * coordinate_system and units must be compatible with defaults for * type, or may be null; errors may be null. * @param type MathType for this LinearNDSet. * @param firsts array of first values for each of the * arithmetic progressions * @param lasts array of last values for each of the * arithmetic progressions * @param lengths array of number of samples for each of the * arithmetic progressions * @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 not null. * @param errors ErrorEstimate-s for values in <code>sets</code>, * may be null * @throws VisADException problem creating VisAD objects. */ public LinearNDSet(MathType type, double[] firsts, double[] lasts, int[] lengths, CoordinateSystem coord_sys, Unit[] units, ErrorEstimate[] errors) throws VisADException { this(type, firsts, lasts, lengths, coord_sys, units, errors, false); } /** * Construct an N-dimensional set as the product of N arithmetic * progressions (lengths[i] samples between firsts[i] and lasts[i]), * coordinate_system and units must be compatible with defaults for * type, or may be null; errors may be null. * @param type MathType for this LinearNDSet. * @param firsts array of first values for each of the * arithmetic progressions * @param lasts array of last values for each of the * arithmetic progressions * @param lengths array of number of samples for each of the * arithmetic progressions * @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 not null. * @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 LinearNDSet(MathType type, double[] firsts, double[] lasts, int[] lengths, CoordinateSystem coord_sys, Unit[] units, ErrorEstimate[] errors, boolean cache) throws VisADException { this(type, get_linear1d_array(type, firsts, lasts, lengths, units), coord_sys, units, errors, cache); } /** * Construct an N-dimensional set as the product of N Linear1DSets; * coordinate_system and units must be compatible with defaults * for type, or may be null; errors may be null * @param type MathType for this LinearNDSet. Must be consistent * with MathType-s of sets. * @param l Linear1DSets that make up this LinearNDSet. * @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 not null. * @param errors ErrorEstimate-s for values in <code>sets</code>, * may be null * @throws VisADException problem creating VisAD objects. */ public LinearNDSet(MathType type, Linear1DSet[] l, CoordinateSystem coord_sys, Unit[] units, ErrorEstimate[] errors) throws VisADException { this(type, l, coord_sys, units, errors, false); } /** * Construct an N-dimensional set as the product of N Linear1DSets; * coordinate_system and units must be compatible with defaults * for type, or may be null; errors may be null * @param type MathType for this LinearNDSet. Must be consistent * with MathType-s of sets. * @param l Linear1DSets that make up this LinearNDSet. * @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 not null. * @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 LinearNDSet(MathType type, Linear1DSet[] l, CoordinateSystem coord_sys, Unit[] units, ErrorEstimate[] errors, boolean cache) throws VisADException { super(type, null, get_lengths(l), coord_sys, LinearNDSet.units_array_linear1d(l, units), errors); if (DomainDimension != ManifoldDimension) { throw new SetException("LinearNDSet: DomainDimension != ManifoldDimension"); } L = LinearNDSet.linear1d_array_units(l, units); for (int j=0; j<DomainDimension; j++) { Low[j] = L[j].getLowX(); Hi[j] = L[j].getHiX(); if (SetErrors[j] != null ) { SetErrors[j] = new ErrorEstimate(SetErrors[j].getErrorValue(), (Low[j] + Hi[j]) / 2.0, Length, SetErrors[j].getUnit()); } } cacheSamples = cache; } private static int[] get_lengths(Linear1DSet[] l) throws VisADException { // used by LinearNDSet constructor int[] lengths = new int[l.length]; for (int j=0; j<l.length; j++) lengths[j] = l[j].getLength(); return lengths; } /** * General Factory method for creating the proper linear set * (Linear1DSet, Linear2DSet, etc.). */ public static LinearSet create(MathType type, double[] firsts, double[] lasts, int[] lengths) throws VisADException { return create(type, firsts, lasts, lengths, null, null, null); } public static LinearSet create(MathType type, double[] firsts, double[] lasts, int[] lengths, CoordinateSystem coord_sys, Unit[] units, ErrorEstimate[] errors) throws VisADException { switch (firsts.length) { case 1: return new Linear1DSet(type, firsts[0], lasts[0], lengths[0], coord_sys, units, errors); case 2: return new Linear2DSet(type, firsts[0], lasts[0], lengths[0], firsts[1], lasts[1], lengths[1], coord_sys, units, errors); case 3: return new Linear3DSet(type, firsts[0], lasts[0], lengths[0], firsts[1], lasts[1], lengths[1], firsts[2], lasts[2], lengths[2], coord_sys, units, errors); default: return new LinearNDSet(type, firsts, lasts, lengths, coord_sys, units, errors); } } static Linear1DSet[] linear1d_array_units(Linear1DSet[] sets, Unit[] units) throws VisADException { if (units == null) return sets; int n = sets.length; if (units.length != n) { throw new SetException("units and sets lengths don't match"); } Linear1DSet[] ss = new Linear1DSet[n]; for (int i=0; i<n; i++) { Unit[] su = sets[i].getSetUnits(); if (units[i] == null || units[i].equals(su[0])) { ss[i] = sets[i]; } else { CoordinateSystem cs = sets[i].getCoordinateSystem(); double first = sets[i].getFirst(); double last = sets[i].getLast(); if (su[0] != null) { first = units[i].toThis(first, su[0]); last = units[i].toThis(last, su[0]); } su = new Unit[] {units[i]}; ss[i] = new Linear1DSet(sets[i].getType(), first, last, sets[i].getLength(), sets[i].getCoordinateSystem(), su, sets[i].getSetErrors()); } } return ss; } static Unit[] units_array_linear1d(Linear1DSet[] sets, Unit[] units) throws VisADException { int n = sets.length; Unit[] newu = new Unit[n]; if (units != null && units.length != n) { throw new SetException("units and sets lengths don't match"); } for (int i=0; i<n; i++) { if (units != null && units[i] != null) { newu[i] = units[i]; } else { Unit[] su = sets[i].getSetUnits(); if (su != null) newu[i] = su[0]; } } return newu; } static Linear1DSet[] get_linear1d_array(MathType type, double[] firsts, double[] lasts, int[] lengths, Unit[] units) throws VisADException { // used by LinearNDSet and IntegerNDSet constructors type = Set.adjustType(type); int len = lengths.length; if (len != firsts.length || len != lasts.length) { throw new SetException("LinearNDSet: array dimensions don't match"); } Linear1DSet[] l = new Linear1DSet[len]; for (int j=0; j<len; j++) { RealType[] types = {(RealType) ((SetType) type).getDomain().getComponent(j)}; SetType set_type = new SetType(new RealTupleType(types)); Unit[] us = {null}; if (units != null && units.length > j) us[0] = units[j]; l[j] = new Linear1DSet(set_type, firsts[j], lasts[j], lengths[j], null, us, null); } return l; } static Linear1DSet[] get_linear1d_array(MathType type, double first1, double last1, int length1, double first2, double last2, int length2, Unit[] units) throws VisADException { double[] firsts = {first1, first2}; double[] lasts = {last1, last2}; int[] lengths = {length1, length2}; return get_linear1d_array(type, firsts, lasts, lengths, units); } static Linear1DSet[] get_linear1d_array(MathType type, double first1, double last1, int length1, double first2, double last2, int length2, double first3, double last3, int length3, Unit[] units) throws VisADException { double[] firsts = {first1, first2, first3}; double[] lasts = {last1, last2, last3}; int[] lengths = {length1, length2, length3}; return get_linear1d_array(type, firsts, lasts, lengths, units); } /** convert an array of 1-D indices to an array of values in R^DomainDimension */ public float[][] indexToValue(int[] index) throws VisADException { int dim = getDimension(); int length = index.length; int[][] indexN = new int[dim][length]; float[][] values = new float[dim][]; int[] lengthN = new int[dim]; for (int j=0; j<dim; j++) lengthN[j] = L[j].getLength(); for (int i=0; i<length; i++) { int k = index[i]; if (0 <= k && k < Length) { for (int j=0; j<dim-1; j++) { indexN[j][i] = k % lengthN[j]; k = k / lengthN[j]; } indexN[dim-1][i] = k; } else { for (int j=0; j<dim; j++) indexN[j][i] = -1; } } for (int j=0; j<dim; j++) { float[][] vals = L[j].indexToValue(indexN[j]); values[j] = vals[0]; } return values; } /** transform an array of non-integer grid coordinates to an array of values in R^DomainDimension */ public float[][] gridToValue(float[][] grid) throws VisADException { int j; if (grid.length != DomainDimension) { throw new SetException("LinearNDSet.gridToValue: grid dimension " + grid.length + " not equal to Domain dimension " + DomainDimension); } if (Length > 1) { for (j=0; j<DomainDimension; j++) { if (Lengths[j] < 2) { throw new SetException("LinearNDSet.gridToValue: requires all grid " + "dimensions to be > 1"); } } } int length = grid[0].length; float[][][] gridJ = new float[DomainDimension][1][]; float[][][] valueJ = new float[DomainDimension][][]; for (j=0; j<DomainDimension; j++) { gridJ[j][0] = grid[j]; valueJ[j] = L[j].gridToValue(gridJ[j]); } float[][] value = new float[DomainDimension][]; for (j=0; j<DomainDimension; j++) value[j] = valueJ[j][0]; return value; } /** transform an array of values in R^DomainDimension to an array of non-integer grid coordinates */ public float[][] valueToGrid(float[][] value) throws VisADException { int j; if (value.length != DomainDimension) { throw new SetException("LinearNDSet.valueToGrid: value dimension " + value.length + " not equal to Domain dimension " + DomainDimension); } if (Length > 1) { for (j=0; j<DomainDimension; j++) { if (Lengths[j] < 2) { throw new SetException("LinearNDSet.valueToGrid: requires all grid " + "dimensions to be > 1"); } } } int length = value[0].length; float[][][] valueJ = new float[DomainDimension][1][]; float[][][] gridJ = new float[DomainDimension][][]; for (j=0; j<DomainDimension; j++) { valueJ[j][0] = value[j]; gridJ[j] = L[j].valueToGrid(valueJ[j]); } float[][] grid = new float[DomainDimension][]; for (j=0; j<DomainDimension; j++) grid[j] = gridJ[j][0]; return grid; } /** * Check to see if this is an empty cross-product. * @return always false. */ public boolean isMissing() { return false; } /** * Get the array of first values of each of the arithmetic progressions * in this cross product. * @return array of first values. */ public double[] getFirsts() throws VisADException { double[] firsts = new double[L.length]; for (int j=0; j<firsts.length; j++) firsts[j] = L[j].getFirst(); return firsts; } /** * Get the array of last values of each of the arithmetic progressions * in this cross product. * @return array of last values. */ public double[] getLasts() throws VisADException { double[] lasts = new double[L.length]; for (int j=0; j<lasts.length; j++) lasts[j] = L[j].getLast(); return lasts; } /** * Return the array of values in R^N space corresponding to * this cross product of arithmetic progressions. * @param copy if true, return a copy of the samples. * @return array of values in R^N space. * @throws VisADException problem creating samples. */ public float[][] getSamples(boolean copy) throws VisADException { 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; } private float[][] makeSamples() throws VisADException { 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); } /** * Check to see if this LinearNDSet is equal to the Object * in question. * @param set Object in question * @return true if <code>set</code> is a LinearNDSet and each * of the Linear1DSet-s that make up this cross product * are equal. */ public boolean equals(Object set) { if (!(set instanceof LinearNDSet) || set == null) return false; if (this == set) return true; if (!equalUnitAndCS((Set) set)) return false; if (DomainDimension != ((LinearNDSet) set).getDimension()) return false; for (int j=0; j<DomainDimension; j++) { if (!L[j].equals(((LinearNDSet) set).getLinear1DComponent(j))) return false; } return true; } /** * Returns the hash code for this instance. * @return The hash code for this instance. */ public int hashCode() { if (!hashCodeSet) { hashCode = unitAndCSHashCode(); for (int i = 0; i < DomainDimension; ++i) hashCode ^= L[i].hashCode(); hashCodeSet = true; } return hashCode; } /** * Get the indexed component. * * @param i Index of component * @return The requested component * @exception ArrayIndexOutOfBoundsException If an invalid index is * specified. */ public Linear1DSet getLinear1DComponent(int i) { return L[i]; } /** * Return a clone of this object with a new MathType. * @param type new MathType. * @return new LinearNDSet with <code>type</code>. * @throws VisADException if <code>type</code> is not compatible * with MathType of component Linear1DSets. */ public Object cloneButType(MathType type) throws VisADException { Linear1DSet[] l = new Linear1DSet[DomainDimension]; for (int j=0; j<DomainDimension; j++) { l[j] = (Linear1DSet) L[j].clone(); } return new LinearNDSet(type, l, DomainCoordinateSystem, SetUnits, SetErrors, cacheSamples); } /** * Extended version of the toString() method. * @param pre prefix for string. * @return wordy string describing this LinearNDSet. */ public String longString(String pre) throws VisADException { String s = pre + "LinearNDSet: Dimension = " + DomainDimension + " Length = " + Length + "\n"; for (int j=0; j<DomainDimension; j++) { s = s + pre + " Dimension " + j + ":" + " Length = " + L[j].getLength() + " Range = " + L[j].getFirst() + " to " + L[j].getLast() + "\n"; } return s; } /** run 'java visad.LinearNDSet' to test the LinearNDSet class */ public static void main(String args[]) throws VisADException { RealTupleType type = new RealTupleType(RealType.Generic, RealType.Generic); double[] firsts = {0.0, 0.0}; double[] lasts = {3.0, 3.0}; int[] lengths = {4, 4}; LinearNDSet set = new LinearNDSet(type, firsts, lasts, lengths); float[][] values = set.getSamples(); int n = values.length; int m = values[0].length; for (int j=0; j<m; j++) { for (int i=0; i<n; i++) { System.out.println("values[" + i + "][" + j + "] = " + values[i][j]); } } } }