//
// SampledSet.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;
import java.util.Arrays;
/**
* SampledSet is the abstract superclass of GriddedSets, PolyCells and MultiCells.
* SampledSet objects are intended to be immutable (but see {@link
* #getSamples(boolean)} for an exception).
*/
public abstract class SampledSet extends SimpleSet implements SampledSetIface {
/** */
static int cnt = 0;
/** */
int mycnt = cnt++;
/** */
private static int cacheSizeThreshold = -1;
/** */
private Object cacheId;
/** */
float[][] Samples;
/** */
float Low[], Hi[];
/**
*
*
* @param type
* @param manifold_dimension
*
* @throws VisADException
*/
public SampledSet(MathType type, int manifold_dimension)
throws VisADException {
super(type, manifold_dimension);
}
/**
*
*
* @param type
* @param manifold_dimension
* @param coord_sys
* @param units
* @param errors
*
* @throws VisADException
*/
public SampledSet(MathType type, int manifold_dimension,
CoordinateSystem coord_sys, Unit[] units,
ErrorEstimate[] errors)
throws VisADException {
super(type, manifold_dimension, coord_sys, units, errors);
Low = new float[DomainDimension];
Hi = new float[DomainDimension];
}
/**
*
*
* @param type
*
* @throws VisADException
*/
public SampledSet(MathType type) throws VisADException {
this(type, null, null, null);
}
/**
*
*
* @param type
* @param coord_sys
* @param units
* @param errors
*
* @throws VisADException
*/
public SampledSet(MathType type, CoordinateSystem coord_sys, Unit[] units,
ErrorEstimate[] errors)
throws VisADException {
super(type, coord_sys, units, errors);
Low = new float[DomainDimension];
Hi = new float[DomainDimension];
}
/**
*
*
* @throws Throwable
*/
public void finalize() throws Throwable {
if (cacheId != null) {
// System.err.println ("sampled set finalize");
visad.data.DataCacheManager.getCacheManager().removeFromCache(cacheId);
}
super.finalize();
}
/**
*
*
* @param threshold
*/
public static void setCacheSizeThreshold(int threshold) {
cacheSizeThreshold = threshold;
}
/**
*
*
* @param samples
*/
protected void setMySamples(float[][] samples) {
if (cacheSizeThreshold >= 0 && samples != null && samples.length > 0 &&
samples[0].length > cacheSizeThreshold) {
if (cacheId != null) {
visad.data.DataCacheManager.getCacheManager().updateData(
cacheId, samples);
}
else {
cacheId =
visad.data.DataCacheManager.getCacheManager().addToCache(getClass().getSimpleName(), samples);
}
// visad.data.DataCacheManager.getCacheManager().printStats();
return;
}
this.Samples = samples;
}
/**
*
*
* @return samples
*/
protected float[][] getMySamples() {
if (cacheId != null) {
return visad.data.DataCacheManager.getCacheManager().getFloatArray2D(
cacheId);
}
return Samples;
}
/**
*
*
* @param samples
*
* @throws VisADException
*/
void init_samples(float[][] samples) throws VisADException {
init_samples(samples, true);
}
/**
*
*
* @param samples
* @param copy
*
* @throws VisADException
*/
void init_samples(float[][] samples, boolean copy) throws VisADException {
if (samples.length != DomainDimension) {
throw new SetException("SampledSet.init_samples: " +
"sample dimension " + samples.length +
" doesn't match expected length " +
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("SampledSet.init_samples: " +
"sample#0 length " + samples[0].length +
" doesn't match expected length " + Length);
}
}
// MEM
float[][] mySamples;
if (copy) {
mySamples = new float[DomainDimension][Length];
}
else {
mySamples = samples;
}
for (int j = 0; j < DomainDimension; j++) {
if (samples[j].length != Length) {
throw new SetException("SampledSet.init_samples: " + "sample#" + j +
" length " + samples[j].length +
" doesn't match expected length " + Length);
}
float[] samplesJ = samples[j];
float[] SamplesJ = mySamples[j];
if (copy) {
System.arraycopy(samplesJ, 0, SamplesJ, 0, Length);
}
Low[j] = Float.POSITIVE_INFINITY;
Hi[j] = Float.NEGATIVE_INFINITY;
float sum = 0.0f;
for (int i = 0; i < Length; i++) {
/* WLH 4 May 99
if (SamplesJ[i] != SamplesJ[i]) {
throw new SetException(
"SampledSet.init_samples: sample values cannot be missing");
}
if (Float.isInfinite(SamplesJ[i])) {
throw new SetException(
"SampledSet.init_samples: sample values cannot be infinite");
}
if (SamplesJ[i] < Low[j]) Low[j] = SamplesJ[i];
if (SamplesJ[i] > Hi[j]) Hi[j] = SamplesJ[i];
*/
if (SamplesJ[i] == SamplesJ[i] && !Float.isInfinite(SamplesJ[i])) {
if (SamplesJ[i] < Low[j]) Low[j] = SamplesJ[i];
if (SamplesJ[i] > Hi[j]) Hi[j] = SamplesJ[i];
}
else {
SamplesJ[i] = Float.NaN;
}
sum += SamplesJ[i];
}
if (SetErrors[j] != null) {
SetErrors[j] = new ErrorEstimate(SetErrors[j].getErrorValue(),
sum / Length, Length,
SetErrors[j].getUnit());
}
}
setMySamples(mySamples);
}
/**
*
*
* @param range_select
*/
public void cram_missing(boolean[] range_select) {
float[][] mySamples = getMySamples();
int n = Math.min(range_select.length, mySamples[0].length);
for (int i = 0; i < n; i++) {
if (!range_select[i]) mySamples[0][i] = Float.NaN;
}
setMySamples(mySamples);
}
/**
*
*
* @param samples
*/
void cram_samples(float[][] samples) {
setMySamples(samples);
}
/**
*
*
* @param neighbors
* @param weights
*
* @throws VisADException
*/
public void getNeighbors(int[][] neighbors, float[][] weights)
throws VisADException {
getNeighbors(neighbors);
int n_points;
float distance;
float distance_squared;
float diff;
float lambda_squared;
float constant = 4f;
float pi_squared = (float)(Math.PI * Math.PI);
float[][] mySamples = getMySamples();
float[][] samples = (mySamples != null)
? mySamples
: getSamples();
for (int ii = 0; ii < Length; ii++) {
n_points = neighbors[ii].length;
weights[ii] = new float[n_points];
for (int kk = 0; kk < n_points; kk++) {
distance_squared = 0f;
for (int tt = 0; tt < DomainDimension; tt++) {
diff = samples[tt][ii] - samples[tt][neighbors[ii][kk]];
distance_squared += diff * diff;
}
lambda_squared = (distance_squared * constant) / pi_squared;
weights[ii][kk] = (float)Math.exp((double)(-1f *
(distance_squared / lambda_squared)));
}
}
}
/**
*
*
* @return true or false
*/
public boolean isMissing() {
return (getMySamples() == null);
}
/**
* <p>Returns a copy of the samples of this instance. Element <code>[i][j]
* </code> of the returned array is the <code>j</code>-th value of the
* <code>i</code>-th component.</p>
*
* <p>This method is equivalent to <code>getSamples(true)</code>.</p>
*
* @return A copy of the sample array.
* @see #getSamples(boolean)
*
* @throws VisADException
*/
public float[][] getSamples() throws VisADException {
return getSamples(true);
}
/**
* <p>Returns the samples of this instance or a copy of the samples.</p>
*
* <p>Note that, if the actual sample array is returned, then it is possible
* to modify the values of this instance -- breaking the immutability aspect
* of this class. Don't do this unless you enjoy debugging.</p>
*
* @param copy Whether or not a copy of the sample array
* should be returned.
* @return The sample array is <code>copy</code> is <code>
* false; otherwise, a copy of the sample array.
*
* @throws VisADException
*/
public float[][] getSamples(boolean copy) throws VisADException {
float[][] mySamples = getMySamples();
return copy
? Set.copyFloats(mySamples)
: mySamples;
}
/**
*
*
* @param type
* @param shadow
*
* @return DataShadow
*
* @throws VisADException
*/
public DataShadow computeRanges(ShadowType type, DataShadow shadow)
throws VisADException {
int n = getDimension();
double[][] ranges = new double[2][n];
return computeRanges(type, shadow, ranges, false);
}
/**
*
*
* @param type
* @param shadow
* @param ranges
* @param domain
*
* @return DataShadow
*
* @throws VisADException
*/
public DataShadow computeRanges(ShadowType type, DataShadow shadow,
double[][] ranges, boolean domain)
throws VisADException {
if (isMissing()) return shadow;
setAnimationSampling(type, shadow, domain);
int[] indices = new int[DomainDimension];
for (int i = 0; i < DomainDimension; i++) {
ShadowRealType real = null;
if (type instanceof ShadowSetType) {
real =
(ShadowRealType)((ShadowSetType)type).getDomain().getComponent(i);
}
else if (type instanceof ShadowRealTupleType) {
real = (ShadowRealType)((ShadowRealTupleType)type).getComponent(i);
}
else {
throw new TypeException("SampledSet.computeRanges: bad ShadowType " +
type.getClass().getName());
}
indices[i] = real.getIndex();
}
for (int i = 0; i < DomainDimension; i++) {
int k = indices[i];
double min = Low[i];
double max = Hi[i];
Unit dunit = ((RealType)((SetType)Type).getDomain().getComponent(
i)).getDefaultUnit();
if (dunit != null && !dunit.equals(SetUnits[i])) {
min = dunit.toThis(min, SetUnits[i]);
max = dunit.toThis(max, SetUnits[i]);
}
if (ranges != null) {
ranges[0][i] = min;
ranges[1][i] = max;
}
if (k >= 0 && k < shadow.ranges[0].length) {
shadow.ranges[0][k] = Math.min(shadow.ranges[0][k], min);
shadow.ranges[1][k] = Math.max(shadow.ranges[1][k], max);
}
}
/* WLH 1 March 98 - moved from FieldImpl and FlatField computeRanges */
ShadowRealTupleType domain_type = null;
if (type instanceof ShadowRealTupleType) {
domain_type = (ShadowRealTupleType)type;
}
else if (type instanceof ShadowSetType) {
domain_type = ((ShadowSetType)type).getDomain();
}
if (domain_type != null && ranges != null) {
ShadowRealTupleType shad_ref = domain_type.getReference();
if (shad_ref != null) {
// computeRanges for Reference (relative to domain) RealTypes
// WLH 20 Nov 2001
shadow = computeReferenceRanges(
domain_type, DomainCoordinateSystem,
((SetType)Type).getDomain().getDefaultUnits(), shadow, shad_ref,
ranges);
// SetUnits, shadow, shad_ref, ranges);
}
}
return shadow;
}
/**
* create a 1-D GeometryArray from this Set and color_values;
* only used by Irregular3DSet and Gridded3DSet
*
* @param color_values
*
* @return new VisADGeometryArray
*
* @throws VisADException
*/
public VisADGeometryArray make1DGeometry(byte[][] color_values)
throws VisADException {
if (DomainDimension != 3) {
throw new SetException("SampledSet.make1DGeometry: " +
"DomainDimension must be 3, not " +
DomainDimension);
}
if (ManifoldDimension != 1) {
throw new SetException("SampledSet.make1DGeometry: " +
"ManifoldDimension must be 1, not " +
ManifoldDimension);
}
VisADGeometryArray array = null;
if (Length == 0) {
return null;
}
else if (Length == 1) {
array = new VisADPointArray();
}
else {
array = new VisADLineStripArray();
((VisADLineStripArray)array).stripVertexCounts = new int[1];
((VisADLineStripArray)array).stripVertexCounts[0] = Length;
}
// set coordinates and colors
setGeometryArray(array, 4, color_values);
return array;
}
/**
* create a 3-D GeometryArray from this Set and color_values;
* NOTE - this version only makes points;
* NOTE - when textures are supported by Java3D the Gridded3DSet
* implementation of make3DGeometry should use Texture3D, and
* the Irregular3DSet implementation should resample to a
* Gridded3DSet and use Texture3D;
* only used by Irregular3DSet and Gridded3DSet
*
* @param color_values
*
* @return arrays of VisADGeometryArray
*
* @throws VisADException
*/
public VisADGeometryArray[] make3DGeometry(byte[][] color_values)
throws VisADException {
if (ManifoldDimension != 3) {
throw new SetException("SampledSet.make3DGeometry: " +
"ManifoldDimension must be 3, not " +
ManifoldDimension);
}
VisADGeometryArray array = makePointGeometry(color_values);
return new VisADGeometryArray[] {array, array, array};
}
/**
* create a PointArray from this Set and color_values;
* can be applied to ManifoldDimension = 1, 2 or 3
*
* @param color_values
*
* @return VisADGeometryArray
*
* @throws VisADException
*/
public VisADGeometryArray makePointGeometry(byte[][] color_values)
throws VisADException {
if (DomainDimension != 3) {
throw new SetException("SampledSet.makePointGeometry: " +
"DomainDimension must be 3, not " +
DomainDimension);
}
VisADPointArray array = new VisADPointArray();
// set coordinates and colors
setGeometryArray(array, 4, color_values);
return array;
}
/**
* copy and transpose Samples (from this Set( and color_values
* into array; if color_length == 3 don't use color_values[3]
*
* @param array
* @param color_length
* @param color_values
*
* @throws VisADException
*/
public void setGeometryArray(VisADGeometryArray array, int color_length,
byte[][] color_values)
throws VisADException {
setGeometryArray(array, getSamples(false), color_length, color_values);
}
/**
* copy and transpose samples and color_values into array;
* if color_length == 3 don't use color_values[3]
*
* @param array
* @param samples
* @param color_length
* @param color_values
*
* @throws VisADException
*/
public static void setGeometryArray(VisADGeometryArray array,
float[][] samples, int color_length,
byte[][] color_values)
throws VisADException {
if (samples == null) {
throw new SetException("SampledSet.setGeometryArray: " +
"Null samples array");
}
else if (samples.length != 3) {
throw new SetException("SampledSet.setGeometryArray: " +
"Expected 3 dimensions in samples array, not " +
samples.length);
}
int len = samples[0].length;
array.vertexCount = len;
// MEM
float[] coordinates = new float[3 * len];
int j = 0;
for (int i = 0; i < len; i++) {
coordinates[j++] = samples[0][i];
coordinates[j++] = samples[1][i];
coordinates[j++] = samples[2][i];
}
array.coordinates = coordinates;
if (color_values != null) {
color_length = Math.min(color_length, color_values.length);
// MEM
byte[] colors = new byte[color_length * len];
j = 0;
if (color_length == 4) {
for (int i = 0; i < len; i++) {
colors[j++] = color_values[0][i];
colors[j++] = color_values[1][i];
colors[j++] = color_values[2][i];
colors[j++] = color_values[3][i];
// colors[j++] = (1.0f - color_values[3][i]);
}
}
else if (color_length == 3) {
for (int i = 0; i < len; i++) {
colors[j++] = color_values[0][i];
colors[j++] = color_values[1][i];
colors[j++] = color_values[2][i];
}
}
// BMF 2009-03-17
// this addresses a issue where a 2D display is used without color
// mapping
else if (color_length == 0) {
colors = null;
}
else {
throw new SetException("SampledSet.setGeometryArray: " +
"color_length must be 0, 3 or 4, not " +
color_length);
}
array.colors = colors;
}
}
/**
*
*
* @return array of low values
*/
public float[] getLow() {
float[] low = new float[Low.length];
for (int i = 0; i < Low.length; i++)
low[i] = Low[i];
return low;
}
/**
*
*
* @return array of hi values
*/
public float[] getHi() {
float[] hi = new float[Hi.length];
for (int i = 0; i < Hi.length; i++)
hi[i] = Hi[i];
return hi;
}
/**
* Clones this instance.
*
* @return A clone of this instance.
*/
public Object clone() {
SampledSet clone = (SampledSet)super.clone();
if (clone.cacheId != null) {
clone.cacheId = null;
}
/*
* The array of sample values is cloned because getSamples(false) allows
* clients to modify the values and the clone() general contract forbids
* cross-clone effects.
*/
float[][] mySamples = getMySamples();
if (mySamples != null) {
clone.setMySamples(visad.util.Util.clone(mySamples));
}
return clone;
}
}