package mil.nga.giat.geowave.core.store.query; import java.util.HashMap; import java.util.Map; import mil.nga.giat.geowave.core.index.ByteArrayId; import mil.nga.giat.geowave.core.index.Coordinate; import mil.nga.giat.geowave.core.index.CoordinateRange; import mil.nga.giat.geowave.core.index.MultiDimensionalCoordinateRanges; import mil.nga.giat.geowave.core.index.MultiDimensionalCoordinateRangesArray; import mil.nga.giat.geowave.core.index.MultiDimensionalCoordinates; public class CoordinateRangeUtils { public static interface RangeCache { public boolean inBounds( final MultiDimensionalCoordinates coordinates ); } private static interface RangeByBinIdCache { public boolean inBounds( final Coordinate coordinate ); } public static class RangeLookupFactory { public static RangeCache createMultiRangeLookup( final MultiDimensionalCoordinateRangesArray[] coordinateRanges ) { if ((coordinateRanges == null) || (coordinateRanges.length == 0)) { return new NullRangeLookup(); } else if (coordinateRanges.length == 1) { return createRangeLookup(coordinateRanges[0].getRangesArray()); } else { return new MultiRangeCacheLookup( coordinateRanges); } } public static RangeCache createRangeLookup( final MultiDimensionalCoordinateRanges[] coordinateRanges ) { if (coordinateRanges == null) { return new NullRangeLookup(); } else if ((coordinateRanges.length == 1) && (coordinateRanges[0].getMultiDimensionalId() == null)) { return new SingleRangeLookup( coordinateRanges[0]); } else { return new MultiRangeLookup( coordinateRanges); } } } private static class MultiRangeCacheLookup implements RangeCache { private final RangeCache[] rangeCaches; public MultiRangeCacheLookup( final MultiDimensionalCoordinateRangesArray[] coordinateRanges ) { rangeCaches = new RangeCache[coordinateRanges.length]; for (int i = 0; i < coordinateRanges.length; i++) { rangeCaches[i] = RangeLookupFactory.createRangeLookup(coordinateRanges[i].getRangesArray()); } } @Override public boolean inBounds( final MultiDimensionalCoordinates coordinates ) { // this should act as an OR clause for (final RangeCache r : rangeCaches) { if (r.inBounds(coordinates)) { return true; } } return false; } } private static class NullRangeLookup implements RangeCache { @Override public boolean inBounds( final MultiDimensionalCoordinates coordinates ) { return false; } } private static class SingleRangeLookup implements RangeCache { private final MultiDimensionalBinLookup singleton; public SingleRangeLookup( final MultiDimensionalCoordinateRanges coordinateRanges ) { singleton = new MultiDimensionalBinLookup( coordinateRanges); } @Override public boolean inBounds( final MultiDimensionalCoordinates coordinates ) { return inBounds( coordinates, singleton); } private static boolean inBounds( final MultiDimensionalCoordinates coordinates, final MultiDimensionalBinLookup binLookup ) { final CoordinateRange[] retVal = new CoordinateRange[coordinates.getNumDimensions()]; for (int d = 0; d < retVal.length; d++) { final Coordinate c = coordinates.getCoordinate(d); if (!binLookup.inBounds( d, c)) { return false; } } return true; } } private static class MultiRangeLookup implements RangeCache { private final Map<ByteArrayId, MultiDimensionalBinLookup> multiDimensionalIdToRangeMap; public MultiRangeLookup( final MultiDimensionalCoordinateRanges[] coordinateRanges ) { multiDimensionalIdToRangeMap = new HashMap<>(); for (final MultiDimensionalCoordinateRanges r : coordinateRanges) { multiDimensionalIdToRangeMap.put( new ByteArrayId( r.getMultiDimensionalId()), new MultiDimensionalBinLookup( r)); } } @Override public boolean inBounds( final MultiDimensionalCoordinates coordinates ) { final MultiDimensionalBinLookup binLookup = multiDimensionalIdToRangeMap.get(new ByteArrayId( coordinates.getMultiDimensionalId())); if (binLookup == null) { return false; } return SingleRangeLookup.inBounds( coordinates, binLookup); } } private static class BinLookupFactory { public static RangeByBinIdCache createBinLookup( final CoordinateRange[] coordinateRanges ) { if (coordinateRanges == null) { return new NullBinLookup(); } else if ((coordinateRanges.length == 1) && (coordinateRanges[0].getBinId() == null)) { return new SingleBinLookup( coordinateRanges[0]); } else { return new MultiBinLookup( coordinateRanges); } } } private static class MultiDimensionalBinLookup { private final RangeByBinIdCache[] rangePerDimensionCache; private MultiDimensionalBinLookup( final MultiDimensionalCoordinateRanges ranges ) { rangePerDimensionCache = new RangeByBinIdCache[ranges.getNumDimensions()]; for (int d = 0; d < rangePerDimensionCache.length; d++) { rangePerDimensionCache[d] = BinLookupFactory.createBinLookup(ranges.getRangeForDimension(d)); } } public boolean inBounds( final int dimension, final Coordinate coordinate ) { return rangePerDimensionCache[dimension].inBounds(coordinate); } } private static class NullBinLookup implements RangeByBinIdCache { @Override public boolean inBounds( final Coordinate coordinate ) { return false; } } private static class SingleBinLookup implements RangeByBinIdCache { private final CoordinateRange singleton; public SingleBinLookup( final CoordinateRange singleton ) { this.singleton = singleton; } @Override public boolean inBounds( final Coordinate coordinate ) { return inBounds( singleton, coordinate); } private static boolean inBounds( final CoordinateRange range, final Coordinate coordinate ) { final long coord = coordinate.getCoordinate(); return (range.getMinCoordinate() <= coord) && (range.getMaxCoordinate() >= coord); } } private static class MultiBinLookup implements RangeByBinIdCache { private final Map<ByteArrayId, CoordinateRange> binIdToRangeMap; public MultiBinLookup( final CoordinateRange[] coordinateRanges ) { binIdToRangeMap = new HashMap<>(); for (final CoordinateRange r : coordinateRanges) { binIdToRangeMap.put( new ByteArrayId( r.getBinId()), r); } } @Override public boolean inBounds( final Coordinate coordinate ) { final CoordinateRange range = binIdToRangeMap.get(new ByteArrayId( coordinate.getBinId())); if (range == null) { return false; } return SingleBinLookup.inBounds( range, coordinate); } } }