package mil.nga.giat.geowave.core.index.sfc.binned;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import mil.nga.giat.geowave.core.index.ByteArrayId;
import mil.nga.giat.geowave.core.index.ByteArrayRange;
import mil.nga.giat.geowave.core.index.ByteArrayUtils;
import mil.nga.giat.geowave.core.index.Coordinate;
import mil.nga.giat.geowave.core.index.CoordinateRange;
import mil.nga.giat.geowave.core.index.MultiDimensionalCoordinateRanges;
import mil.nga.giat.geowave.core.index.dimension.NumericDimensionDefinition;
import mil.nga.giat.geowave.core.index.dimension.bin.BinRange;
import mil.nga.giat.geowave.core.index.sfc.RangeDecomposition;
import mil.nga.giat.geowave.core.index.sfc.SpaceFillingCurve;
import mil.nga.giat.geowave.core.index.sfc.data.BasicNumericDataset;
import mil.nga.giat.geowave.core.index.sfc.data.BinnedNumericDataset;
import mil.nga.giat.geowave.core.index.sfc.data.MultiDimensionalNumericData;
import mil.nga.giat.geowave.core.index.sfc.data.NumericData;
import mil.nga.giat.geowave.core.index.sfc.data.NumericRange;
public class BinnedSFCUtils
{
public static List<ByteArrayRange> getQueryRanges(
final BinnedNumericDataset[] binnedQueries,
final SpaceFillingCurve sfc,
final int maxRanges,
final byte tier ) {
final List<ByteArrayRange> queryRanges = new ArrayList<ByteArrayRange>();
int maxRangeDecompositionPerBin = maxRanges;
if ((maxRanges > 1) && (binnedQueries.length > 1)) {
maxRangeDecompositionPerBin = (int) Math.ceil((double) maxRanges / (double) binnedQueries.length);
}
for (final BinnedNumericDataset binnedQuery : binnedQueries) {
final RangeDecomposition rangeDecomp = sfc.decomposeRange(
binnedQuery,
true,
maxRangeDecompositionPerBin);
final byte[] tierAndBinId = ByteArrayUtils.combineArrays(
new byte[] {
tier
// we're assuming tiers only go to 127 (the max byte
// value)
},
binnedQuery.getBinId());
for (final ByteArrayRange range : rangeDecomp.getRanges()) {
queryRanges.add(new ByteArrayRange(
new ByteArrayId(
ByteArrayUtils.combineArrays(
tierAndBinId,
range.getStart().getBytes())),
new ByteArrayId(
ByteArrayUtils.combineArrays(
tierAndBinId,
range.getEnd().getBytes()))));
}
}
return queryRanges;
}
public static MultiDimensionalCoordinateRanges getCoordinateRanges(
final BinRange[][] binRangesPerDimension,
final SpaceFillingCurve sfc,
final int numDimensions,
final byte tier ) {
final CoordinateRange[][] coordinateRangesPerDimension = new CoordinateRange[numDimensions][];
for (int d = 0; d < coordinateRangesPerDimension.length; d++) {
coordinateRangesPerDimension[d] = new CoordinateRange[binRangesPerDimension[d].length];
for (int i = 0; i < binRangesPerDimension[d].length; i++) {
final long[] range = sfc.normalizeRange(
binRangesPerDimension[d][i].getNormalizedMin(),
binRangesPerDimension[d][i].getNormalizedMax(),
d);
coordinateRangesPerDimension[d][i] = new CoordinateRange(
range[0],
range[1],
binRangesPerDimension[d][i].getBinId());
}
}
return new MultiDimensionalCoordinateRanges(
new byte[] {
tier
},
coordinateRangesPerDimension);
}
public static ByteArrayId getSingleBinnedRowId(
final BigInteger rowCount,
final byte multiDimensionalId,
final BinnedNumericDataset index,
final SpaceFillingCurve sfc ) {
if (rowCount.equals(BigInteger.ONE)) {
final byte[] tierAndBinId = ByteArrayUtils.combineArrays(
new byte[] {
multiDimensionalId
},
index.getBinId());
final double[] minValues = index.getMinValuesPerDimension();
final double[] maxValues = index.getMaxValuesPerDimension();
byte[] singleId = null;
if (Arrays.equals(
maxValues,
minValues)) {
singleId = sfc.getId(minValues);
}
else {
byte[] minId = sfc.getId(minValues);
byte[] maxId = sfc.getId(maxValues);
if (Arrays.equals(
minId,
maxId)) {
singleId = minId;
}
}
if (singleId != null) {
return new ByteArrayId(
ByteArrayUtils.combineArrays(
tierAndBinId,
singleId));
}
}
return null;
}
public static Coordinate[] getCoordinatesForId(
final byte[] rowId,
final NumericDimensionDefinition[] baseDefinitions,
final SpaceFillingCurve sfc ) {
final SFCIdAndBinInfo sfcIdAndBinInfo = getSFCIdAndBinInfo(
rowId,
baseDefinitions);
final long[] coordinateValues = sfc.getCoordinates(sfcIdAndBinInfo.sfcId);
final Coordinate[] retVal = new Coordinate[coordinateValues.length];
for (int i = 0; i < coordinateValues.length; i++) {
final byte[] bin = sfcIdAndBinInfo.binIds.get(i);
retVal[i] = new Coordinate(
coordinateValues[i],
bin);
}
return retVal;
}
public static MultiDimensionalNumericData getRangeForId(
final byte[] rowId,
final NumericDimensionDefinition[] baseDefinitions,
final SpaceFillingCurve sfc ) {
final SFCIdAndBinInfo sfcIdAndBinInfo = getSFCIdAndBinInfo(
rowId,
baseDefinitions);
final MultiDimensionalNumericData numericData = sfc.getRanges(sfcIdAndBinInfo.sfcId);
// now we need to unapply the bins to the data, denormalizing the
// ranges to the native bounds
if (sfcIdAndBinInfo.rowIdOffset > 1) {
final NumericData[] data = numericData.getDataPerDimension();
for (final Entry<Integer, byte[]> entry : sfcIdAndBinInfo.binIds.entrySet()) {
final int dimension = entry.getKey();
final NumericRange range = baseDefinitions[dimension].getDenormalizedRange(new BinRange(
entry.getValue(),
data[dimension].getMin(),
data[dimension].getMax(),
false));
data[dimension] = range;
}
return new BasicNumericDataset(
data);
}
return numericData;
}
private static SFCIdAndBinInfo getSFCIdAndBinInfo(
final byte[] rowId,
final NumericDimensionDefinition[] baseDefinitions ) {
final Map<Integer, byte[]> binIds = new HashMap<Integer, byte[]>();
// one for the tier
int rowIdOffset = 1;
for (int dimensionIdx = 0; dimensionIdx < baseDefinitions.length; dimensionIdx++) {
final int binSize = baseDefinitions[dimensionIdx].getFixedBinIdSize();
if (binSize > 0) {
binIds.put(
dimensionIdx,
Arrays.copyOfRange(
rowId,
rowIdOffset,
rowIdOffset + binSize));
rowIdOffset += binSize;
}
}
final byte[] sfcId = Arrays.copyOfRange(
rowId,
rowIdOffset,
rowId.length);
return new SFCIdAndBinInfo(
sfcId,
binIds,
rowIdOffset);
}
private static class SFCIdAndBinInfo
{
private final byte[] sfcId;
private final Map<Integer, byte[]> binIds;
private final int rowIdOffset;
public SFCIdAndBinInfo(
final byte[] sfcId,
final Map<Integer, byte[]> binIds,
final int rowIdOffset ) {
super();
this.sfcId = sfcId;
this.binIds = binIds;
this.rowIdOffset = rowIdOffset;
}
}
}