* - In "geophysics to packed" transform, we can still use "Rescale"
* if the NaN value maps to 0.
*/
if (toGeo) {
canRescale = false;
final NumberRange target = sourceCategory.geophysics(true).getRange();
offset = target.getMinimum();
if (Double.doubleToRawLongBits(offset) != Double.doubleToRawLongBits(target.getMaximum())) {
canPiecewise = false;
break testLinear;
}
scale = 0;
} else {
canPiecewise = false;
assert !packedCategory.equals(sourceCategory) : packedCategory;
final NumberRange range = packedCategory.getRange();
if (range.getMinimum(true) == 0 && range.getMaximum(true) == 0) {
assert isNaN(sourceCategory.getRange().getMinimum()) : sourceCategory;
conditional = true;
continue;
}
canRescale = false;
break testLinear;
}
} else {
if (!toGeo) {
// We are going to convert geophysics values to packed ones.
transform = transform.inverse();
}
offset = transform.transform(0);
scale = transform.derivative(Double.NaN);
if (isNaN(scale) || isNaN(offset)) {
// One category doesn't use a linear transformation. We can't deal with
// that with "Rescale" or "Piecewise". Fallback on our "SampleTranscode".
canRescale = false;
canPiecewise = false;
break testLinear;
}
}
// Allocates arrays the first time the loop is run up to this point.
// Store scale and offset, and check if they still the same.
if (j == 0) {
if (i == 0) {
scales = new double[numBands];
offsets = new double[numBands];
breakpoints = new float [numBands][][];
}
sourceBreakpoints = new float[numCategories * 2];
targetBreakpoints = new float[numCategories * 2];
breakpoints[i] = new float[][] {sourceBreakpoints, targetBreakpoints};
offsets [i] = offset;
scales [i] = scale;
}
if (offset!=offsets[i] || scale!=scales[i]) {
canRescale = false;
}
// Computes breakpoints.
final NumberRange range = sourceCategory.getRange();
final double minimum = range.getMinimum(true);
final double maximum = range.getMaximum(true);
final float sourceMin = (float) minimum;
final float sourceMax = (float) maximum;
final float targetMin = (float)(minimum * scale + offset);
final float targetMax = (float)(maximum * scale + offset);
assert sourceMin <= sourceMax : range;
if (Math.abs(minimum - expectedSource) <= EPS) {
if (Math.abs(targetMin - expectedTarget) <= EPS || isNaN(expectedTarget)) {
/*
* This breakpoint is identical to the previous one. Do not duplicate;
* overwrites the previous breakpoint since the later is likely to be
* more accurate. Note that we accept NaN in expected (not calculated)
* target values but not in source values, because "Piecewise" performs
* its search on source values, wich must be monotonically increasing.
*/
jbp--;
} else {
// Found a discontinuity!!! The "piecewise" operation is not really
// designed for such case. The behavior between the last breakpoint
// and the current one may not be what the user expected.
assert sourceBreakpoints[jbp-1] < sourceMin : expectedSource;
canPiecewise = false;
}
} else if (j != 0) {
// Found a gap between the last category and the current one. But the
// piecewise operation still work as expected for values not in the gap.
assert !(expectedSource > sourceMin) : expectedSource;
}
sourceBreakpoints[jbp ] = sourceMin;
sourceBreakpoints[jbp+1] = sourceMax;
targetBreakpoints[jbp ] = targetMin;
targetBreakpoints[jbp+1] = targetMax;
jbp += 2;
expectedSource = range.getMaximum(false);
expectedTarget = expectedSource * scale + offset;
}
breakpoints[i][0] = sourceBreakpoints = XArray.resize(sourceBreakpoints, jbp);
breakpoints[i][1] = targetBreakpoints = XArray.resize(targetBreakpoints, jbp);
assert XArray.isSorted(sourceBreakpoints);