package ucar.coord;
import net.jcip.annotations.Immutable;
import ucar.ma2.Section;
import ucar.nc2.grib.grib2.Grib2Record;
import ucar.nc2.util.Indent;
import java.util.*;
/**
* N dimensional coordinates.
*
* @author caron
* @since 11/27/13
*/
@Immutable
public class CoordinateND<T> {
private final List<Coordinate> coordinates; // result is orthogonal coordinates
private final SparseArray<T> sa; // indexes refer to coordinates
public CoordinateND( List<Coordinate> coordinates, SparseArray<T> sa) {
this.coordinates = Collections.unmodifiableList(coordinates);
this.sa = sa;
}
public List<Coordinate> getCoordinates() {
return coordinates;
}
public int getNCoordinates() {
return coordinates.size();
}
public SparseArray<T> getSparseArray() {
return sa;
}
/* public void showInfo(List<T> records, Formatter info) {
if (sa == null) buildSparseArray(records, info);
for (Coordinate coord : coordinates)
coord.showInfo(info, new Indent(2));
info.format("%n%n");
if (sa != null) sa.showInfo(info, null);
info.format("%n");
} */
public void showInfo(Formatter info, Counter all) {
for (Coordinate coord : coordinates)
coord.showInfo(info, new Indent(2));
if (sa != null) sa.showInfo(info, all);
}
////////////////////
public static class Builder<T> {
private List<CoordinateBuilder<T>> builders = new ArrayList<>();
private List<Coordinate> coordb = new ArrayList<>();
public Builder() {
builders = new ArrayList<>();
}
public void addBuilder(CoordinateBuilder<T> builder) {
builders.add(builder);
}
public void addRecord(T gr) {
for (CoordinateBuilder<T> builder : builders)
builder.addRecord(gr);
}
public CoordinateND<T> finish(List<T> records, Formatter info) {
for (CoordinateBuilder builder : builders) {
Coordinate coord = builder.finish();
if (coord.getType() == Coordinate.Type.time2D)
coordb.add(((CoordinateTime2D) coord).getRuntimeCoordinate());
coordb.add(coord);
}
SparseArray<T> sa = buildSparseArray(records, info);
return new CoordinateND<T>(coordb, sa);
}
public SparseArray<T> buildSparseArray(List<T> records, Formatter info) {
int[] sizeArray = new int[coordb.size()];
for (int i = 0; i < coordb.size(); i++) {
Coordinate coord = coordb.get(i);
if (coord.getType() == Coordinate.Type.time2D)
sizeArray[i] = ((CoordinateTime2D) coord).getNtimes();
else
sizeArray[i] = coordb.get(i).getSize();
}
SparseArray.Builder<T> saBuilder = new SparseArray.Builder<>(sizeArray);
int[] index = new int[coordb.size()];
for (T gr : records) {
int count = 0;
for (CoordinateBuilder<T> builder : builders) {
if (builder instanceof CoordinateBuilder.TwoD) {
CoordinateBuilder.TwoD<T> builder2D = (CoordinateBuilder.TwoD<T>) builder;
int[] coordsIdx = builder2D.getCoordIndices(gr);
index[count++] = coordsIdx[0];
index[count++] = coordsIdx[1];
} else {
index[count++] = builder.getIndex(gr);
}
}
saBuilder.add(gr, info, index);
}
return saBuilder.finish();
}
/* private SparseArray<T> buildSparseArray(List<T> records, Formatter info) {
int count = 0;
for (Coordinate coord : coordb) {
if (coord.getType() == Coordinate.Type.time2D) count++;
count++;
}
int[] sizeArray = new int[count];
count = 0;
for (Coordinate coord : coordb) {
if (coord.getType() == Coordinate.Type.time2D) {
CoordinateTime2D coord2D = (CoordinateTime2D) coord;
sizeArray[count++] = coord2D.getNruns();
sizeArray[count++] = coord2D.getNtimes();
} else {
sizeArray[count++] = coord.getSize();
}
}
SparseArray.Builder<T> saBuilder = new SparseArray.Builder<>(sizeArray);
int[] index = new int[coordb.size()];
for (T gr : records) {
int count2 = 0;
for (CoordinateBuilder<T> builder : builders) {
if (coord.getType() == Coordinate.Type.time2D) {
CoordinateTime2D coord2D = (CoordinateTime2D) coord;
sizeArray[count++] = coord2D.getNruns();
sizeArray[count++] = coord2D.getNtimes();
} else {
index[count2++] = builder.getIndex(gr);
}
}
saBuilder.add(gr, info, index);
}
return saBuilder.finish();
} */
/**
* Reindex the sparse array, based on the new Coordinates.
* Do this by running all the Records through the Coordinates, assigning each to a new spot in the new sparse array.
*
* @param prev must have same list of Coordinates, with possibly additional values.
*/
public CoordinateND<T> reindex(List<Coordinate> newCoords, CoordinateND<T> prev) {
SparseArray<T> prevSA = prev.getSparseArray();
List<Coordinate> prevCoords = prev.getCoordinates();
// make a working sparse array with new shape
int[] sizeArray = new int[newCoords.size()];
for (int i = 0; i < newCoords.size(); i++) {
Coordinate coord = newCoords.get(i);
sizeArray[i] = (coord instanceof CoordinateTime2D) ? ((CoordinateTime2D) coord).getNtimes() : coord.getSize();
}
SparseArray.Builder<T> workingSAbuilder = new SparseArray.Builder<>(sizeArray);
// for each coordinate, calculate the map of oldIndex -> newIndex
List<IndexMapIF> indexMaps = new ArrayList<>();
int count = 0;
for (Coordinate curr : newCoords) {
if (curr.getType() == Coordinate.Type.time2D)
indexMaps.add(new Time2DIndexMap((CoordinateTime2D) curr, (CoordinateTime2D) prevCoords.get(count++)));
else
indexMaps.add(new IndexMap(curr, prevCoords.get(count++)));
}
int[] currIndex = new int[newCoords.size()];
int[] prevIndex = new int[newCoords.size()];
int[] track = new int[SparseArray.calcTotalSize(sizeArray)];
// iterate through the contents of the prev track array
Section section = new Section(prevSA.getShape());
Section.Iterator iter = section.getIterator(prevSA.getShape());
while (iter.hasNext()) {
int oldTrackIdx = iter.next(prevIndex); // gets both the oldTrackIdx (1D) and prevIndex (nD)
int oldTrackValue = prevSA.getTrack(oldTrackIdx);
if (oldTrackValue == 0) continue; // skip missing values
// calculate position in the current track array, and store the value there
int coordIdx = 0;
for (IndexMapIF indexMap : indexMaps) {
currIndex[coordIdx] = indexMap.map(prevIndex[coordIdx]);
coordIdx++;
}
int trackIdx = workingSAbuilder.calcIndex(currIndex);
if (trackIdx >= track.length)
System.out.println("HEY CoordinateND trackIdx >= track.length");
track[trackIdx] = oldTrackValue;
}
// now that we have the track, make the real SA
SparseArray<T> newSA = new SparseArray<>(sizeArray, track, prevSA.getContent(), prevSA.getNdups()); // content (list of records) is the same
return new CoordinateND<>(newCoords, newSA); // reindexed result
}
//////////////////////////////////////////////
// a quick lookup of values from prev coordinate to current coordinate.
private static interface IndexMapIF {
int map(int oldIndex);
}
private static class IndexMap implements IndexMapIF {
boolean identity = true;
int[] indexMap;
IndexMap(Coordinate curr, Coordinate prev) {
identity = curr.equals(prev);
if (identity) return;
assert curr.getType() == prev.getType() : curr.getType()+" != "+prev.getType();
int count = 0;
Map<Object, Integer> currValMap = new HashMap<>();
if (curr.getValues() == null)
System.out.println("HEY CoordinateND curr.getValues() == null");
for (Object val : curr.getValues()) currValMap.put(val, count++);
count = 0;
indexMap = new int[prev.getSize()];
for (Object val : prev.getValues()) {
indexMap[count++] = currValMap.get(val); // where does this value fit in the curr coordinates?
}
}
public int map(int oldIndex) {
if (identity) return oldIndex;
return indexMap[oldIndex];
}
}
private static class Time2DIndexMap implements IndexMapIF {
int[] indexMap;
Time2DIndexMap(CoordinateTime2D curr, CoordinateTime2D prev) {
assert curr.getType() == prev.getType() : curr.getType()+" != "+prev.getType();
int[] index2D = new int[2];
Map<Object, Integer> currValMap = new HashMap<>();
for (Object val : curr.getValues()) {
boolean ok = curr.getIndex((CoordinateTime2D.Time2D) val, index2D);
if (!ok)
System.out.println("HEY CoordinateND !ok"); // LOOK
currValMap.put(val, index2D[1]); // want the time index
}
int count = 0;
indexMap = new int[prev.getSize()];
for (Object val : prev.getValues()) {
if (currValMap.get(val) == null)
System.out.printf("HEY Time2DIndexMap %s%n", val); // LOOK
else
indexMap[count++] = currValMap.get(val); // where does this value fit in the curr coordinates?
}
}
public int map(int oldIndex) {
return indexMap[oldIndex];
}
}
}
}