package mil.nga.giat.geowave.core.index.sfc.tiered;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import mil.nga.giat.geowave.core.index.ByteArrayId;
import mil.nga.giat.geowave.core.index.ByteArrayRange;
import mil.nga.giat.geowave.core.index.IndexMetaData;
import mil.nga.giat.geowave.core.index.MultiDimensionalCoordinateRanges;
import mil.nga.giat.geowave.core.index.MultiDimensionalCoordinates;
import mil.nga.giat.geowave.core.index.NumericIndexStrategy;
import mil.nga.giat.geowave.core.index.PersistenceUtils;
import mil.nga.giat.geowave.core.index.StringUtils;
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.SpaceFillingCurve;
import mil.nga.giat.geowave.core.index.sfc.binned.BinnedSFCUtils;
import mil.nga.giat.geowave.core.index.sfc.data.BinnedNumericDataset;
import mil.nga.giat.geowave.core.index.sfc.data.MultiDimensionalNumericData;
/**
* This class wraps a single SpaceFillingCurve implementation with a tiered
* approach to indexing (an SFC with a tier ID). This can be utilized by an
* overall HierarchicalNumericIndexStrategy as an encapsulated sub-strategy.
*
*/
public class SingleTierSubStrategy implements
NumericIndexStrategy
{
private SpaceFillingCurve sfc;
private NumericDimensionDefinition[] baseDefinitions;
public byte tier;
protected SingleTierSubStrategy() {}
public SingleTierSubStrategy(
final SpaceFillingCurve sfc,
final NumericDimensionDefinition[] baseDefinitions,
final byte tier ) {
this.sfc = sfc;
this.baseDefinitions = baseDefinitions;
this.tier = tier;
}
@Override
public List<ByteArrayRange> getQueryRanges(
final MultiDimensionalNumericData indexedRange,
final IndexMetaData... hints ) {
return getQueryRanges(
indexedRange,
TieredSFCIndexStrategy.DEFAULT_MAX_RANGES);
}
@Override
public List<ByteArrayRange> getQueryRanges(
final MultiDimensionalNumericData indexedRange,
final int maxRangeDecomposition,
final IndexMetaData... hints ) {
final BinnedNumericDataset[] binnedQueries = BinnedNumericDataset.applyBins(
indexedRange,
baseDefinitions);
return BinnedSFCUtils.getQueryRanges(
binnedQueries,
sfc,
maxRangeDecomposition,
tier);
}
@Override
public MultiDimensionalNumericData getRangeForId(
final ByteArrayId insertionId ) {
final byte[] rowId = insertionId.getBytes();
return BinnedSFCUtils.getRangeForId(
rowId,
baseDefinitions,
sfc);
}
@Override
public MultiDimensionalCoordinates getCoordinatesPerDimension(
final ByteArrayId insertionId ) {
final byte[] rowId = insertionId.getBytes();
return new MultiDimensionalCoordinates(
new byte[] {
tier
},
BinnedSFCUtils.getCoordinatesForId(
rowId,
baseDefinitions,
sfc));
}
@Override
public List<ByteArrayId> getInsertionIds(
final MultiDimensionalNumericData indexedData ) {
return getInsertionIds(
indexedData,
1);
}
@Override
public List<ByteArrayId> getInsertionIds(
final MultiDimensionalNumericData indexedData,
final int maxDuplicateInsertionIds ) {
// we need to duplicate per bin so we can't adhere to max duplication
// anyways
final BinnedNumericDataset[] ranges = BinnedNumericDataset.applyBins(
indexedData,
baseDefinitions);
// place each of these indices into a single row ID at a tier that will
// fit its min and max
final List<ByteArrayId> rowIds = new ArrayList<ByteArrayId>();
for (final BinnedNumericDataset range : ranges) {
final List<ByteArrayId> binRowIds = TieredSFCIndexStrategy.getRowIdsAtTier(
range,
tier,
sfc,
null,
tier);
if (binRowIds != null) {
rowIds.addAll(binRowIds);
}
}
return rowIds;
}
@Override
public NumericDimensionDefinition[] getOrderedDimensionDefinitions() {
return baseDefinitions;
}
@Override
public String getId() {
return StringUtils.intToString(hashCode());
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + Arrays.hashCode(baseDefinitions);
result = (prime * result) + ((sfc == null) ? 0 : sfc.hashCode());
result = (prime * result) + tier;
return result;
}
@Override
public boolean equals(
final Object obj ) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final SingleTierSubStrategy other = (SingleTierSubStrategy) obj;
if (!Arrays.equals(
baseDefinitions,
other.baseDefinitions)) {
return false;
}
if (sfc == null) {
if (other.sfc != null) {
return false;
}
}
else if (!sfc.equals(other.sfc)) {
return false;
}
if (tier != other.tier) {
return false;
}
return true;
}
@Override
public byte[] toBinary() {
int byteBufferLength = 5;
final List<byte[]> dimensionBinaries = new ArrayList<byte[]>(
baseDefinitions.length);
final byte[] sfcBinary = PersistenceUtils.toBinary(sfc);
byteBufferLength += (4 + sfcBinary.length);
for (final NumericDimensionDefinition dimension : baseDefinitions) {
final byte[] dimensionBinary = PersistenceUtils.toBinary(dimension);
byteBufferLength += (4 + dimensionBinary.length);
dimensionBinaries.add(dimensionBinary);
}
final ByteBuffer buf = ByteBuffer.allocate(byteBufferLength);
buf.put(tier);
buf.putInt(baseDefinitions.length);
buf.putInt(sfcBinary.length);
buf.put(sfcBinary);
for (final byte[] dimensionBinary : dimensionBinaries) {
buf.putInt(dimensionBinary.length);
buf.put(dimensionBinary);
}
return buf.array();
}
@Override
public void fromBinary(
final byte[] bytes ) {
final ByteBuffer buf = ByteBuffer.wrap(bytes);
tier = buf.get();
final int numDimensions = buf.getInt();
baseDefinitions = new NumericDimensionDefinition[numDimensions];
final byte[] sfcBinary = new byte[buf.getInt()];
buf.get(sfcBinary);
sfc = PersistenceUtils.fromBinary(
sfcBinary,
SpaceFillingCurve.class);
for (int i = 0; i < numDimensions; i++) {
final byte[] dim = new byte[buf.getInt()];
buf.get(dim);
baseDefinitions[i] = PersistenceUtils.fromBinary(
dim,
NumericDimensionDefinition.class);
}
}
@Override
public double[] getHighestPrecisionIdRangePerDimension() {
return sfc.getInsertionIdRangePerDimension();
}
@Override
public Set<ByteArrayId> getNaturalSplits() {
return null;
}
@Override
public int getByteOffsetFromDimensionalIndex() {
int rowIdOffset = 1;
for (int dimensionIdx = 0; dimensionIdx < baseDefinitions.length; dimensionIdx++) {
final int binSize = baseDefinitions[dimensionIdx].getFixedBinIdSize();
if (binSize > 0) {
rowIdOffset += binSize;
}
}
return rowIdOffset;
}
@Override
public List<IndexMetaData> createMetaData() {
return Collections.<IndexMetaData> emptyList();
}
@Override
public MultiDimensionalCoordinateRanges[] getCoordinateRangesPerDimension(
final MultiDimensionalNumericData dataRange,
final IndexMetaData... hints ) {
final BinRange[][] binRangesPerDimension = BinnedNumericDataset.getBinnedRangesPerDimension(
dataRange,
baseDefinitions);
return new MultiDimensionalCoordinateRanges[] {
BinnedSFCUtils.getCoordinateRanges(
binRangesPerDimension,
sfc,
baseDefinitions.length,
tier)
};
}
}