package mil.nga.giat.geowave.core.index.simple; 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.Coordinate; 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.StringUtils; import mil.nga.giat.geowave.core.index.dimension.BasicDimensionDefinition; import mil.nga.giat.geowave.core.index.dimension.NumericDimensionDefinition; import mil.nga.giat.geowave.core.index.lexicoder.NumberLexicoder; import mil.nga.giat.geowave.core.index.sfc.data.BasicNumericDataset; 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.NumericValue; /** * A simple 1-dimensional NumericIndexStrategy that represents an index of * signed integer values (currently supports 16 bit, 32 bit, and 64 bit * integers). The strategy doesn't use any binning. The ids are simply the byte * arrays of the value. This index strategy will not perform well for inserting * ranges because there will be too much replication of data. * */ public abstract class SimpleNumericIndexStrategy<T extends Number> implements NumericIndexStrategy { private final NumberLexicoder<T> lexicoder; private final NumericDimensionDefinition[] definitions; protected SimpleNumericIndexStrategy( final NumberLexicoder<T> lexicoder ) { this.lexicoder = lexicoder; this.definitions = new NumericDimensionDefinition[] { new BasicDimensionDefinition( lexicoder.getMinimumValue().doubleValue(), lexicoder.getMaximumValue().doubleValue()) }; } protected NumberLexicoder<T> getLexicoder() { return lexicoder; } /** * Cast a double into the type T * * @param value * a double value * @return the value represented as a T */ protected abstract T cast( double value ); /** * Always returns a single range since this is a 1-dimensional index. The * sort-order of the bytes is the same as the sort order of values, so an * indexedRange can be represented by a single contiguous ByteArrayRange. * {@inheritDoc} */ @Override public List<ByteArrayRange> getQueryRanges( final MultiDimensionalNumericData indexedRange, final IndexMetaData... hints ) { return getQueryRanges( indexedRange, -1, hints); } /** * Always returns a single range since this is a 1-dimensional index. The * sort-order of the bytes is the same as the sort order of values, so an * indexedRange can be represented by a single contiguous ByteArrayRange. * {@inheritDoc} */ @Override public List<ByteArrayRange> getQueryRanges( final MultiDimensionalNumericData indexedRange, final int maxEstimatedRangeDecomposition, final IndexMetaData... hints ) { final T min = cast(indexedRange.getMinValuesPerDimension()[0]); final ByteArrayId start = new ByteArrayId( lexicoder.toByteArray(min)); final T max = cast(Math.ceil(indexedRange.getMaxValuesPerDimension()[0])); final ByteArrayId end = new ByteArrayId( lexicoder.toByteArray(max)); final ByteArrayRange range = new ByteArrayRange( start, end); return Collections.singletonList(range); } /** * Returns all of the insertion ids for the range. Since this index strategy * doensn't use binning, it will return the ByteArrayId of every value in * the range (i.e. if you are storing a range using this index strategy, * your data will be replicated for every integer value in the range). * * {@inheritDoc} */ @Override public List<ByteArrayId> getInsertionIds( final MultiDimensionalNumericData indexedData ) { return getInsertionIds( indexedData, -1); } /** * Returns all of the insertion ids for the range. Since this index strategy * doensn't use binning, it will return the ByteArrayId of every value in * the range (i.e. if you are storing a range using this index strategy, * your data will be replicated for every integer value in the range). * * {@inheritDoc} */ @Override public List<ByteArrayId> getInsertionIds( final MultiDimensionalNumericData indexedData, final int maxEstimatedDuplicateIds ) { final long min = (long) indexedData.getMinValuesPerDimension()[0]; final long max = (long) Math.ceil(indexedData.getMaxValuesPerDimension()[0]); final List<ByteArrayId> insertionIds = new ArrayList<>( (int) (max - min) + 1); for (long i = min; i <= max; i++) { insertionIds.add(new ByteArrayId( lexicoder.toByteArray(cast(i)))); } return insertionIds; } @Override public NumericDimensionDefinition[] getOrderedDimensionDefinitions() { return definitions; } @Override public MultiDimensionalNumericData getRangeForId( final ByteArrayId insertionId ) { final long value = Long.class.cast(lexicoder.fromByteArray(insertionId.getBytes())); final NumericData[] dataPerDimension = new NumericData[] { new NumericValue( value) }; return new BasicNumericDataset( dataPerDimension); } @Override public MultiDimensionalCoordinates getCoordinatesPerDimension( final ByteArrayId insertionId ) { return new MultiDimensionalCoordinates( null, new Coordinate[] { new Coordinate( Long.class.cast(lexicoder.fromByteArray(insertionId.getBytes())), null) }); } @Override public MultiDimensionalCoordinateRanges[] getCoordinateRangesPerDimension( final MultiDimensionalNumericData dataRange, final IndexMetaData... hints ) { // TODO: not sure what to do here return new MultiDimensionalCoordinateRanges[] { new MultiDimensionalCoordinateRanges() }; } @Override public double[] getHighestPrecisionIdRangePerDimension() { return new double[] { 1d }; } @Override public String getId() { return StringUtils.intToString(hashCode()); } @Override public int hashCode() { final int prime = 31; int result = 1; result = (prime * result) + Arrays.hashCode(definitions); result = (prime * result) + ((lexicoder == null) ? 0 : lexicoder.hashCode()); 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 SimpleNumericIndexStrategy other = (SimpleNumericIndexStrategy) obj; if (!Arrays.equals( definitions, other.definitions)) { return false; } if (lexicoder == null) { if (other.lexicoder != null) { return false; } } else if (!lexicoder.equals(other.lexicoder)) { return false; } return true; } @Override public Set<ByteArrayId> getNaturalSplits() { return null; } @Override public List<IndexMetaData> createMetaData() { return Collections.emptyList(); } }