package mil.nga.giat.geowave.datastore.accumulo.query; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Random; import mil.nga.giat.geowave.core.geotime.GeometryUtils; import mil.nga.giat.geowave.core.geotime.ingest.SpatialDimensionalityTypeProvider; import mil.nga.giat.geowave.core.geotime.store.dimension.GeometryWrapper; import mil.nga.giat.geowave.core.geotime.store.query.SpatialQuery; import mil.nga.giat.geowave.core.index.ByteArrayId; import mil.nga.giat.geowave.core.store.CloseableIterator; import mil.nga.giat.geowave.core.store.DataStore; import mil.nga.giat.geowave.core.store.EntryVisibilityHandler; import mil.nga.giat.geowave.core.store.IndexWriter; import mil.nga.giat.geowave.core.store.adapter.AbstractDataAdapter; import mil.nga.giat.geowave.core.store.adapter.NativeFieldHandler; import mil.nga.giat.geowave.core.store.adapter.NativeFieldHandler.RowBuilder; import mil.nga.giat.geowave.core.store.adapter.PersistentIndexFieldHandler; import mil.nga.giat.geowave.core.store.adapter.WritableDataAdapter; import mil.nga.giat.geowave.core.store.adapter.statistics.DataStatistics; import mil.nga.giat.geowave.core.store.adapter.statistics.StatisticsProvider; import mil.nga.giat.geowave.core.store.base.DataStoreEntryInfo; import mil.nga.giat.geowave.core.store.data.PersistentValue; import mil.nga.giat.geowave.core.store.data.field.FieldReader; import mil.nga.giat.geowave.core.store.data.field.FieldUtils; import mil.nga.giat.geowave.core.store.data.field.FieldWriter; import mil.nga.giat.geowave.core.store.dimension.NumericDimensionField; import mil.nga.giat.geowave.core.store.index.CommonIndexModel; import mil.nga.giat.geowave.core.store.index.CommonIndexValue; import mil.nga.giat.geowave.core.store.index.PrimaryIndex; import mil.nga.giat.geowave.core.store.query.Query; import mil.nga.giat.geowave.core.store.query.QueryOptions; import mil.nga.giat.geowave.datastore.accumulo.AccumuloDataStore; import mil.nga.giat.geowave.datastore.accumulo.BasicAccumuloOperations; import org.apache.accumulo.core.client.AccumuloException; import org.apache.accumulo.core.client.AccumuloSecurityException; import org.apache.accumulo.core.client.Connector; import org.apache.accumulo.core.client.mock.MockInstance; import org.apache.accumulo.core.client.security.tokens.PasswordToken; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKBReader; import com.vividsolutions.jts.io.WKBWriter; public class AccumuloRangeQueryTest { private DataStore mockDataStore; private PrimaryIndex index; private WritableDataAdapter<TestGeometry> adapter; private final GeometryFactory factory = new GeometryFactory(); private final TestGeometry testdata = new TestGeometry( factory.createPolygon(new Coordinate[] { new Coordinate( 1.025, 1.032), new Coordinate( 1.026, 1.032), new Coordinate( 1.026, 1.033), new Coordinate( 1.025, 1.032) }), "test_shape_1"); @Before public void ingestGeometries() throws AccumuloException, AccumuloSecurityException, IOException { final MockInstance mockInstance = new MockInstance(); final Connector mockConnector = mockInstance.getConnector( "root", new PasswordToken( new byte[0])); mockDataStore = new AccumuloDataStore( new BasicAccumuloOperations( mockConnector)); index = new SpatialDimensionalityTypeProvider().createPrimaryIndex(); adapter = new TestGeometryAdapter(); try (IndexWriter writer = mockDataStore.createWriter( adapter, index)) { writer.write(testdata); } } @Test public void testIntersection() { final Geometry testGeo = factory.createPolygon(new Coordinate[] { new Coordinate( 1.0249, 1.0319), new Coordinate( 1.0261, 1.0319), new Coordinate( 1.0261, 1.0323), new Coordinate( 1.0249, 1.0319) }); final Query intersectQuery = new SpatialQuery( testGeo); Assert.assertTrue(testdata.geom.intersects(testGeo)); final CloseableIterator<TestGeometry> resultOfIntersect = mockDataStore.query( new QueryOptions( adapter, index), intersectQuery); Assert.assertTrue(resultOfIntersect.hasNext()); } @Test public void largeQuery() { final Geometry largeGeo = createPolygon(50000); final Query largeQuery = new SpatialQuery( largeGeo); final CloseableIterator itr = mockDataStore.query( new QueryOptions( adapter, index), largeQuery); int numfeats = 0; while (itr.hasNext()) { itr.next(); numfeats++; } Assert.assertEquals( numfeats, 1); } /** * Verifies equality for interning is still working as expected * (topologically), as the the largeQuery() test has a dependency on this; * * @throws ParseException */ @Test public void testInterning() throws ParseException { final Geometry g = GeometryUtils.GEOMETRY_FACTORY.createPolygon(new Coordinate[] { new Coordinate( 0, 0), new Coordinate( 1, 0), new Coordinate( 1, 1), new Coordinate( 0, 1), new Coordinate( 0, 0) }); final Geometry gNewInstance = GeometryUtils.GEOMETRY_FACTORY.createPolygon(new Coordinate[] { new Coordinate( 0, 0), new Coordinate( 1, 0), new Coordinate( 1, 1), new Coordinate( 0, 1), new Coordinate( 0, 0) }); final WKBWriter wkbWriter = new WKBWriter(); final byte[] b = wkbWriter.write(g); final byte[] b2 = new byte[b.length]; System.arraycopy( b, 0, b2, 0, b.length); final WKBReader wkbReader = new WKBReader(); final Geometry gSerialized = wkbReader.read(b); final Geometry gSerializedArrayCopy = wkbReader.read(b2); Assert.assertEquals( g, gNewInstance); Assert.assertEquals( g, gSerializedArrayCopy); Assert.assertEquals( gSerialized, gSerializedArrayCopy); Assert.assertEquals( gSerialized, gSerializedArrayCopy); } @Test public void testMiss() { final Query intersectQuery = new SpatialQuery( factory.createPolygon(new Coordinate[] { new Coordinate( 1.0247, 1.0319), new Coordinate( 1.0249, 1.0319), new Coordinate( 1.0249, 1.0323), new Coordinate( 1.0247, 1.0319) })); final CloseableIterator<TestGeometry> resultOfIntersect = mockDataStore.query( new QueryOptions( adapter, index), intersectQuery); Assert.assertFalse(resultOfIntersect.hasNext()); } @Test public void testEncompass() { final Query encompassQuery = new SpatialQuery( factory.createPolygon(new Coordinate[] { new Coordinate( 1.0249, 1.0319), new Coordinate( 1.0261, 1.0319), new Coordinate( 1.0261, 1.0331), new Coordinate( 1.0249, 1.0319) })); final CloseableIterator<TestGeometry> resultOfIntersect = mockDataStore.query( new QueryOptions( adapter, index), encompassQuery); Assert.assertTrue(resultOfIntersect.hasNext()); final TestGeometry geom1 = resultOfIntersect.next(); Assert.assertEquals( "test_shape_1", geom1.id); } private static Polygon createPolygon( final int numPoints ) { final double centerX = 4; final double centerY = 12; final int maxRadius = 80; final List<Coordinate> coords = new ArrayList<Coordinate>(); final Random rand = new Random( 8675309l); final double increment = (double) 360 / numPoints; for (double theta = 0; theta <= 360; theta += increment) { final double radius = (rand.nextDouble() * maxRadius) + 0.1; final double rad = (theta * Math.PI) / 180.0; final double x = centerX + (radius * Math.sin(rad)); final double y = centerY + (radius * Math.cos(rad)); coords.add(new Coordinate( x, y)); } coords.add(coords.get(0)); return GeometryUtils.GEOMETRY_FACTORY.createPolygon(coords.toArray(new Coordinate[coords.size()])); } protected static class TestGeometry { protected final Geometry geom; protected final String id; public TestGeometry( final Geometry geom, final String id ) { this.geom = geom; this.id = id; } } protected WritableDataAdapter<TestGeometry> createGeometryAdapter() { return new TestGeometryAdapter(); } protected static class TestGeometryAdapter extends AbstractDataAdapter<TestGeometry> implements StatisticsProvider<TestGeometry> { private static final ByteArrayId GEOM = new ByteArrayId( "myGeo"); private static final ByteArrayId ID = new ByteArrayId( "myId"); private static final PersistentIndexFieldHandler<TestGeometry, ? extends CommonIndexValue, Object> GEOM_FIELD_HANDLER = new PersistentIndexFieldHandler<TestGeometry, CommonIndexValue, Object>() { @Override public ByteArrayId[] getNativeFieldIds() { return new ByteArrayId[] { GEOM }; } @Override public CommonIndexValue toIndexValue( final TestGeometry row ) { return new GeometryWrapper( row.geom, new byte[0]); } @Override public PersistentValue<Object>[] toNativeValues( final CommonIndexValue indexValue ) { return new PersistentValue[] { new PersistentValue<Object>( GEOM, ((GeometryWrapper) indexValue).getGeometry()) }; } @Override public byte[] toBinary() { return new byte[0]; } @Override public void fromBinary( final byte[] bytes ) { } }; private static final NativeFieldHandler<TestGeometry, Object> ID_FIELD_HANDLER = new NativeFieldHandler<AccumuloRangeQueryTest.TestGeometry, Object>() { @Override public ByteArrayId getFieldId() { return ID; } @Override public Object getFieldValue( final TestGeometry row ) { return row.id; } }; private static final List<NativeFieldHandler<TestGeometry, Object>> NATIVE_FIELD_HANDLER_LIST = new ArrayList<NativeFieldHandler<TestGeometry, Object>>(); private static final List<PersistentIndexFieldHandler<TestGeometry, ? extends CommonIndexValue, Object>> COMMON_FIELD_HANDLER_LIST = new ArrayList<PersistentIndexFieldHandler<TestGeometry, ? extends CommonIndexValue, Object>>(); static { COMMON_FIELD_HANDLER_LIST.add(GEOM_FIELD_HANDLER); NATIVE_FIELD_HANDLER_LIST.add(ID_FIELD_HANDLER); } public TestGeometryAdapter() { super( COMMON_FIELD_HANDLER_LIST, NATIVE_FIELD_HANDLER_LIST); } @Override public ByteArrayId getAdapterId() { return new ByteArrayId( "test"); } @Override public boolean isSupported( final TestGeometry entry ) { return true; } @Override public ByteArrayId getDataId( final TestGeometry entry ) { return new ByteArrayId( entry.id); } @Override public FieldReader getReader( final ByteArrayId fieldId ) { if (fieldId.equals(GEOM)) { return FieldUtils.getDefaultReaderForClass(Geometry.class); } else if (fieldId.equals(ID)) { return FieldUtils.getDefaultReaderForClass(String.class); } return null; } @Override public FieldWriter getWriter( final ByteArrayId fieldId ) { if (fieldId.equals(GEOM)) { return FieldUtils.getDefaultWriterForClass(Geometry.class); } else if (fieldId.equals(ID)) { return FieldUtils.getDefaultWriterForClass(String.class); } return null; } @Override protected RowBuilder newBuilder() { return new RowBuilder<TestGeometry, Object>() { private String id; private Geometry geom; @Override public void setField( final PersistentValue<Object> fieldValue ) { if (fieldValue.getId().equals( GEOM)) { geom = (Geometry) fieldValue.getValue(); } else if (fieldValue.getId().equals( ID)) { id = (String) fieldValue.getValue(); } } @Override public TestGeometry buildRow( final ByteArrayId dataId ) { return new TestGeometry( geom, id); } }; } @Override public int getPositionOfOrderedField( final CommonIndexModel model, final ByteArrayId fieldId ) { int i = 0; for (final NumericDimensionField<? extends CommonIndexValue> dimensionField : model.getDimensions()) { if (fieldId.equals(dimensionField.getFieldId())) { return i; } i++; } if (fieldId.equals(GEOM)) { return i; } else if (fieldId.equals(ID)) { return i + 1; } return -1; } @Override public ByteArrayId getFieldIdForPosition( final CommonIndexModel model, final int position ) { if (position < model.getDimensions().length) { int i = 0; for (final NumericDimensionField<? extends CommonIndexValue> dimensionField : model.getDimensions()) { if (i == position) { return dimensionField.getFieldId(); } i++; } } else { final int numDimensions = model.getDimensions().length; if (position == numDimensions) { return GEOM; } else if (position == (numDimensions + 1)) { return ID; } } return null; } @Override public ByteArrayId[] getSupportedStatisticsTypes() { return new ByteArrayId[0]; } @Override public DataStatistics<TestGeometry> createDataStatistics( ByteArrayId statisticsId ) { return null; } @Override public EntryVisibilityHandler<TestGeometry> getVisibilityHandler( ByteArrayId statisticsId ) { return new EntryVisibilityHandler<TestGeometry>() { @Override public byte[] getVisibility( DataStoreEntryInfo entryInfo, TestGeometry entry ) { return null; } }; } } }