package mil.nga.giat.geowave.core.index.sfc.data;
import java.nio.ByteBuffer;
import mil.nga.giat.geowave.core.index.ByteArrayUtils;
import mil.nga.giat.geowave.core.index.PersistenceUtils;
import mil.nga.giat.geowave.core.index.dimension.NumericDimensionDefinition;
import mil.nga.giat.geowave.core.index.dimension.bin.BinRange;
/**
* The Binned Numeric Dataset class creates an object that associates a
* multi-dimensional index range to a particular bin ID.
*
*/
public class BinnedNumericDataset implements
MultiDimensionalNumericData
{
private byte[] binId;
private MultiDimensionalNumericData indexRanges;
private boolean fullExtent;
protected BinnedNumericDataset() {}
/**
*
* @param binId
* a unique ID associated with the BinnedQuery object
* @param indexRanges
* multi-dimensional range data
*/
public BinnedNumericDataset(
final byte[] binId,
final MultiDimensionalNumericData indexRanges,
final boolean fullExtent ) {
this.binId = binId;
this.indexRanges = indexRanges;
this.fullExtent = fullExtent;
}
public boolean isFullExtent() {
return fullExtent;
}
/**
* @return an array of NumericData objects associated with this object.
*/
@Override
public NumericData[] getDataPerDimension() {
return indexRanges.getDataPerDimension();
}
/**
* @return an array of max values associated with each dimension
*/
@Override
public double[] getMaxValuesPerDimension() {
return indexRanges.getMaxValuesPerDimension();
}
/**
* @return an array of min values associated with each dimension
*/
@Override
public double[] getMinValuesPerDimension() {
return indexRanges.getMinValuesPerDimension();
}
/**
* @return an array of centroid values associated with each dimension
*/
@Override
public double[] getCentroidPerDimension() {
return indexRanges.getCentroidPerDimension();
}
/**
* @return the number of total dimensions
*/
@Override
public int getDimensionCount() {
return indexRanges.getDimensionCount();
}
/**
* @return a unique ID associated with this object
*/
public byte[] getBinId() {
return binId;
}
/**
* This method is responsible for translating a query into appropriate
* normalized and binned (if necessary) queries that can be used by the
* underlying index implementation. For example, for unbounded dimensions
* such as time, an incoming query of July 2012 to July 2013 may get
* translated into 2 binned queries representing the 2012 portion of the
* query and the 2013 portion, each normalized to millis from the beginning
* of the year.
*
* @param numericData
* the incoming query into the index implementation, to be
* translated into normalized, binned queries
* @param dimensionDefinitions
* the definition for the dimensions
* @return normalized indexes
*/
public static BinnedNumericDataset[] applyBins(
final MultiDimensionalNumericData numericData,
final NumericDimensionDefinition[] dimensionDefinitions ) {
if (dimensionDefinitions.length == 0) {
return new BinnedNumericDataset[0];
}
final BinRange[][] binRangesPerDimension = getBinnedRangesPerDimension(
numericData,
dimensionDefinitions);
int numBinnedQueries = 1;
for (int d = 0; d < dimensionDefinitions.length; d++) {
numBinnedQueries *= binRangesPerDimension[d].length;
}
// now we need to combine all permutations of bin ranges into
// BinnedQuery objects
final BinnedNumericDataset[] binnedQueries = new BinnedNumericDataset[numBinnedQueries];
for (int d = 0; d < dimensionDefinitions.length; d++) {
for (int b = 0; b < binRangesPerDimension[d].length; b++) {
for (int i = b; i < numBinnedQueries; i += binRangesPerDimension[d].length) {
final NumericData[] rangePerDimension;
if (binnedQueries[i] == null) {
rangePerDimension = new NumericRange[dimensionDefinitions.length];
binnedQueries[i] = new BinnedNumericDataset(
binRangesPerDimension[d][b].getBinId(),
new BasicNumericDataset(
rangePerDimension),
binRangesPerDimension[d][b].isFullExtent());
}
else {
// because binned queries were intended to be immutable,
// re-instantiate the object
rangePerDimension = binnedQueries[i].getDataPerDimension();
final byte[] combinedBinId = ByteArrayUtils.combineArrays(
binnedQueries[i].getBinId(),
binRangesPerDimension[d][b].getBinId());
binnedQueries[i] = new BinnedNumericDataset(
combinedBinId,
new BasicNumericDataset(
rangePerDimension),
binnedQueries[i].fullExtent |= binRangesPerDimension[d][b].isFullExtent());
}
rangePerDimension[d] = new NumericRange(
binRangesPerDimension[d][b].getNormalizedMin(),
binRangesPerDimension[d][b].getNormalizedMax());
}
}
}
return binnedQueries;
}
public static BinRange[][] getBinnedRangesPerDimension(
final MultiDimensionalNumericData numericData,
final NumericDimensionDefinition[] dimensionDefinitions ) {
if (dimensionDefinitions.length == 0) {
return new BinRange[0][];
}
final BinRange[][] binRangesPerDimension = new BinRange[dimensionDefinitions.length][];
for (int d = 0; d < dimensionDefinitions.length; d++) {
binRangesPerDimension[d] = dimensionDefinitions[d]
.getNormalizedRanges(numericData.getDataPerDimension()[d]);
}
return binRangesPerDimension;
}
@Override
public boolean isEmpty() {
return indexRanges.isEmpty();
}
@Override
public byte[] toBinary() {
final byte[] indexRangesBinary = PersistenceUtils.toBinary(indexRanges);
final ByteBuffer buf = ByteBuffer.allocate(5 + indexRangesBinary.length + binId.length);
buf.put((byte) (fullExtent ? 1 : 0));
buf.putInt(binId.length);
buf.put(binId);
buf.put(indexRangesBinary);
return null;
}
@Override
public void fromBinary(
final byte[] bytes ) {
final ByteBuffer buf = ByteBuffer.wrap(bytes);
fullExtent = (buf.get() == 1);
binId = new byte[buf.getInt()];
buf.get(binId);
final byte[] indexRangesBinary = new byte[bytes.length - 5 - binId.length];
buf.get(indexRangesBinary);
indexRanges = PersistenceUtils.fromBinary(
indexRangesBinary,
MultiDimensionalNumericData.class);
}
}