package mil.nga.giat.geowave.format.landsat8.index;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import mil.nga.giat.geowave.core.index.dimension.bin.BinRange;
import mil.nga.giat.geowave.core.index.dimension.bin.BinValue;
import mil.nga.giat.geowave.core.index.dimension.bin.BinningStrategy;
import mil.nga.giat.geowave.core.index.sfc.data.NumericData;
import mil.nga.giat.geowave.core.index.sfc.data.NumericRange;
/**
* This class is useful for establishing a consistent binning strategy with the
* bin size being 256 * 16 days.
*/
public class Landsat8TemporalBinningStrategy implements
BinningStrategy
{
protected static final long MILLIS_PER_DAY = 86400000L;
protected static final long BIN_SIZE_MILLIS = MILLIS_PER_DAY * 16 * 256;
protected static final long ORIGIN_MILLIS = 1420070400L;
public Landsat8TemporalBinningStrategy() {}
@Override
public double getBinMin() {
return 0;
}
@Override
public double getBinMax() {
return getBinSizeMillis() - 1;
}
/**
* Method used to bin a raw date in milliseconds to a binned value of the
* Binning Strategy.
*/
@Override
public BinValue getBinnedValue(
final double value ) {
final long millisFromOrigin = (long) value - ORIGIN_MILLIS;
if (millisFromOrigin < 0) {
final int binId = (int) (((millisFromOrigin - BIN_SIZE_MILLIS) + 1) / BIN_SIZE_MILLIS);
final long startOfEpochFromOrigin = binId * BIN_SIZE_MILLIS;
final ByteBuffer buf = ByteBuffer.allocate(4);
buf.putInt(binId);
return new BinValue(
buf.array(),
millisFromOrigin - startOfEpochFromOrigin);
}
else {
final int binId = (int) (millisFromOrigin / BIN_SIZE_MILLIS);
final ByteBuffer buf = ByteBuffer.allocate(4);
buf.putInt(binId);
return new BinValue(
buf.array(),
millisFromOrigin % BIN_SIZE_MILLIS);
}
}
private long getStartEpoch(
final byte[] binId ) {
final ByteBuffer buf = ByteBuffer.wrap(binId);
final int binsFromOrigin = buf.getInt();
final long millisFromOrigin = binsFromOrigin * BIN_SIZE_MILLIS;
return ORIGIN_MILLIS + millisFromOrigin;
}
private long getBinSizeMillis() {
return BIN_SIZE_MILLIS;
}
@Override
public int getFixedBinIdSize() {
return 4;
}
private byte[] getBinId(
final long value ) {
final long millisFromOrigin = value - ORIGIN_MILLIS;
if (millisFromOrigin < 0) {
final int binId = (int) (((millisFromOrigin - BIN_SIZE_MILLIS) + 1) / BIN_SIZE_MILLIS);
final ByteBuffer buf = ByteBuffer.allocate(4);
buf.putInt(binId);
return buf.array();
}
else {
final int binId = (int) (millisFromOrigin / BIN_SIZE_MILLIS);
final ByteBuffer buf = ByteBuffer.allocate(4);
buf.putInt(binId);
return buf.array();
}
}
@Override
public BinRange[] getNormalizedRanges(
final NumericData range ) {
// now make sure all bin definitions between the start and end bins
// are covered
final long millisFromOrigin = (long) range.getMin() - ORIGIN_MILLIS;
final int binId;
if (millisFromOrigin < 0) {
binId = (int) (millisFromOrigin / BIN_SIZE_MILLIS) - 1;
}
else {
binId = (int) (millisFromOrigin / BIN_SIZE_MILLIS);
}
final long startOfEpochFromOrigin = binId * BIN_SIZE_MILLIS;
long epochIterator = startOfEpochFromOrigin + ORIGIN_MILLIS;
final List<BinRange> bins = new ArrayList<BinRange>();
// track this, so that we can easily declare a range to be the full
// extent and use the information to perform a more efficient scan
boolean firstBin = (millisFromOrigin != startOfEpochFromOrigin);
boolean lastBin = false;
do {
final long nextEpoch = epochIterator + BIN_SIZE_MILLIS;
final long maxOfBin = nextEpoch - 1;
long startMillis, endMillis;
boolean fullExtent;
if ((long) range.getMax() <= maxOfBin) {
lastBin = true;
endMillis = (long) range.getMax();
// its questionable whether we use
fullExtent = ((long) range.getMax()) == maxOfBin;
}
else {
endMillis = maxOfBin;
fullExtent = !firstBin;
}
if (firstBin) {
startMillis = (long) range.getMin();
firstBin = false;
}
else {
startMillis = epochIterator;
}
// we have the millis for range, but to normalize for this bin we
// need to subtract the epoch of the bin
bins.add(new BinRange(
getBinId(epochIterator),
startMillis - epochIterator,
endMillis - epochIterator,
fullExtent));
epochIterator = nextEpoch;
// iterate until we reach our end epoch
}
while (!lastBin);
return bins.toArray(new BinRange[bins.size()]);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
final String className = getClass().getName();
result = (prime * result) + ((className == null) ? 0 : className.hashCode());
return result;
}
@Override
public boolean equals(
final Object obj ) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
return true;
}
@Override
public NumericRange getDenormalizedRanges(
final BinRange binnedRange ) {
final long startOfEpochMillis = getStartEpoch(binnedRange.getBinId());
final long minMillis = startOfEpochMillis + (long) binnedRange.getNormalizedMin();
final long maxMillis = startOfEpochMillis + (long) binnedRange.getNormalizedMax();
return new NumericRange(
minMillis,
maxMillis);
}
@Override
public byte[] toBinary() {
return new byte[] {};
}
@Override
public void fromBinary(
final byte[] bytes ) {}
}