package mil.nga.giat.geowave.core.geotime.ingest; import com.beust.jcommander.Parameter; 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.store.dimension.GeometryWrapper; import mil.nga.giat.geowave.core.geotime.store.dimension.LatitudeField; import mil.nga.giat.geowave.core.geotime.store.dimension.LongitudeField; 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.dimension.NumericDimensionDefinition; import mil.nga.giat.geowave.core.index.sfc.SFCFactory.SFCType; import mil.nga.giat.geowave.core.index.sfc.xz.XZHierarchicalIndexFactory; import mil.nga.giat.geowave.core.store.dimension.NumericDimensionField; import mil.nga.giat.geowave.core.store.index.BasicIndexModel; import mil.nga.giat.geowave.core.store.index.CommonIndexValue; import mil.nga.giat.geowave.core.store.index.CustomIdIndex; import mil.nga.giat.geowave.core.store.index.PrimaryIndex; import mil.nga.giat.geowave.core.store.operations.remote.options.IndexPluginOptions.BaseIndexBuilder; import mil.nga.giat.geowave.core.store.spi.DimensionalityTypeOptions; import mil.nga.giat.geowave.core.store.spi.DimensionalityTypeProviderSpi; public class SpatialDimensionalityTypeProvider implements DimensionalityTypeProviderSpi { private final SpatialOptions options = new SpatialOptions(); private static final String DEFAULT_SPATIAL_ID = "SPATIAL_IDX"; private static final int LONGITUDE_BITS = 31; private static final int LATITUDE_BITS = 31; protected static final NumericDimensionDefinition[] SPATIAL_DIMENSIONS = new NumericDimensionDefinition[] { new LongitudeDefinition(), new LatitudeDefinition( true) // just use the same range for latitude to make square sfc values in // decimal degrees (EPSG:4326) }; protected static final NumericDimensionField[] SPATIAL_FIELDS = new NumericDimensionField[] { new LongitudeField(), new LatitudeField( true) // just use the same range for latitude to make square sfc values in // decimal degrees (EPSG:4326) }; protected static final NumericDimensionField[] SPATIAL_TEMPORAL_FIELDS = new NumericDimensionField[] { new LongitudeField(), new LatitudeField( true), new TimeField( Unit.YEAR) }; public SpatialDimensionalityTypeProvider() {} @Override public String getDimensionalityTypeName() { return "spatial"; } @Override public String getDimensionalityTypeDescription() { return "This dimensionality type matches all indices that only require Geometry."; } @Override public int getPriority() { // arbitrary - just higher than spatial temporal so that the default // will be spatial over spatial-temporal return 10; } @Override public DimensionalityTypeOptions getOptions() { return options; } @Override public PrimaryIndex createPrimaryIndex() { return internalCreatePrimaryIndex(options); } private static PrimaryIndex internalCreatePrimaryIndex( final SpatialOptions options ) { return new CustomIdIndex( XZHierarchicalIndexFactory.createFullIncrementalTieredStrategy( SPATIAL_DIMENSIONS, new int[] { LONGITUDE_BITS, LATITUDE_BITS }, SFCType.HILBERT), new BasicIndexModel( options.storeTime ? SPATIAL_TEMPORAL_FIELDS : SPATIAL_FIELDS), new ByteArrayId( options.storeTime ? DEFAULT_SPATIAL_ID + "_TIME" : DEFAULT_SPATIAL_ID)); } private static class SpatialOptions implements DimensionalityTypeOptions { @Parameter(names = { "--storeTime" }, required = false, description = "The index will store temporal values. This allows it to slightly more efficiently run spatial-temporal queries although if spatial-temporal queries are a common use case, a separate spatial-temporal index is recommended.") protected boolean storeTime = false; } @Override public Class<? extends CommonIndexValue>[] getRequiredIndexTypes() { return new Class[] { GeometryWrapper.class }; } public static class SpatialIndexBuilder extends BaseIndexBuilder<SpatialIndexBuilder> { private final SpatialOptions options; public SpatialIndexBuilder() { super(); options = new SpatialOptions(); } public SpatialIndexBuilder setIncludeTimeInCommonIndexModel( final boolean storeTime ) { options.storeTime = storeTime; return this; } @Override public PrimaryIndex createIndex() { return createIndex(internalCreatePrimaryIndex(options)); } } public static boolean isSpatial( final PrimaryIndex index ) { if ((index == null) || (index.getIndexStrategy() == null) || (index.getIndexStrategy().getOrderedDimensionDefinitions() == null)) { return false; } final NumericDimensionDefinition[] dimensions = index.getIndexStrategy().getOrderedDimensionDefinitions(); if (dimensions.length != 2) { return false; } boolean hasLat = false, hasLon = false; for (final NumericDimensionDefinition definition : dimensions) { if (definition instanceof LatitudeDefinition) { hasLat = true; } else if (definition instanceof LongitudeDefinition) { hasLon = true; } } return hasLat && hasLon; } }