package mil.nga.giat.geowave.core.geotime.store.data; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.TimeZone; 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.SpatialTemporalDimensionalityTypeProvider; import mil.nga.giat.geowave.core.geotime.store.dimension.GeometryWrapper; import mil.nga.giat.geowave.core.geotime.store.dimension.Time; import mil.nga.giat.geowave.core.geotime.store.dimension.TimeField; import mil.nga.giat.geowave.core.index.ByteArrayId; 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.tiered.TieredSFCIndexFactory; import mil.nga.giat.geowave.core.store.adapter.AbstractDataAdapter; import mil.nga.giat.geowave.core.store.adapter.DimensionMatchingIndexFieldHandler; 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.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 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.PrecisionModel; public class PersistenceEncodingTest { private final GeometryFactory factory = new GeometryFactory( new PrecisionModel( PrecisionModel.FLOATING)); private static final NumericDimensionDefinition[] SPATIAL_TEMPORAL_DIMENSIONS = new NumericDimensionDefinition[] { new LongitudeDefinition(), new LatitudeDefinition(), new TimeDefinition( Unit.YEAR), }; private static final CommonIndexModel model = new SpatialTemporalDimensionalityTypeProvider() .createPrimaryIndex() .getIndexModel(); private static final NumericIndexStrategy strategy = TieredSFCIndexFactory.createSingleTierStrategy( SPATIAL_TEMPORAL_DIMENSIONS, new int[] { 16, 16, 16 }, SFCType.HILBERT); private static final PrimaryIndex index = new PrimaryIndex( strategy, model); Date start = null, end = null; @Before public void setUp() throws ParseException { TimeZone.setDefault(TimeZone.getTimeZone("GMT")); final SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.S"); start = dateFormat.parse("2012-04-03 13:30:23.304"); end = dateFormat.parse("2012-04-03 14:30:23.304"); } @Test public void testPoint() { final GeoObjDataAdapter adapter = new GeoObjDataAdapter( NATIVE_FIELD_HANDLER_LIST, COMMON_FIELD_HANDLER_LIST); final GeoObj entry = new GeoObj( factory.createPoint(new Coordinate( 43.454, 28.232)), start, end, "g1"); final List<ByteArrayId> ids = adapter.encode( entry, model).getInsertionIds( index); assertEquals( 1, ids.size()); } @Test public void testLine() { final GeoObjDataAdapter adapter = new GeoObjDataAdapter( NATIVE_FIELD_HANDLER_LIST, COMMON_FIELD_HANDLER_LIST); final GeoObj entry = new GeoObj( factory.createLineString(new Coordinate[] { new Coordinate( 43.444, 28.232), new Coordinate( 43.454, 28.242) }), start, end, "g1"); final List<ByteArrayId> ids = adapter.encode( entry, model).getInsertionIds( index); assertEquals( 7, ids.size()); } @Test public void testLineWithPrecisionOnTheTileEdge() { final NumericIndexStrategy strategy = TieredSFCIndexFactory.createSingleTierStrategy( SPATIAL_TEMPORAL_DIMENSIONS, new int[] { 14, 14, 14 }, SFCType.HILBERT); final PrimaryIndex index = new PrimaryIndex( strategy, model); final GeoObjDataAdapter adapter = new GeoObjDataAdapter( NATIVE_FIELD_HANDLER_LIST, COMMON_FIELD_HANDLER_LIST); final GeoObj entry = new GeoObj( factory.createLineString(new Coordinate[] { new Coordinate( -99.22, 33.75000000000001), // notice that // this gets // tiled as // 33.75 new Coordinate( -99.15, 33.75000000000001) // notice that this gets tiled as 33.75 }), new Date( 352771200000l), new Date( 352771200000l), "g1"); final List<ByteArrayId> ids = adapter.encode( entry, model).getInsertionIds( index); assertEquals( 4, ids.size()); } @Test public void testPoly() { final GeoObjDataAdapter adapter = new GeoObjDataAdapter( NATIVE_FIELD_HANDLER_LIST, COMMON_FIELD_HANDLER_LIST); final GeoObj entry = new GeoObj( factory.createLineString(new Coordinate[] { new Coordinate( 43.444, 28.232), new Coordinate( 43.454, 28.242), new Coordinate( 43.444, 28.252), new Coordinate( 43.444, 28.232), }), start, end, "g1"); final List<ByteArrayId> ids = adapter.encode( entry, model).getInsertionIds( index); assertEquals( 18, ids.size()); } @Test public void testPointRange() { final GeoObjDataAdapter adapter = new GeoObjDataAdapter( NATIVE_FIELD_RANGE_HANDLER_LIST, COMMON_FIELD_RANGE_HANDLER_LIST); final GeoObj entry = new GeoObj( factory.createPoint(new Coordinate( 43.454, 28.232)), start, end, "g1"); final List<ByteArrayId> ids = adapter.encode( entry, model).getInsertionIds( index); assertEquals( 8, ids.size()); } @Test public void testLineRnge() { final GeoObjDataAdapter adapter = new GeoObjDataAdapter( NATIVE_FIELD_RANGE_HANDLER_LIST, COMMON_FIELD_RANGE_HANDLER_LIST); final GeoObj entry = new GeoObj( factory.createLineString(new Coordinate[] { new Coordinate( 43.444, 28.232), new Coordinate( 43.454, 28.242) }), start, end, "g1"); final List<ByteArrayId> ids = adapter.encode( entry, model).getInsertionIds( index); assertTrue(ids.size() < 100); } private static final ByteArrayId GEOM = new ByteArrayId( "myGeo"); private static final ByteArrayId ID = new ByteArrayId( "myId"); private static final ByteArrayId START_TIME = new ByteArrayId( "startTime"); private static final ByteArrayId END_TIME = new ByteArrayId( "endTime"); private static final List<NativeFieldHandler<GeoObj, Object>> NATIVE_FIELD_HANDLER_LIST = new ArrayList<NativeFieldHandler<GeoObj, Object>>(); private static final List<NativeFieldHandler<GeoObj, Object>> NATIVE_FIELD_RANGE_HANDLER_LIST = new ArrayList<NativeFieldHandler<GeoObj, Object>>(); private static final List<PersistentIndexFieldHandler<GeoObj, ? extends CommonIndexValue, Object>> COMMON_FIELD_HANDLER_LIST = new ArrayList<PersistentIndexFieldHandler<GeoObj, ? extends CommonIndexValue, Object>>(); private static final List<PersistentIndexFieldHandler<GeoObj, ? extends CommonIndexValue, Object>> COMMON_FIELD_RANGE_HANDLER_LIST = new ArrayList<PersistentIndexFieldHandler<GeoObj, ? extends CommonIndexValue, Object>>(); private static final NativeFieldHandler<GeoObj, Object> END_TIME_FIELD_HANDLER = new NativeFieldHandler<GeoObj, Object>() { @Override public ByteArrayId getFieldId() { return END_TIME; } @Override public Object getFieldValue( final GeoObj row ) { return row.endTime; } }; private static final NativeFieldHandler<GeoObj, Object> ID_FIELD_HANDLER = new NativeFieldHandler<GeoObj, Object>() { @Override public ByteArrayId getFieldId() { return ID; } @Override public Object getFieldValue( final GeoObj row ) { return row.id; } }; private static final PersistentIndexFieldHandler<GeoObj, ? extends CommonIndexValue, Object> GEOM_FIELD_HANDLER = new PersistentIndexFieldHandler<GeoObj, CommonIndexValue, Object>() { @Override public ByteArrayId[] getNativeFieldIds() { return new ByteArrayId[] { GEOM }; } @Override public CommonIndexValue toIndexValue( final GeoObj row ) { return new GeometryWrapper( row.geometry, 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 ) { } }; static { COMMON_FIELD_HANDLER_LIST.add(GEOM_FIELD_HANDLER); COMMON_FIELD_HANDLER_LIST.add(new TimeFieldHandler()); COMMON_FIELD_RANGE_HANDLER_LIST.add(GEOM_FIELD_HANDLER); COMMON_FIELD_RANGE_HANDLER_LIST.add(new TimeRangeFieldHandler()); NATIVE_FIELD_HANDLER_LIST.add(ID_FIELD_HANDLER); NATIVE_FIELD_HANDLER_LIST.add(END_TIME_FIELD_HANDLER); NATIVE_FIELD_RANGE_HANDLER_LIST.add(ID_FIELD_HANDLER); } private static class GeoObjDataAdapter extends AbstractDataAdapter<GeoObj> { public GeoObjDataAdapter( final List<NativeFieldHandler<GeoObj, Object>> nativeFields, final List<PersistentIndexFieldHandler<GeoObj, ? extends CommonIndexValue, Object>> commonFields ) { super( commonFields, nativeFields); } @Override public ByteArrayId getAdapterId() { return new ByteArrayId( "geoobj".getBytes()); } @Override public boolean isSupported( final GeoObj entry ) { return true; } @Override public ByteArrayId getDataId( final GeoObj entry ) { return new ByteArrayId( entry.id.getBytes()); } @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); } else if (fieldId.equals(START_TIME)) { return FieldUtils.getDefaultReaderForClass(Date.class); } else if (fieldId.equals(END_TIME)) { return FieldUtils.getDefaultReaderForClass(Date.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); } else if (fieldId.equals(START_TIME)) { return FieldUtils.getDefaultWriterForClass(Date.class); } else if (fieldId.equals(END_TIME)) { return FieldUtils.getDefaultWriterForClass(Date.class); } return null; } @Override protected RowBuilder newBuilder() { return new RowBuilder<GeoObj, Object>() { private String id; private Geometry geom; private Date stime; private Date etime; @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(); } else if (fieldValue.getId().equals( START_TIME)) { stime = (Date) fieldValue.getValue(); } else { etime = (Date) fieldValue.getValue(); } } @Override public GeoObj buildRow( final ByteArrayId dataId ) { return new GeoObj( geom, stime, etime, 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; } else if (fieldId.equals(START_TIME)) { return i + 2; } else if (fieldId.equals(END_TIME)) { return i + 3; } 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; } else if (position == (numDimensions + 2)) { return START_TIME; } else if (position == (numDimensions + 3)) { return END_TIME; } } return null; } } private static class GeoObj { private final Geometry geometry; private final String id; private final Date startTime; private final Date endTime; public GeoObj( final Geometry geometry, final Date startTime, final Date endTime, final String id ) { super(); this.geometry = geometry; this.startTime = startTime; this.endTime = endTime; this.id = id; } } private static class TimeFieldHandler implements PersistentIndexFieldHandler<GeoObj, CommonIndexValue, Object>, DimensionMatchingIndexFieldHandler<GeoObj, CommonIndexValue, Object> { public TimeFieldHandler() {} @Override public ByteArrayId[] getNativeFieldIds() { return new ByteArrayId[] { START_TIME }; } @Override public CommonIndexValue toIndexValue( final GeoObj row ) { return new Time.Timestamp( row.startTime.getTime(), new byte[0]); } @Override public PersistentValue<Object>[] toNativeValues( final CommonIndexValue indexValue ) { return new PersistentValue[] { new PersistentValue<Object>( START_TIME, new Date( (long) ((Time.TimeRange) indexValue).toNumericData().getMin())), new PersistentValue<Object>( END_TIME, new Date( (long) ((Time.TimeRange) indexValue).toNumericData().getMin())) }; } @Override public ByteArrayId[] getSupportedIndexFieldIds() { return new ByteArrayId[] { new TimeField( Unit.YEAR).getFieldId() }; } @Override public byte[] toBinary() { return new byte[0]; } @Override public void fromBinary( final byte[] bytes ) { } } private static class TimeRangeFieldHandler implements PersistentIndexFieldHandler<GeoObj, CommonIndexValue, Object>, DimensionMatchingIndexFieldHandler<GeoObj, CommonIndexValue, Object> { public TimeRangeFieldHandler() {} @Override public ByteArrayId[] getNativeFieldIds() { return new ByteArrayId[] { START_TIME, END_TIME }; } @Override public CommonIndexValue toIndexValue( final GeoObj row ) { return new Time.TimeRange( row.startTime.getTime(), row.endTime.getTime(), new byte[0]); } @Override public PersistentValue<Object>[] toNativeValues( final CommonIndexValue indexValue ) { return new PersistentValue[] { new PersistentValue<Object>( START_TIME, new Date( (long) ((Time.TimeRange) indexValue).toNumericData().getMin())), new PersistentValue<Object>( END_TIME, new Date( (long) ((Time.TimeRange) indexValue).toNumericData().getMin())) }; } @Override public ByteArrayId[] getSupportedIndexFieldIds() { return new ByteArrayId[] { new TimeField( Unit.YEAR).getFieldId() }; } @Override public byte[] toBinary() { return new byte[0]; } @Override public void fromBinary( final byte[] bytes ) { } } }