ucar.ma2.ArrayStructureMA Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
* See LICENSE for license information.
*/
package ucar.ma2;
import ucar.nc2.Structure;
import ucar.nc2.Variable;
import ucar.nc2.Sequence;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Concrete implementation of ArrayStructure, data storage is in member arrays, which are converted to
* StructureData member data on the fly.
* This defers object creation for efficiency. Use getJavaArrayXXX() and getScalarXXX() data accessors if
* possible.
*
* How to create:
*
*
* ArrayStructureMA asma = new ArrayStructureMA(smembers, getShape());
* for (int i = 0; i < orgVariables.size(); i++) {
* Variable v = (Variable) orgVariables.get(i);
* Array data = v.read();
* asma.setMemberArray(v.getName(), data);
* }
*
*
* How to do Nested Structures:
*
*
Structure {
float f1;
short f2(3);
Structure {
int g1;
double(2) g2;
double(3,4) g3;
Structure {
int h1;
double(2) h2;
} nested2(7);
} nested1(12);
} s(4);
*
*
* - For f1, you need an ArrayFloat of shape {4}
*
- For f2, you need an ArrayShort of shape {4, 3} .
*
- For nested1, you need an ArrayStructure of shape {4, 12}.
* Use an ArrayStructureMA that has 3 members:
*
* - For g1, you need an ArrayInt of shape (4, 12}
*
- For g2, you need an ArrayDouble of shape {4, 12, 2}.
*
- For g3, you need an ArrayDouble of shape {4, 12, 3, 4}.
*
* - For nested2, you need an ArrayStructure of shape {4, 12, 7}.
* Use an ArrayStructureMA that has 2 members:
*
* - For h1, you need an ArrayInt of shape (4, 12, 7}
*
- For h2, you need an ArrayDouble of shape {4, 12, 7, 2}.
*
*
* Example code:
*
*
* public void testMA() throws IOException, InvalidRangeException {
* StructureMembers members = new StructureMembers("s");
*
* StructureMembers.Member m = members.addMember("f1", "desc", CDM.UNITS, DataType.FLOAT, new int[] {1});
* Array data = Array.factory(DataType.FLOAT, new int[] {4});
* m.setDataArray(data);
* fill(data);
*
* m = members.addMember("f2", "desc", CDM.UNITS, DataType.SHORT, new int[] {3});
* data = Array.factory(DataType.SHORT, new int[] {4, 3});
* m.setDataArray(data);
* fill(data);
*
* m = members.addMember("nested1", "desc", CDM.UNITS, DataType.STRUCTURE, new int[] {9});
* data = makeNested1(m);
* m.setDataArray(data);
*
* ArrayStructureMA as = new ArrayStructureMA(members, new int[] {4});
* new TestStructureArray().testArrayStructure(as);
*
* // get f2 out of the 3nd "s"
* StructureMembers.Member f2 = as.getStructureMembers().findMember("f2");
* short[] f2data = as.getJavaArrayShort(2, f2);
* assert f2data[0] == 20;
* assert f2data[1] == 21;
* assert f2data[2] == 22;
*
* // get nested1 out of the 3nd "s"
* StructureMembers.Member nested1 = as.getStructureMembers().findMember("nested1");
* ArrayStructure nested1Data = as.getArrayStructure(2, nested1);
*
* // get g1 out of the 7th "nested1"
* StructureMembers.Member g1 = nested1Data.getStructureMembers().findMember("g1");
* int g1data = nested1Data.getScalarInt(6, g1);
* assert g1data == 26;
*
* // get nested2 out of the 7th "nested1"
* StructureMembers.Member nested2 = nested1Data.getStructureMembers().findMember("nested2");
* ArrayStructure nested2Data = nested1Data.getArrayStructure(6, nested2);
*
* // get h1 out of the 4th "nested2"
* StructureMembers.Member h1 = nested2Data.getStructureMembers().findMember("h1");
* int val = nested2Data.getScalarInt(4, h1);
* assert (val == 264);
* }
*
*
* public ArrayStructure makeNested1(StructureMembers.Member parent) throws IOException, InvalidRangeException {
* StructureMembers members = new StructureMembers(parent.getName());
* parent.setStructureMembers(members);
*
* StructureMembers.Member m = members.addMember("g1", "desc", CDM.UNITS, DataType.INT, new int[] {1});
* Array data = Array.factory(DataType.INT, new int[] {4, 9});
* m.setDataArray(data);
* fill(data);
*
* m = members.addMember("g2", "desc", CDM.UNITS, DataType.DOUBLE, new int[] {2});
* data = Array.factory(DataType.DOUBLE, new int[] {4, 9, 2});
* m.setDataArray(data);
* fill(data);
*
* m = members.addMember("g3", "desc", CDM.UNITS, DataType.DOUBLE, new int[] {3, 4});
* data = Array.factory(DataType.DOUBLE, new int[] {4, 9, 3, 4});
* m.setDataArray(data);
* fill(data);
*
* m = members.addMember("nested2", "desc", CDM.UNITS, DataType.STRUCTURE, new int[] {7});
* data = makeNested2(m);
* m.setDataArray(data);
*
* return new ArrayStructureMA(members, new int[] {4, 9});
* }
*
* public ArrayStructure makeNested2(StructureMembers.Member parent) throws IOException, InvalidRangeException {
* StructureMembers members = new StructureMembers(parent.getName());
* parent.setStructureMembers(members);
*
* StructureMembers.Member m = members.addMember("h1", "desc", CDM.UNITS, DataType.INT, new int[] {1});
* Array data = Array.factory(DataType.INT, new int[] {4, 9, 7});
* m.setDataArray(data);
* fill(data);
*
* m = members.addMember("h2", "desc", CDM.UNITS, DataType.DOUBLE, new int[] {2});
* data = Array.factory(DataType.DOUBLE, new int[] {4, 9, 7, 2});
* m.setDataArray(data);
* fill(data);
*
* return new ArrayStructureMA(members, new int[] {4, 9, 7});
* }
*
*
* @author caron
* @see Array
*/
public class ArrayStructureMA extends ArrayStructure {
/*
* Implementation notes
* Most of the methods are now the default methods in the superclass, so that other subclasses can call them.
* This happens when the data is "enhanced", member arrays are set and must override the other possible storage
* methods.
*/
/**
* Create a new Array of type StructureData and the given members and shape.
*
* You must set the data Arrays on each of the Members, using setDataObject(). These data Arrays contain the data
* for that member Variable, for all the StructureData. Therefore it has rank one greater that the Members. The extra
* dimension must be the outermost (slowest varying) dimension. ie, if some member has shape [3,10], the array would
* have
* shape [nrows, 3, 10].
*
* @param members a description of the structure members
* @param shape the shape of the Array.
*/
public ArrayStructureMA(StructureMembers members, int[] shape) {
super(members, shape);
}
public ArrayStructureMA(StructureMembers members, int[] shape, StructureData[] sdata) {
super(members, shape);
if (nelems != sdata.length)
throw new IllegalArgumentException("StructureData length= " + sdata.length + "!= shape.length=" + nelems);
this.sdata = sdata;
}
/**
* Turn any ArrayStructure into a ArrayStructureMA
*
* @param from copy from here. If from is a ArrayStructureMA, return it.
* @return equivalent ArrayStructureMA
* @throws java.io.IOException on error reading a sequence
*/
public static ArrayStructureMA factoryMA(ArrayStructure from) throws IOException {
if (from instanceof ArrayStructureMA)
return (ArrayStructureMA) from;
// To create an ArrayStructureMA that we can iterate over later, we need to know the shape of "from".
if (from.getSize() > 0) {
ArrayStructureMA to = new ArrayStructureMA(from.getStructureMembers().toBuilder(false).build(), from.getShape());
for (StructureMembers.Member m : from.getMembers()) {
to.setMemberArray(m.getName(), from.extractMemberArray(m));
}
return to;
}
// from.getSize() <= 0. This usually means that "from" is an ArraySequence, and that we won't know its size until
// we iterate over it. extractMemberArray() will do that iteration for us, and then we can use the size of the
// array it returns to determine the shape of "from".
int numRecords = -1;
Map memberArrayMap = new LinkedHashMap<>();
for (StructureMembers.Member m : from.getMembers()) {
Array array = from.extractMemberArray(m);
assert array.getSize() > 0 : "array's size should have been computed in extractMemberArray().";
int firstDimLen = array.getShape()[0];
if (numRecords == -1) {
numRecords = firstDimLen;
} else {
assert numRecords == firstDimLen : String.format(
"Expected all structure members to have the same first" + "dimension length, but %d != %d.", numRecords,
firstDimLen);
}
memberArrayMap.put(m.getName(), array);
}
int[] shape;
if (numRecords == -1) {
shape = new int[] {0}; // "from" really was empty.
} else {
shape = new int[] {numRecords};
}
ArrayStructureMA to = new ArrayStructureMA(from.getStructureMembers().toBuilder(false).build(), shape);
for (Map.Entry entry : memberArrayMap.entrySet()) {
to.setMemberArray(entry.getKey(), entry.getValue());
}
return to;
}
@Override
protected StructureData makeStructureData(ArrayStructure as, int index) {
return new StructureDataA(as, index);
}
/**
* Set the data array for this member.
*
* @param memberName name of member
* @param data Array for this member.
*/
public void setMemberArray(String memberName, Array data) {
StructureMembers.Member m = members.findMember(memberName);
m.setDataArray(data);
}
public Array getArray(int recno, StructureMembers.Member m) {
if (m.isVariableLength()) {
Array data = m.getDataArray();
if (data instanceof ArrayObject) {
ArrayObject ao = (ArrayObject) data;
Object dao = ao.getObject(recno);
assert dao instanceof Array;
return (Array) dao;
}
}
return super.getArray(recno, m);
}
/**
* Create an ArrayStructure for a Structure. Allow nested Structures.
* Create the data arrays, and an iterator.
*
* @param from copy from here. If from is a ArrayStructureMA, return it.
* @param shape the shape of the ArrayStructure
* @return equivilent ArrayStructureMA
*/
public static ArrayStructureMA factoryMA(Structure from, int[] shape) {
StructureMembers sm = from.makeStructureMembers();
for (Variable v : from.getVariables()) {
Array data;
if (v instanceof Sequence) {
data = Array.factory(DataType.SEQUENCE, shape); // an array sequence - one for each parent element
// Structure s = (Structure) v;
// StructureMembers smn = s.makeStructureMembers();
// data = new ArraySequenceNested(smn, (int) Index.computeSize(v.getShapeAll())); // ??
} else if (v instanceof Structure)
data = ArrayStructureMA.factoryMA((Structure) v, combine(shape, v.getShape()));
else
data = Array.factory(v.getDataType(), combine(shape, v.getShape()));
StructureMembers.Member m = sm.findMember(v.getShortName());
m.setDataArray(data);
}
return new ArrayStructureMA(sm, shape);
}
private static int[] combine(int[] shape1, int[] shape2) {
int[] result = new int[shape1.length + shape2.length];
System.arraycopy(shape1, 0, result, 0, shape1.length);
System.arraycopy(shape2, 0, result, shape1.length, shape2.length);
return result;
}
}