//
// DataUtility.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.util;
// General Java
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.ColorModel;
import java.awt.image.DirectColorModel;
import java.awt.image.MemoryImageSource;
import java.awt.image.PixelGrabber;
import java.io.IOException;
import java.util.ArrayList;
import java.util.TreeSet;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Comparator;
import java.util.Vector;
import java.util.StringTokenizer;
// RMI classes
import java.rmi.RemoteException;
import visad.java2d.DisplayImplJ2D;
import visad.java3d.DisplayImplJ3D;
import visad.java3d.TwoDDisplayRendererJ3D;
// GUI handling
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.*;
// VisAD packages
import visad.*;
/** A general utility class for VisAD Data objects */
public class DataUtility {
private static boolean init = false;
private static FunctionType simpleImageType;
private static RealType radiance;
private static RealTupleType imageDomain;
private static RealType line;
private static RealType element;
private int num_lines, num_elements;
private static synchronized void makeTypes()
throws VisADException {
if (!init) {
init = true;
simpleImageType = (FunctionType)
MathType.stringToType("((ImageElement, ImageLine) -> ImageRadiance)");
imageDomain = simpleImageType.getDomain();
line = (RealType) imageDomain.getComponent(1);
element = (RealType) imageDomain.getComponent(0);
MathType range = simpleImageType.getRange();
if (range instanceof RealType) {
radiance = (RealType) range;
}
else {
radiance = (RealType) ((RealTupleType) range).getComponent(0);
}
}
}
/** return a FlatField for a simple image from
values[nlines][nelements] */
public static FlatField makeImage(float[][] values)
throws VisADException, RemoteException {
if (values == null) return null;
int nlines = values.length;
int nelements = 0;
for (int l=0; l<nlines; l++) {
if (values[l] != null) {
if (values[l].length > nelements) nelements = values[l].length;
}
}
if (!init) makeTypes();
FlatField image = new FlatField(simpleImageType,
new Integer2DSet(imageDomain, nelements, nlines));
setPixels(image, values);
return image;
}
/** set pixel values in a simple image,
indexed as values[line][element] */
public static void setPixels(FlatField image, float[][] values)
throws VisADException, RemoteException {
if (values == null) return;
Integer2DSet set = (Integer2DSet) image.getDomainSet();
int num_elements = set.getLength(0);
int num_lines = set.getLength(1);
float[][] vals = new float[1][num_lines * num_elements];
for (int i=0; i<num_lines * num_elements; i++) {
vals[0][i] = Float.NaN;
}
int nl = values.length;
if (num_lines < nl) nl = num_lines;
int base = 0;
for (int l=0; l<nl; l++) {
if (values[l] != null) {
int ne = values[l].length;
if (num_elements < ne) ne = num_elements;
for (int e=0; e<ne; e++) {
vals[0][base + e] = values[l][e];
}
}
base += num_elements;
}
image.setSamples(vals, false); // no need to copy
}
public static float[][] getPixels(FlatField image)
throws VisADException, RemoteException {
Integer2DSet set = (Integer2DSet) image.getDomainSet();
int num_elements = set.getLength(0);
int num_lines = set.getLength(1);
float[][] values = new float[num_lines][num_elements];
double[][] vals = image.getValues();
int base = 0;
for (int l=0; l<num_lines; l++) {
for (int e=0; e<num_elements; e++) {
values[l][e] = (float) vals[0][base + e];
}
base += num_elements;
}
return values;
}
/**
* Create a VisAD Data object from the given Image
* @param image image to use
* @return a FlatField representation of the image
*/
public static FlatField makeField(Image image)
throws IOException, VisADException
{
return makeField(image, false);
}
/**
* Create a VisAD Data object from the given Image
* @param image image to use
* @param withAlpha include Alpha in the field if the image ColorModel
* supports it and the image is not opaque.
* @return a FlatField representation of the image
*/
public static FlatField makeField(Image image, boolean withAlpha)
throws IOException, VisADException
{
if (image == null) {
throw new VisADException("image cannot be null");
}
ImageHelper ih = new ImageHelper();
// determine image height and width
int width = -1;
int height = -1;
do {
if (width < 0) width = image.getWidth(ih);
if (height < 0) height = image.getHeight(ih);
if (ih.badImage || (width >= 0 && height >= 0)) break;
try { Thread.sleep(100); } catch (InterruptedException e) { }
}
while (true);
if (ih.badImage) throw new IOException("Not an image");
// extract image pixels
int numPixels = width * height;
int[] words = new int[numPixels];
PixelGrabber grabber = new PixelGrabber(
image.getSource(), 0, 0, width, height, words, 0, width);
try { grabber.grabPixels(); }
catch (InterruptedException e) { }
ColorModel cm = grabber.getColorModel();
boolean doAlpha = cm.hasAlpha() && withAlpha;
//System.out.println("do alpha: " + doAlpha);
float[] red_pix = new float[numPixels];
float[] green_pix = new float[numPixels];
float[] blue_pix = new float[numPixels];
float[] alpha_pix = null;
for (int i=0; i<numPixels; i++) {
red_pix[i] = cm.getRed(words[i]);
green_pix[i] = cm.getGreen(words[i]);
blue_pix[i] = cm.getBlue(words[i]);
}
boolean opaque = true;
if (doAlpha) {
alpha_pix = new float[numPixels];
for (int i=0; i<numPixels; i++) {
alpha_pix[i] = cm.getAlpha(words[i]);
if (alpha_pix[i] != 255.0f) opaque = false;
}
}
if (opaque && doAlpha) { // if opaque, don't include alpha
doAlpha = false;
alpha_pix = null;
}
//System.out.println("opaque = " + opaque);
// build FlatField
RealType line = RealType.getRealType("ImageLine");
RealType element = RealType.getRealType("ImageElement");
RealType c_red = RealType.getRealType("Red");
RealType c_green = RealType.getRealType("Green");
RealType c_blue = RealType.getRealType("Blue");
RealType c_alpha = RealType.getRealType("Alpha");
RealType[] c_all =
(doAlpha) ? new RealType[] {c_red, c_green, c_blue, c_alpha}
: new RealType[] {c_red, c_green, c_blue};
RealTupleType radiance = new RealTupleType(c_all);
RealType[] domain_components = {element, line};
RealTupleType image_domain = new RealTupleType(domain_components);
Linear2DSet domain_set = new Linear2DSet(image_domain, 0.0,
(float) (width - 1.0), width, (float) (height - 1.0), 0.0, height);
FunctionType image_type = new FunctionType(image_domain, radiance);
FlatField field = new FlatField(image_type, domain_set);
float[][] samples = new float[doAlpha?4:3][];
samples[0] = red_pix;
samples[1] = green_pix;
samples[2] = blue_pix;
if (doAlpha) samples[3] = alpha_pix;
try { field.setSamples(samples, false); }
catch (RemoteException e) {
throw new VisADException("Couldn't finish image initialization");
}
return field;
}
/**
* Converts a flat field of the form <tt>((x, y) -> (r, g, b))</tt>
* to an AWT Image. If reverse flag is set, image will be upside-down.
*/
public static Image extractImage(FlatField field, boolean reverse) {
try {
GriddedSet set = (GriddedSet) field.getDomainSet();
int[] wh = set.getLengths();
int w = wh[0];
int h = wh[1];
double[][] samples = field.getValues();
int[] pixels = new int[samples[0].length];
if (samples.length == 3) {
int len = samples[0].length;
for (int i=0; i<len; i++) {
int r = (int) samples[0][i] & 0x000000ff;
int g = (int) samples[1][i] & 0x000000ff;
int b = (int) samples[2][i] & 0x000000ff;
int index = reverse ? len - i - 1 : i;
pixels[index] = r << 16 | g << 8 | b;
}
}
else {
int len = samples[0].length;
for (int i=0; i<len; i++) {
int v = (int) samples[0][i] & 0x000000ff;
int index = reverse ? len - i - 1 : i;
pixels[index] = v << 16 | v << 8 | v;
}
}
MemoryImageSource source = new MemoryImageSource(w, h,
new DirectColorModel(24, 0xff0000, 0xff00, 0xff), pixels, 0, w);
source.setFullBufferUpdates(true);
return Toolkit.getDefaultToolkit().createImage(source);
}
catch (VisADException exc) {
return null;
}
}
public static FlatField[] getImageFields(Data data) {
FlatField[] fields = null;
String pcImageType = "((e, l) -> v)";
String rgbImageType = "((e, l) -> (r, g, b))";
String pcTimeType = "(t -> " + pcImageType + ")";
String rgbTimeType = "(t -> " + rgbImageType + ")";
try {
MathType type = data.getType();
if (type.equalsExceptName(MathType.stringToType(pcTimeType)) ||
type.equalsExceptName(MathType.stringToType(rgbTimeType)))
{
FieldImpl fi = (FieldImpl) data;
int len = fi.getLength();
fields = new FlatField[len];
for (int i=0; i<len; i++) fields[i] = (FlatField) fi.getSample(i);
}
else if (type.equalsExceptName(MathType.stringToType(pcImageType)) ||
type.equalsExceptName(MathType.stringToType(rgbImageType)))
{
fields = new FlatField[1];
fields[0] = (FlatField) data;
}
}
catch (VisADException exc) { }
catch (RemoteException exc) { }
return fields;
}
public static DisplayImpl makeSimpleDisplay(DataImpl data)
throws VisADException, RemoteException {
boolean three_d = true;
DisplayImpl display = null;
try {
display = new DisplayImplJ3D("simple data display");
}
catch (UnsatisfiedLinkError e) {
display = new DisplayImplJ2D("simple data display");
three_d = false;
}
MathType type = data.getType();
ScalarMap[] maps = type.guessMaps(three_d);
if (maps == null) {
display.stop();
return null;
}
if (three_d) {
boolean only_2d = true;
for (int i=0; i<maps.length; i++) {
DisplayRealType dtype = maps[i].getDisplayScalar();
if (Display.ZAxis.equals(maps[i]) ||
Display.Latitude.equals(maps[i])) {
only_2d = false;
break;
}
}
if (only_2d) {
display.destroy();
display = new DisplayImplJ3D("simple data display",
new TwoDDisplayRendererJ3D());
}
}
for (int i=0; i<maps.length; i++) {
display.addMap(maps[i]);
}
DataReferenceImpl ref = new DataReferenceImpl("simple data display");
ref.setData(data);
display.addReference(ref);
return display;
}
public static void main(String[] argv)
throws VisADException, RemoteException {
float[][] pixels = new float[64][64];
for (int i=0; i<64; i++) {
for (int j=0; j<64; j++) {
pixels[i][j] = i * (i - 32) * (i - 64) *
j * (j - 32) * (j - 64) + 100000;
}
}
FlatField image = DataUtility.makeImage(pixels);
DisplayImpl display = DataUtility.makeSimpleDisplay(image);
JFrame jframe = new JFrame("SimplImage.main");
jframe.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);}
});
jframe.setContentPane((JPanel) display.getComponent());
jframe.pack();
jframe.setVisible(true);
}
/**
* Ensures that a MathType is a RealTupleType. Converts if necessary.
* @param type The math type to be "converted" to a
* RealTupleType. It shall be either a RealType,
* a RealTupleType, or a SetType.
* @return The RealTupleType version of <code>type</code>.
* If <code>type</code> is a RealTupleType, then
* it is returned; otherwise, if <code>type</code>
* is a RealType, then a RealTupleType
* containing <code>type</code> as the
* only component is returned; otherwise,
* if <code>type</code> is a SetType, then
* <code>((SetType)type).getDomain()</code> is
* returned.
* @throws TypeException <code>type</code> is the wrong type: it can't
* be converted into a RealTupleType.
* @throws VisADException Couldn't create necessary VisAD object.
*/
public static RealTupleType
ensureRealTupleType(MathType type)
throws TypeException, VisADException
{
RealTupleType result;
if (type instanceof RealTupleType)
result = (RealTupleType)type;
else if (type instanceof RealType)
result = new RealTupleType((RealType)type);
else if (type instanceof SetType)
result = ((SetType)type).getDomain();
else
throw new TypeException(
DataUtility.class.getName() +
".ensureRealTupleType(MathType): Can't convert MathType \"" +
type + "\" into a RealTupleType");
return result;
}
/**
* Ensures that a MathType is a TupleType. Converts if necessary.
* @param type The math type to be "converted" to a TupleType.
* @return The TupleType version of <code>type</code>.
* If <code>type</code> is a TupleType,
* then it is returned; otherwise, if
* <code>type</code> is a SetType, then
* <code>((SetType)type).getDomain()</code> is
* returned; otherwise, a TupleType containing
* <code>type</code> as the only component is
* returned (if <code>type</code> is a RealType,
* then the returned TupleType is a RealTupleType);
* @throws VisADException Couldn't create necessary VisAD object.
*/
public static TupleType
ensureTupleType(MathType type)
throws VisADException
{
return
type instanceof TupleType
? (TupleType)type
: type instanceof SetType
? ((SetType)type).getDomain() // actually a RealTupleType
: type instanceof RealType
? new RealTupleType((RealType)type)
: new TupleType(new MathType[] {type});
}
/**
* Ensures that a Data is a Tuple. Creates a Tuple if necessary.
* @param datum The math type to be "converted" to a Tuple.
* @return The Tuple version of <code>datum</code>. If
* <code>datum</code> is a Tuple, then it is
* returned; otherwise, if <code>datum</code> is
* a Real, then a RealTuple containing <code>
* datum</code> as the only component is returned;
* otherwise, a Tuple containing <code>datum</code>
* as the only component is returned.
* @throws VisADException Couldn't create necessary VisAD object.
* @throws RemoteException Java RMI failure.
*/
public static TupleIface
ensureTuple(Data datum)
throws VisADException, RemoteException
{
return
datum instanceof TupleIface
? (TupleIface)datum
: datum instanceof Real
? new RealTuple(new Real[] {(Real)datum})
: new Tuple(new Data[] {datum});
}
/**
* Gets the MathType of the domain of a Set.
* @param set A set.
* @return The MathType of the domain of the Set.
*/
public static RealTupleType
getDomainType(Set set)
{
return ((SetType)set.getType()).getDomain();
}
/**
* Gets the MathType of the domain of a Function.
* @param function A function.
* @return The MathType of the domain of the function.
* @throws VisADException Couldn't create necessary VisAD object.
* @throws RemoteException Java RMI failure.
*/
public static RealTupleType
getDomainType(Function function)
throws VisADException, RemoteException
{
return ((FunctionType)function.getType()).getDomain();
}
/**
* Gets the MathType of the range of a Function.
* @param function A function.
* @return The MathType of the range of the function.
* @throws VisADException Couldn't create necessary VisAD object.
* @throws RemoteException Java RMI failure.
*/
public static MathType
getRangeType(Function function)
throws VisADException, RemoteException
{
return ((FunctionType)function.getType()).getRange();
}
/**
* Gets the TupleType of the range of a Function.
* @param function A function.
* @return The TupleType of the range of the function.
* @throws VisADException Couldn't create necessary VisAD object.
* @throws RemoteException Java RMI failure.
*/
public static TupleType
getRangeTupleType(Function function)
throws VisADException, RemoteException
{
return ensureTupleType(getRangeType(function));
}
/**
* Gets the MathType of the flat-range of a Function.
* @param function A function.
* @return The MathType of the flat-range of the function.
* @throws VisADException Couldn't create necessary VisAD object.
* @throws RemoteException Java RMI failure.
*/
public static RealTupleType
getFlatRangeType(Function function)
throws VisADException, RemoteException
{
return ((FunctionType)function.getType()).getFlatRange();
}
/**
* Gets the number of components in the range of a Function. NB: This differs
* from visad.FlatField.getRangeDimension() in that it returns the number of
* components in the actual range rather than the number of components in the
* flat range.
* @param function A function.
* @return The number of components in the range of the
* function.
* @throws VisADException Couldn't create necessary VisAD object.
* @throws RemoteException Java RMI failure.
*/
public static int
getRangeDimension(Function function)
throws VisADException, RemoteException
{
return getRangeTupleType(function).getDimension();
}
/**
* Gets the index of a component in a TupleType. If the TupleType
* contains multiple instances of the component, then it is unspecified
* which component index is returned. This method first looks for an
* exact match via the <code>equals(Object)</code> method. If none is
* found, then this method looks for an approximate match based on the
* <code>equalsExceptNameButUnits(MathType)</code> method.
* @param tupleType The type of the tuple.
* @param componentType The MathType of the component.
* @return The index of the component in the tuple
* or -1 if the component is not in the tuple.
* @throws VisADException Couldn't create necessary VisAD object.
* @throws RemoteException Java RMI failure.
*/
public static int
getComponentIndex(TupleType tupleType, MathType componentType)
throws VisADException, RemoteException
{
/*
* Return first exact match if found.
*/
for (int i = tupleType.getDimension(); --i >= 0; )
if (componentType.equals(tupleType.getComponent(i)))
return i;
/*
* Return the first convertible-units match if found.
*/
for (int i = tupleType.getDimension(); --i >= 0; )
if (componentType.equalsExceptNameButUnits(tupleType.getComponent(i)))
return i;
return -1;
}
/**
* Gets the index of a component in a Set. If the set contains multiple
* instances of the component, then it is unspecified which component index is
* returned.
* @param set The Set.
* @param componentType The MathType of the component.
* @return The index of the component in the set or -1 if
* the component is not in the set.
* @throws VisADException Couldn't create necessary VisAD object.
* @throws RemoteException Java RMI failure.
*/
public static int
getComponentIndex(Set set, MathType componentType)
throws VisADException, RemoteException
{
return
getComponentIndex(((SetType)set.getType()).getDomain(), componentType);
}
/**
* Gets the index of a component in the range of a Function. If the range
* contains multiple instances of the component, then it is unspecified
* which component index is returned.
* @param function The Function.
* @param componentType The MathType of the component.
* @return The index of the component in the range of the
* field or -1 if the component is not in the range
* of the field (NB: this is not the flat-range
* index).
* @throws VisADException Couldn't create necessary VisAD object.
* @throws RemoteException Java RMI failure.
*/
public static int
getComponentIndex(Function function, MathType componentType)
throws VisADException, RemoteException
{
return getComponentIndex(getRangeTupleType(function), componentType);
}
/**
* Ensures that the range of a Field is a given type. Extracts from
* the input field only if necessary.
* @param field The input field.
* @param newRangeType The desired type of range for the resulting
* field.
* @return A field with the desired range type. The range
* data will be missing, however, if <em>all</em>
* of it couldn't be extracted from the input
* Field (i.e.
* RETURN_VALUE<code>.isMissing()</code> will be
* true.
* @throws UnimplementedException
* The desired range type is a TupleType and not
* a RealTupleType.
* @throws TypeException The new range type cannot be the range of a
* field.
* @throws VisADException Couldn't create necessary VisAD object.
* @throws RemoteException Java RMI failure.
*/
public static Field
ensureRange(Field field, MathType newRangeType)
throws UnimplementedException, TypeException, VisADException,
RemoteException
{
Field result;
if (newRangeType.equals(getRangeType(field)))
{
result = field;
}
else if (newRangeType instanceof RealType)
{
int componentIndex = getComponentIndex(field, newRangeType);
if (componentIndex >= 0)
{
result = field.extract(componentIndex);
}
else
{
result =
new FlatField(
new FunctionType(getDomainType(field), newRangeType),
field.getDomainSet());
}
}
else if (newRangeType instanceof RealTupleType)
{
int realTupleIndex = getComponentIndex(field, newRangeType);
if (realTupleIndex >= 0)
{
/*
* The desired RealTuple range is a component of the input Field.
*/
result = (FlatField)field.extract(realTupleIndex);
}
else
{
/*
* The desired RealTuple range is not a component of the input Field.
* Extract each component of the desired RealTuple into a separate
* Field and then combine the Field-s.
*/
RealTupleType newRangeRealTupleType = (RealTupleType)newRangeType;
int componentCount = newRangeRealTupleType.getDimension();
ArrayList flatFields = new ArrayList(componentCount);
for (int i = 0; i < componentCount; ++i)
{
int componentIndex =
getComponentIndex(field, newRangeRealTupleType.getComponent(i));
if (componentIndex >= 0)
flatFields.add(field.extract(componentIndex));
}
if (flatFields.size() != componentCount)
{
/* Not all desired components exist in the range of the input Field */
result =
new FlatField(
new FunctionType(getDomainType(field), newRangeType),
field.getDomainSet());
}
else
{
/* All desired components exist in the range of the input Field */
result =
(FlatField)FieldImpl.combine(
(FlatField[])flatFields.toArray(new FlatField[componentCount]));
}
}
}
else if (newRangeType instanceof TupleType)
{
throw new UnimplementedException(
"Can't yet create Field with range " + newRangeType +
" from existing Field");
}
else
{
throw new TypeException("Can't create Field with range " + newRangeType);
}
return result;
}
/**
* Provides support for comparing RealTuple-s of the same RealTupleType.
*
* @author Steven R. Emmerson
*/
public static final class
RealTupleComparator
implements Comparator
{
/**
* The single instance.
*/
public static final RealTupleComparator INSTANCE =
new RealTupleComparator();
/**
* Constructs from nothing.
*/
private
RealTupleComparator()
{}
/**
* Compares two RealTuple-s of the same RealTupleType.
*
* @param obj1 The first RealTuple.
* @param obj2 The second RealTuple. It shall have the same
* RealTupleType as the first RealTuple.
* @return A negative integer, zero, or a positive integer
* as the first RealTuple is less than, equal to,
* or greater than the second RealTuple.
* @throws ClassCastException
* The types of the arguments prevent this
* comparator from comparing them.
*/
public int
compare(Object obj1, Object obj2)
throws ClassCastException
{
RealTuple realTuple1 = (RealTuple)obj1;
RealTuple realTuple2 = (RealTuple)obj2;
try
{
int rank = realTuple1.getDimension();
int comp = 0;
/*
* Because Set rasterization has the last component as the outermost
* dimension, the last tuple component is treated as the grossest one
* for the purpose of comparison. Hence, components are compared in
* decreasing order.
*/
for (int i = rank; --i >= 0 && comp == 0; )
comp = ((Real)realTuple1.getComponent(i)).compareTo(
((Real)realTuple2.getComponent(i)));
return comp;
}
catch (Exception e)
{
/*
* This is the only checked exception a Comparator is allowed to throw.
* The original exception could be either a visad.VisADException or a
* java.rmi.RemoteException.
*/
String reason = e.getMessage();
throw new ClassCastException("Can't compare RealTuple-s" +
(reason == null ? "" : ": " + reason));
}
}
}
/**
* Provides support for comparing domain points that are reference to a
* particular Field.
*/
private static class
ReferencedDomainPoint
implements Comparable
{
protected final RealTuple sample;
protected final Field field;
public ReferencedDomainPoint(RealTuple sample, Field field)
{
this.sample = sample;
this.field = field;
}
public int compareTo(Object obj)
{
return
RealTupleComparator.INSTANCE.compare(
sample, ((ReferencedDomainPoint)obj).sample);
}
}
/**
* Consolidates fields.
* @param fields The fields to consolidate. Each field shall
* have the same FunctionType.
* @return The input Fields consolidated into one Field.
* The domain shall be a GriddedSet comprising the
* union of the sample points of the fields. The
* FunctionType shall be the same as that of the
* input Field-s. When more than one input Field
* has valid range data for the same domain point,
* it is unspecified which range datum is used for
* the output Field.
* @throws FieldException The Field array has zero length.
* @throws TypeException Input Field-s not all same type.
* @throws VisADException Couldn't create necessary VisAD object.
* @throws RemoteException Java RMI failure.
*/
public static Field
consolidate(Field[] fields)
throws FieldException, TypeException, VisADException, RemoteException
{
/*
* Identify valid fields.
*/
ArrayList validFields = new ArrayList(fields.length);
for (int i = 0; i < fields.length; ++i)
{
Field field = fields[i];
if (!field.isMissing())
validFields.add(field);
}
if (validFields.size() == 0)
throw new FieldException(
DataUtility.class.getName() + "(Field[]): Zero fields to consolidate");
/*
* Determine the consolidated domain.
*/
FunctionType funcType = (FunctionType)fields[0].getType();
TreeSet consolidatedDomainTuples = new TreeSet();
for (Iterator iter = validFields.iterator(); iter.hasNext(); )
{
Field field = (Field)iter.next();
if (!field.getType().equals(funcType))
throw new TypeException("Field type mismatch");
for (Enumeration en = field.domainEnumeration();
en.hasMoreElements(); )
{
consolidatedDomainTuples.add(
new ReferencedDomainPoint((RealTuple)en.nextElement(), field));
}
}
/*
* Create the consolidated field (with no range data).
*/
Field field = fields[0];
float[][] domainFloats =
new float[field.getDomainDimension()][consolidatedDomainTuples.size()];
Unit[] domainUnits = field.getDomainUnits();
int sampleIndex = 0;
for (Iterator iter = consolidatedDomainTuples.iterator();
iter.hasNext();
++sampleIndex)
{
RealTuple domainTuple = ((ReferencedDomainPoint)iter.next()).sample;
for (int i = domainFloats.length; --i >= 0; )
domainFloats[i][sampleIndex] =
(float)((Real)domainTuple.getComponent(i)).getValue(domainUnits[i]);
}
SampledSet consolidatedDomain =
domainFloats.length == 1
? (SampledSet)new Gridded1DSet(
getDomainType(field), domainFloats, domainFloats[0].length,
(CoordinateSystem)null, field.getDomainUnits(),
(ErrorEstimate[])null)
: domainFloats.length == 2
? (SampledSet)new Irregular2DSet(
getDomainType(field), domainFloats, (CoordinateSystem)null,
field.getDomainUnits(), (ErrorEstimate[])null, (Delaunay)null)
: domainFloats.length == 3
? (SampledSet)new Irregular3DSet(
getDomainType(field), domainFloats, (CoordinateSystem)null,
field.getDomainUnits(), (ErrorEstimate[])null, (Delaunay)null)
: (SampledSet)new IrregularSet(
getDomainType(field), domainFloats, (CoordinateSystem)null,
field.getDomainUnits(), (ErrorEstimate[])null);
Field consolidatedField =
field instanceof FlatField
? new FlatField(funcType, consolidatedDomain)
: new FieldImpl(funcType, consolidatedDomain);
/*
* Set the range of the consolidated field.
*/
for (Iterator iter = consolidatedDomainTuples.iterator(); iter.hasNext(); )
{
ReferencedDomainPoint point = (ReferencedDomainPoint)iter.next();
RealTuple domainTuple = point.sample;
consolidatedField.setSample(
domainTuple, point.field.evaluate(domainTuple));
}
return consolidatedField;
}
/**
* Creates a GriddedSet from a FlatField. The GriddedSet will be created from
* the domain and flat-range of the FlatField. The first components in the
* tuples of the GriddedSet will come from the domain of the FlatField and the
* remaining components will come from the range of the FlatField. Note that,
* because a GriddedSet doesn't have the ability to contain values in small
* primitives (e.g. byte, short), the returned set may be significantly larger
* than the input field.
* @param field The FlatField from which to create a GriddedSet.
* @param copy Whether or not to copy the range values from
* the field (i.e. <code>field.getFloats(copy)
* </code>).
* @return The GriddedSet corresponding to the input
* FlatField.
* @throws VisADException Couldn't create necessary VisAD object.
* @throws RemoteException Java RMI failure.
*/
public static GriddedSet
createGriddedSet(FlatField field, boolean copy)
throws VisADException, RemoteException
{
int domainRank = field.getDomainDimension();
int flatRangeRank = field.getRangeDimension();
RealType[] realTypes = new RealType[domainRank + flatRangeRank];
RealTupleType tupleType = getDomainType(field);
for (int i = 0; i < domainRank; i++)
realTypes[i] = (RealType)tupleType.getComponent(i);
tupleType = getFlatRangeType(field);
for (int i = 0; i < flatRangeRank; i++)
realTypes[domainRank+i] = (RealType)tupleType.getComponent(i);
float[][] samples = new float[realTypes.length][field.getLength()];
/*
* The following gets the domain values in their actual units.
*/
System.arraycopy(
field.getDomainSet().getSamples(), 0, samples, 0, domainRank);
/*
* The following gets the range values in their default units.
*/
System.arraycopy(
field.getFloats(copy), 0, samples, domainRank, flatRangeRank);
int[] lengths = new int[samples.length];
for (int i = samples.length; --i >= 0; )
lengths[i] = samples[i].length;
Unit[] units = new Unit[realTypes.length];
System.arraycopy(field.getDomainUnits(), 0, units, 0, domainRank);
System.arraycopy(
field.getDefaultRangeUnits(), 0, units, domainRank, flatRangeRank);
return
GriddedSet.create(
new RealTupleType(realTypes),
samples,
lengths,
(CoordinateSystem)null,
units,
(ErrorEstimate[])null);
}
/**
* Simplifies a MathType. Removes all enclosing, single-component TupleType-s
* until the "core" is revealed (e.g. ScalarType, multi-component TupleType).
* @param type The MathType to be simplified.
* @return The simplest form corresponding to
* <code>type</code>.
* @throws VisADException Couldn't create necessary VisAD object.
*/
public static MathType simplify(MathType type)
throws VisADException
{
while (type instanceof TupleType && ((TupleType)type).getDimension() == 1)
type = ((TupleType)type).getComponent(0);
return type;
}
/**
* @deprecated Use getScalarTypes(Data, Vector) instead.
*/
public static int getRealTypes(Data data, Vector v)
throws VisADException, RemoteException
{
return getRealTypes(new Data[] {data}, v, true, false);
}
/**
* @deprecated Use getScalarTypes(Data[], Vector, boolean, boolean) instead.
*/
public static int getRealTypes(Data[] data, Vector v, boolean keepDupl,
boolean doCoordSys) throws VisADException, RemoteException
{
int dupl = getScalarTypes(data, v, keepDupl, doCoordSys);
int i = 0;
while (i < v.size()) {
ScalarType st = (ScalarType) v.elementAt(i);
if (!(st instanceof RealType)) v.remove(i);
else i++;
}
return dupl;
}
/**
* Obtains a Vector consisting of all ScalarTypes present in a Data object's
* MathType.
* @param data The Data from which to extract the ScalarTypes.
* @param v The Vector in which to store the ScalarTypes.
* @throws VisADException Couldn't parse the Data's MathType.
* @throws RemoteException Couldn't obtain the remote Data's MathType.
* @return The number of duplicate ScalarTypes found.
*/
public static int getScalarTypes(Data data, Vector v)
throws VisADException, RemoteException
{
return getScalarTypes(new Data[] {data}, v, true, false);
}
/**
* Obtains a Vector consisting of all ScalarTypes present in an array of
* Data objects' MathTypes.
* @param data The array of Data from which to extract the
* ScalarTypes.
* @param v The Vector in which to store the ScalarTypes.
* @param keepDupl Whether to add a RealType to the Vector when
* it is already present there.
* @param doCoordSys Whether to include ScalarTypes from
* CoordinateSystem references.
* @throws VisADException Couldn't parse a Data's MathType.
* @throws RemoteException Couldn't obtain a remote Data's MathType.
* @return The number of duplicate ScalarTypes found.
*/
public static int getScalarTypes(Data[] data, Vector v, boolean keepDupl,
boolean doCoordSys) throws VisADException, RemoteException
{
Vector coord = (doCoordSys ? new Vector() : null);
int[] dupl = new int[1];
dupl[0] = 0;
for (int i=0; i<data.length; i++) {
Data d = data[i];
if (d != null) {
MathType type = d.getType();
parse(type, v, dupl, keepDupl, coord);
}
}
if (coord != null) {
// append coordinate system reference ScalarTypes to vector
for (int i=0; i<coord.size(); i++) {
Object o = coord.elementAt(i);
boolean c = v.contains(o);
if (c) dupl[0]++;
if (keepDupl || !c) v.add(o);
}
}
return dupl[0];
}
/**
* getScalarTypes helper method.
*/
private static void parse(MathType mathType, Vector v, int[] i,
boolean keepDupl, Vector coord) throws VisADException
{
if (mathType instanceof FunctionType) {
parseFunction((FunctionType) mathType, v, i, keepDupl, coord);
}
else if (mathType instanceof SetType) {
parseSet((SetType) mathType, v, i, keepDupl, coord);
}
else if (mathType instanceof TupleType) {
parseTuple((TupleType) mathType, v, i, keepDupl, coord);
}
else parseScalar((ScalarType) mathType, v, i, keepDupl);
}
/**
* getScalarTypes helper method.
*/
private static void parseFunction(FunctionType mathType, Vector v, int[] i,
boolean keepDupl, Vector coord) throws VisADException
{
// extract domain
RealTupleType domain = mathType.getDomain();
parseTuple((TupleType) domain, v, i, keepDupl, coord);
// extract range
MathType range = mathType.getRange();
parse(range, v, i, keepDupl, coord);
}
/**
* getScalarTypes helper method.
*/
private static void parseSet(SetType mathType, Vector v, int[] i,
boolean keepDupl, Vector coord) throws VisADException
{
// extract domain
RealTupleType domain = mathType.getDomain();
parseTuple((TupleType) domain, v, i, keepDupl, coord);
}
/**
* getScalarTypes helper method.
*/
private static void parseTuple(TupleType mathType, Vector v, int[] i,
boolean keepDupl, Vector coord) throws VisADException
{
// extract components
for (int j=0; j<mathType.getDimension(); j++) {
MathType cType = mathType.getComponent(j);
if (cType != null) parse(cType, v, i, keepDupl, coord);
}
if (mathType instanceof RealTupleType && coord != null) {
// add coordinate system references
RealTupleType realTupleType = (RealTupleType) mathType;
CoordinateSystem coordSys = realTupleType.getCoordinateSystem();
if (coordSys != null) {
RealTupleType ref = coordSys.getReference();
for (int j=0; j<realTupleType.getDimension(); j++) {
RealType rType = (RealType) realTupleType.getComponent(j);
parseScalar(rType, coord, new int[1], keepDupl);
}
for (int j=0; j<ref.getDimension(); j++) {
RealType rType = (RealType) ref.getComponent(j);
parseScalar(rType, coord, new int[1], keepDupl);
}
}
}
}
/**
* getScalarTypes helper method.
*/
private static void parseScalar(ScalarType mathType, Vector v, int[] i,
boolean keepDupl)
{
if (v.contains(mathType)) {
if (keepDupl) v.add(mathType);
i[0]++;
}
else v.add(mathType);
}
/**
* Attempts to guess a good set of mappings for a display containing
* Data objects of the given types. The algorithm simply returns mappings
* for the first successfully guessed dataset.
*/
public static ScalarMap[] guessMaps(MathType[] types, boolean allow3d) {
int len = types.length;
ScalarMap[] maps = null;
for (int i=0; i<len; i++) {
MathType t = types[i];
if (t != null) maps = t.guessMaps(allow3d);
if (maps != null) break;
}
return maps;
}
/**
* Converts the given vector of mappings to an easy-to-read String form.
*/
public static String convertMapsToString(Vector v) {
int len = v.size();
ScalarMap[] sm = new ScalarMap[len];
for (int i=0; i<len; i++) sm[i] = (ScalarMap) v.elementAt(i);
return convertMapsToString(sm);
}
/**
* Converts the given array of mappings to an easy-to-read String form.
*/
public static String convertMapsToString(ScalarMap[] sm) {
StringBuffer sb = new StringBuffer(128);
for (int i=0; i<sm.length; i++) {
ScalarMap m = sm[i];
ScalarType domain = m.getScalar();
DisplayRealType range = m.getDisplayScalar();
int q = -1;
for (int j=0; j<Display.DisplayRealArray.length; j++) {
if (range.equals(Display.DisplayRealArray[j])) q = j;
}
sb.append(' ');
sb.append(domain.getName());
sb.append(' ');
sb.append(q);
}
return sb.toString();
}
/**
* Converts the given map string to its corresponding array of mappings.
* @param mapString The String from which to extract the ScalarMaps.
* @param data The Data object to search for valid ScalarTypes.
* @param showErrors Whether to output errors to stdout.
*/
public static ScalarMap[] convertStringToMaps(
String mapString, Data data, boolean showErrors)
{
return convertStringToMaps(mapString, new Data[] {data}, showErrors);
}
/**
* Converts the given map string to its corresponding array of mappings.
* @param mapString The String from which to extract the ScalarMaps.
* @param data The Data objects to search for valid ScalarTypes.
* @param showErrors Whether to output errors to stdout.
*/
public static ScalarMap[] convertStringToMaps(
String mapString, Data[] data, boolean showErrors)
{
Vector types = new Vector();
for (int i=0; i<data.length; i++) {
try {
getScalarTypes(data[i], types);
}
catch (VisADException exc) {
if (showErrors) {
System.out.println("Warning: " +
"could not extract ScalarTypes from Data object.");
}
}
catch (RemoteException exc) {
if (showErrors) {
System.out.println("Warning: " +
"could not extract ScalarTypes from Data object.");
}
}
}
return convertStringToMaps(mapString, types, showErrors);
}
/**
* Converts the given map string to its corresponding array of mappings.
* @param mapString The String from which to extract the ScalarMaps.
* @param types List of valid ScalarTypes.
* @param showErrors Whether to output errors to stdout.
*/
public static ScalarMap[] convertStringToMaps(
String mapString, Vector types, boolean showErrors)
{
// extract mapping information from string
if (mapString == null) return null;
StringTokenizer st = new StringTokenizer(mapString);
Vector dnames = new Vector();
Vector rnames = new Vector();
while (true) {
if (!st.hasMoreTokens()) break;
String s = st.nextToken();
if (!st.hasMoreTokens()) {
if (showErrors) {
System.err.println("Warning: trailing maps value " + s +
" has no corresponding number and will be ignored");
}
continue;
}
String si = st.nextToken();
Integer i = null;
try {
i = new Integer(Integer.parseInt(si));
}
catch (NumberFormatException exc) { }
if (i == null) {
if (showErrors) {
System.err.println("Warning: maps value " + si + " is not a " +
"valid integer and the maps pair (" + s + ", " + si + ") " +
"will be ignored");
}
}
else {
dnames.add(s);
rnames.add(i);
}
}
// set up mappings
if (dnames != null) {
int len = dnames.size();
if (len > 0) {
int vLen = types.size();
int dLen = Display.DisplayRealArray.length;
// construct ScalarMaps
ScalarMap[] maps = new ScalarMap[len];
for (int j=0; j<len; j++) {
// find appropriate ScalarType
ScalarType mapDomain = null;
String name = (String) dnames.elementAt(j);
for (int k=0; k<vLen; k++) {
ScalarType type = (ScalarType) types.elementAt(k);
if (name.equals(type.getName())) {
mapDomain = type;
break;
}
}
if (mapDomain == null) {
// still haven't found type; look in static Vector for it
mapDomain = ScalarType.getScalarTypeByName(name);
}
// find appropriate DisplayRealType
int q = ((Integer) rnames.elementAt(j)).intValue();
DisplayRealType mapRange = null;
if (q >= 0 && q < dLen) mapRange = Display.DisplayRealArray[q];
// construct mapping
if (mapDomain == null || mapRange == null) {
if (showErrors) {
System.err.print("Warning: maps pair (" + name + ", " +
q + ") has an invalid ");
if (mapDomain == null && mapRange == null) {
System.err.print("domain and range");
}
else if (mapDomain == null) System.err.print("domain");
else System.err.print("range");
System.err.println(" and will be ignored");
}
maps[j] = null;
}
else {
try {
maps[j] = new ScalarMap(mapDomain, mapRange);
}
catch (VisADException exc) {
if (showErrors) {
System.err.println("Warning: maps pair (" + name + ", " +
q + ") cannot be converted to a ScalarMap");
}
maps[j] = null;
}
}
}
return maps;
}
}
return null;
}
/**
* Converts an array of strings into a VisAD Tuple.
*
* @param s The array of strings to be converted to a VisAD Tuple.
*
* @return VisAD Tuple, or null if Tuple could not be created.
*/
public static Tuple stringsToTuple(String[] s) {
return stringsToTuple(s, false);
}
/**
* Converts an array of strings into a VisAD Tuple.
*
* @param s The array of strings to be converted to a VisAD Tuple.
* @param printStackTraces <tt>true</tt> if the stack trace for
* any exception should be printed.
*
* @return VisAD Tuple, or null if Tuple could not be created.
*/
public static Tuple stringsToTuple(String[] s, boolean printStackTraces) {
try {
if (s == null) return null;
int len = s.length;
if (len == 0) return null;
Text[] text = new Text[len];
for (int i=0; i<len; i++) text[i] = new Text(s[i]);
Tuple tuple = new Tuple(text);
return tuple;
}
catch (VisADException exc) {
if (printStackTraces) exc.printStackTrace();
}
catch (RemoteException exc) {
if (printStackTraces) exc.printStackTrace();
}
return null;
}
/**
* Converts a VisAD tuple into an array of strings.
*
* @param t The VisAD Tuple to be converted to an array of strings.
*
* @return Array of Strings, or null if array could not be created.
*/
public static String[] tupleToStrings(Tuple t) {
return tupleToStrings(t, false);
}
/**
* Converts a VisAD tuple into an array of strings.
*
* @param t The VisAD Tuple to be converted to an array of strings.
* @param printStackTraces <tt>true</tt> if the stack trace for
* any exception should be printed.
*
* @return Array of Strings, or null if array could not be created.
*/
public static String[] tupleToStrings(Tuple t, boolean printStackTraces) {
if (t == null) return null;
int len = t.getDimension();
try {
String[] errors = new String[len];
for (int i=0; i<len; i++) {
Text text = (Text) t.getComponent(i);
errors[i] = text.getValue();
}
return errors;
}
catch (VisADException exc) {
if (printStackTraces) exc.printStackTrace();
}
catch (RemoteException exc) {
if (printStackTraces) exc.printStackTrace();
}
return null;
}
/**
* Verify that an object is Serializable by attempting to
* serialize it.
*
* @param obj An object which needs to be serialized
*
* @return <tt>true</tt> if the object is Serializable, false otherwise.
*/
public static boolean isSerializable(Object obj) {
return isSerializable(obj, false);
}
/**
* Verify that an object is Serializable by attempting to
* serialize it.
*
* @param obj An object which needs to be serialized
* @param printStackTraces <tt>true</tt> if the stack trace for
* any exception should be printed.
*
* @return <tt>true</tt> if the object is Serializable, false otherwise.
*/
public static boolean isSerializable(Object obj, boolean printStackTraces)
{
java.io.ByteArrayOutputStream outBytes;
outBytes = new java.io.ByteArrayOutputStream();
java.io.ObjectOutputStream outStream;
try {
outStream = new java.io.ObjectOutputStream(outBytes);
} catch (java.io.IOException ioe) {
if (printStackTraces) ioe.printStackTrace();
return false;
}
try {
outStream.writeObject(obj);
outStream.flush();
} catch (java.io.IOException ioe) {
if (printStackTraces) ioe.printStackTrace();
return false;
}
java.io.ByteArrayInputStream inBytes;
inBytes = new java.io.ByteArrayInputStream(outBytes.toByteArray());
try {
outStream.close();
outBytes.close();
} catch (java.io.IOException ioe) {
if (printStackTraces) ioe.printStackTrace();
return false;
}
java.io.ObjectInputStream inStream;
try {
inStream = new java.io.ObjectInputStream(inBytes);
} catch (java.io.IOException ioe) {
if (printStackTraces) ioe.printStackTrace();
return false;
}
Object obj2;
try {
obj2 = inStream.readObject();
} catch (java.io.IOException ioe) {
if (printStackTraces) ioe.printStackTrace();
return false;
} catch (ClassNotFoundException cnfe) {
if (printStackTraces) cnfe.printStackTrace();
return false;
}
try {
inStream.close();
inBytes.close();
} catch (java.io.IOException ioe) {
if (printStackTraces) ioe.printStackTrace();
return false;
}
return true;
}
/**
* Converts a remote Data object to a local Data object.
*
* @param data The Data object to be made local.
*
* @return Local Data object, or null if Data could not be converted.
*/
public static DataImpl makeLocal(Data data) {
return makeLocal(data, false);
}
/**
* Converts a remote Data object to a local Data object.
*
* @param data The Data object to be made local.
* @param printStackTraces <tt>true</tt> if the stack trace for
* any exception should be printed.
*
* @return Local Data object, or null if Data could not be converted.
*/
public static DataImpl makeLocal(Data data, boolean printStackTraces) {
try {
if (data != null) return data.local();
}
catch (VisADException exc) {
if (printStackTraces) exc.printStackTrace();
}
catch (RemoteException exc) {
if (printStackTraces) exc.printStackTrace();
}
return null;
}
/**
* Gets the specified sample of a VisAD Set.
*
* @param set The set to have a sample returned.
* @param index The index of the sample to be returned.
* @return The sample at the specified index. Will
* be empty if the index is out-of-bounds.
* Will have a single component if the set is
* one-dimensional.
* @throws VisADException Couldn't create necessary VisAD object.
* @throws RemoteException Java RMI failure.
*/
public static RealTuple getSample(Set set, int index)
throws VisADException, RemoteException {
RealTuple sample;
RealTupleType realTupleType = ((SetType) set.getType()).getDomain();
double[][] values =
Unit.convertTuple(set.indexToDouble(new int[]{ index }),
set.getSetUnits(),
realTupleType.getDefaultUnits());
if ((index < 0) || (index >= set.getLength())) {
sample = new RealTuple(realTupleType);
} else {
double[] doubles = new double[values.length];
for (int i = doubles.length; --i >= 0; ) {
doubles[i] = values[i][0];
}
sample = new RealTuple(realTupleType, doubles);
}
return sample;
}
/**
* Gets the units of the (flat) components of the range of a FlatField.
*
* @param field The FlatField.
* @return The units of the (flat) components of the
* range of the FlatField. Won't be
* <code>null</code>.
*/
public static Unit[] getRangeUnits(FlatField field) {
Unit[][] units = field.getRangeUnits();
Unit[] result = new Unit[units.length];
for (int i = result.length; --i >= 0; ) {
result[i] = units[i][0];
}
return result;
}
/**
* Make a RealType with a unique name for the given unit
*
* @param name name of the RealType
* @param unit the default unit
*
* @return the RealType
*
* @throws VisADException problem creating the RealType
*/
public static RealType getUniqueRealType(String name, Unit unit) {
return getUniqueRealType(name, unit, null, 0);
}
/**
* Make a RealType with a unique name for the given unit
*
* @param name name of the RealType
* @param unit the default unit
* @param set the default set
* @param attrMask the attribute mask
*
* @return the RealType or null if it still can't create a unique name
*/
public static RealType getUniqueRealType(String name, Unit unit,
Set set, int attrMask) {
RealType type = RealType.getRealType(name, unit, set, attrMask);
if (type == null) {
String newname = cleanTypeName(name) + "[unit:" + ((unit == null)
? "null"
: cleanTypeName(unit.toString())) + "]";
type = RealType.getRealType(newname, unit, set, attrMask);
}
return type;
}
/**
* Make a valid VisAD RealType name from the string. Remove
* spaces, "." and parens.
* @param name name to clean
* @return cleaned up name
*/
public static String cleanTypeName(String name) {
name = name.replace('.', '_');
name = name.replace(",","_");
name = name.replace("-","_"); // so doesn't get confused with ->
name = name.replace("*","_"); // so it doesn't confuse regexes
name = name.replace(' ', '_');
name = name.replace('(', '[');
name = name.replace(')', ']');
while(name.indexOf("__")>=0) {
name = name.replace("__","_");
}
return name;
}
}