package mil.nga.giat.geowave.core.geotime.index.sfc.hilbert.tiered;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import mil.nga.giat.geowave.core.geotime.index.dimension.LatitudeDefinition;
import mil.nga.giat.geowave.core.geotime.index.dimension.LongitudeDefinition;
import mil.nga.giat.geowave.core.geotime.index.dimension.TemporalBinningStrategy.Unit;
import mil.nga.giat.geowave.core.geotime.index.dimension.TimeDefinition;
import mil.nga.giat.geowave.core.geotime.ingest.SpatialDimensionalityTypeProvider;
import mil.nga.giat.geowave.core.geotime.ingest.SpatialTemporalDimensionalityTypeProvider;
import mil.nga.giat.geowave.core.index.ByteArrayId;
import mil.nga.giat.geowave.core.index.ByteArrayRange;
import mil.nga.giat.geowave.core.index.NumericIndexStrategy;
import mil.nga.giat.geowave.core.index.dimension.NumericDimensionDefinition;
import mil.nga.giat.geowave.core.index.sfc.SFCFactory.SFCType;
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.NumericRange;
import mil.nga.giat.geowave.core.index.sfc.data.NumericValue;
import mil.nga.giat.geowave.core.index.sfc.tiered.TieredSFCIndexFactory;
import mil.nga.giat.geowave.core.store.index.BasicIndexModel;
import mil.nga.giat.geowave.core.store.index.CustomIdIndex;
public class TieredSFCIndexStrategyTest
{
public static final int[] DEFINED_BITS_OF_PRECISION = new int[] {
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
13,
18,
31
};
NumericDimensionDefinition[] SPATIAL_TEMPORAL_DIMENSIONS = new NumericDimensionDefinition[] {
new LongitudeDefinition(),
new LatitudeDefinition(
true),
new TimeDefinition(
Unit.YEAR),
};
private static final double QUERY_RANGE_EPSILON = 1E-12;
@Test
public void testSingleEntry() {
final Calendar cal = Calendar.getInstance();
final NumericData[] dataPerDimension1 = new NumericData[SPATIAL_TEMPORAL_DIMENSIONS.length];
dataPerDimension1[0] = new NumericValue(
45);
dataPerDimension1[1] = new NumericValue(
45);
dataPerDimension1[2] = new NumericValue(
cal.getTimeInMillis());
final int year = cal.get(Calendar.YEAR);
cal.set(
Calendar.DAY_OF_YEAR,
1);
final NumericData[] dataPerDimension2 = new NumericData[SPATIAL_TEMPORAL_DIMENSIONS.length];
dataPerDimension2[0] = new NumericValue(
45);
dataPerDimension2[1] = new NumericValue(
45);
dataPerDimension2[2] = new NumericValue(
cal.getTimeInMillis());
cal.set(
Calendar.YEAR,
year - 1);
final NumericData[] dataPerDimension3 = new NumericData[SPATIAL_TEMPORAL_DIMENSIONS.length];
dataPerDimension3[0] = new NumericValue(
45);
dataPerDimension3[1] = new NumericValue(
45);
dataPerDimension3[2] = new NumericValue(
cal.getTimeInMillis());
MultiDimensionalNumericData indexedData = new BasicNumericDataset(
dataPerDimension1);
final NumericIndexStrategy strategy = new SpatialTemporalDimensionalityTypeProvider()
.createPrimaryIndex()
.getIndexStrategy();
final List<ByteArrayId> ids1 = strategy.getInsertionIds(indexedData);
assertEquals(
1,
ids1.size());
assertEquals(
13,
ids1.get(
0).getBytes().length);
// same bin
indexedData = new BasicNumericDataset(
dataPerDimension2);
final List<ByteArrayId> ids2 = strategy.getInsertionIds(indexedData);
assertEquals(
1,
ids2.size());
assertTrue(compare(
ids1.get(
0).getBytes(),
ids2.get(
0).getBytes(),
5));
// different bin
indexedData = new BasicNumericDataset(
dataPerDimension3);
final List<ByteArrayId> ids3 = strategy.getInsertionIds(indexedData);
assertEquals(
1,
ids3.size());
assertFalse(compare(
ids1.get(
0).getBytes(),
ids3.get(
0).getBytes(),
5));
}
@Test
public void testPredefinedSpatialEntries()
throws Exception {
final NumericIndexStrategy strategy = TieredSFCIndexFactory.createDefinedPrecisionTieredStrategy(
new NumericDimensionDefinition[] {
new LongitudeDefinition(),
new LatitudeDefinition(
true)
},
new int[][] {
DEFINED_BITS_OF_PRECISION.clone(),
DEFINED_BITS_OF_PRECISION.clone()
},
SFCType.HILBERT);
for (int sfcIndex = 0; sfcIndex < DEFINED_BITS_OF_PRECISION.length; sfcIndex++) {
final NumericData[] dataPerDimension = new NumericData[2];
final double precision = 360 / Math.pow(
2,
DEFINED_BITS_OF_PRECISION[sfcIndex]);
if (precision > 180) {
dataPerDimension[0] = new NumericRange(
-180,
180);
dataPerDimension[1] = new NumericRange(
-90,
90);
}
else {
dataPerDimension[0] = new NumericRange(
0,
precision);
dataPerDimension[1] = new NumericRange(
-precision,
0);
}
final MultiDimensionalNumericData indexedData = new BasicNumericDataset(
dataPerDimension);
final List<ByteArrayId> ids = strategy.getInsertionIds(indexedData);
final NumericData[] queryRangePerDimension = new NumericData[2];
queryRangePerDimension[0] = new NumericRange(
dataPerDimension[0].getMin() + QUERY_RANGE_EPSILON,
dataPerDimension[0].getMax() - QUERY_RANGE_EPSILON);
queryRangePerDimension[1] = new NumericRange(
dataPerDimension[1].getMin() + QUERY_RANGE_EPSILON,
dataPerDimension[1].getMax() - QUERY_RANGE_EPSILON);
final MultiDimensionalNumericData queryData = new BasicNumericDataset(
queryRangePerDimension);
final List<ByteArrayRange> queryRanges = strategy.getQueryRanges(queryData);
final Set<Byte> queryRangeTiers = new HashSet<Byte>();
boolean rangeAtTierFound = false;
for (final ByteArrayRange range : queryRanges) {
final byte tier = range.getStart().getBytes()[0];
queryRangeTiers.add(range.getStart().getBytes()[0]);
if (tier == DEFINED_BITS_OF_PRECISION[sfcIndex]) {
if (rangeAtTierFound) {
throw new Exception(
"multiple ranges were found unexpectedly for tier " + tier);
}
assertEquals(
"this range is an exact fit, so it should have exactly one value for tier "
+ DEFINED_BITS_OF_PRECISION[sfcIndex],
range.getStart(),
range.getEnd());
rangeAtTierFound = true;
}
}
if (!rangeAtTierFound) {
throw new Exception(
"no ranges were found at the expected exact fit tier " + DEFINED_BITS_OF_PRECISION[sfcIndex]);
}
// ensure the first byte is equal to the appropriate number of bits
// of precision
if ((ids.get(
0).getBytes()[0] == 0)
|| ((sfcIndex == (DEFINED_BITS_OF_PRECISION.length - 1)) || (DEFINED_BITS_OF_PRECISION[sfcIndex + 1] != (DEFINED_BITS_OF_PRECISION[sfcIndex] + 1)))) {
assertEquals(
"Insertion ID expected to be exact match at tier " + DEFINED_BITS_OF_PRECISION[sfcIndex],
DEFINED_BITS_OF_PRECISION[sfcIndex],
ids.get(
0).getBytes()[0]);
assertEquals(
"Insertion ID size expected to be 1 at tier " + DEFINED_BITS_OF_PRECISION[sfcIndex],
1,
ids.size());
}
else {
assertEquals(
"Insertion ID expected to be duplicated at tier " + DEFINED_BITS_OF_PRECISION[sfcIndex + 1],
DEFINED_BITS_OF_PRECISION[sfcIndex + 1],
ids.get(
0).getBytes()[0]);
// if the precision is within the bounds of longitude but not
// within latitude we will end up with 2 (rectangular
// decomposition)
// otherwise we will get a square decomposition of 4 ids
final int expectedIds = (precision > 90) && (precision <= 180) ? 2 : 4;
assertEquals(
"Insertion ID size expected to be " + expectedIds + " at tier "
+ DEFINED_BITS_OF_PRECISION[sfcIndex + 1],
expectedIds,
ids.size());
}
}
}
@Test
public void testOneEstimatedDuplicateInsertion()
throws Exception {
final NumericIndexStrategy strategy = TieredSFCIndexFactory.createFullIncrementalTieredStrategy(
new NumericDimensionDefinition[] {
new LongitudeDefinition(),
new LatitudeDefinition(
true)
},
new int[] {
31,
31
},
SFCType.HILBERT);
for (final int element : DEFINED_BITS_OF_PRECISION) {
final NumericData[] dataPerDimension = new NumericData[2];
final double precision = 360 / Math.pow(
2,
element);
if (precision > 180) {
dataPerDimension[0] = new NumericRange(
-180,
180);
dataPerDimension[1] = new NumericRange(
-90,
90);
}
else {
dataPerDimension[0] = new NumericRange(
0,
precision);
dataPerDimension[1] = new NumericRange(
-precision,
0);
}
final MultiDimensionalNumericData indexedData = new BasicNumericDataset(
dataPerDimension);
final List<ByteArrayId> ids = strategy.getInsertionIds(
indexedData,
1);
assertEquals(
"Insertion ID size expected to be 1 at tier " + element,
1,
ids.size());
// ensure the first byte is equal to the appropriate number of bits
// of precision
assertEquals(
"Insertion ID expected to be exact match at tier " + element,
element,
ids.get(
0).getBytes()[0]);
}
}
@Test
public void testRegions()
throws ParseException {
final Calendar cal = Calendar.getInstance();
final Calendar calEnd = Calendar.getInstance();
final SimpleDateFormat format = new SimpleDateFormat(
"MM-dd-yyyy HH:mm:ss");
cal.setTime(format.parse("03-03-1999 11:01:01"));
calEnd.setTime(format.parse("03-03-1999 11:05:01"));
final NumericData[] dataPerDimension1 = new NumericData[SPATIAL_TEMPORAL_DIMENSIONS.length];
dataPerDimension1[0] = new NumericRange(
45.170,
45.173);
dataPerDimension1[1] = new NumericRange(
50.190,
50.192);
dataPerDimension1[2] = new NumericRange(
cal.getTimeInMillis(),
calEnd.getTimeInMillis());
final int year = cal.get(Calendar.YEAR);
cal.set(
Calendar.DAY_OF_YEAR,
1);
final NumericData[] dataPerDimension2 = new NumericData[SPATIAL_TEMPORAL_DIMENSIONS.length];
dataPerDimension2[0] = new NumericRange(
45,
50);
dataPerDimension2[1] = new NumericRange(
45,
50);
dataPerDimension2[2] = new NumericRange(
cal.getTimeInMillis(),
calEnd.getTimeInMillis());
cal.set(
Calendar.YEAR,
year - 1);
calEnd.set(
Calendar.YEAR,
year - 1);
final NumericData[] dataPerDimension3 = new NumericData[SPATIAL_TEMPORAL_DIMENSIONS.length];
dataPerDimension3[0] = new NumericRange(
45.1701,
45.1703);
dataPerDimension3[1] = new NumericRange(
50.1901,
50.1902);
dataPerDimension3[2] = new NumericRange(
cal.getTimeInMillis(),
calEnd.getTimeInMillis());
MultiDimensionalNumericData indexedData = new BasicNumericDataset(
dataPerDimension1);
final NumericIndexStrategy strategy = TieredSFCIndexFactory.createEqualIntervalPrecisionTieredStrategy(
SPATIAL_TEMPORAL_DIMENSIONS,
new int[] {
20,
20,
20
},
SFCType.HILBERT,
4);
final List<ByteArrayId> ids1 = strategy.getInsertionIds(indexedData);
assertEquals(
1,
ids1.size());
assertEquals(
10,
ids1.get(
0).getBytes().length);
// different bin bin
indexedData = new BasicNumericDataset(
dataPerDimension2);
final List<ByteArrayId> ids2 = strategy.getInsertionIds(indexedData);
assertEquals(
1,
ids2.size());
// different tier
assertFalse(compare(
ids1.get(
0).getBytes(),
ids2.get(
0).getBytes(),
1));
// same time
assertTrue(compare(
ids1.get(
0).getBytes(),
ids2.get(
0).getBytes(),
1,
5));
// different bin
indexedData = new BasicNumericDataset(
dataPerDimension3);
final List<ByteArrayId> ids3 = strategy.getInsertionIds(indexedData);
assertEquals(
1,
ids3.size());
assertFalse(compare(
ids1.get(
0).getBytes(),
ids3.get(
0).getBytes(),
1,
5));
}
private boolean compare(
final byte[] one,
final byte[] two,
final int start,
final int stop ) {
return Arrays.equals(
Arrays.copyOfRange(
one,
start,
stop),
Arrays.copyOfRange(
two,
start,
stop));
}
private boolean compare(
final byte[] one,
final byte[] two,
final int length ) {
return Arrays.equals(
Arrays.copyOf(
one,
length),
Arrays.copyOf(
two,
length));
}
}