diff --git a/pom.xml b/pom.xml
index 03b8475bc6d..4841ff7ffd1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,6 +38,7 @@
This is necessary to make NetBeans modules updatable and independent from the Maven version (= snap.version).
-->
${project.version}
+
2.0.05
2.7.1
5.3.1
diff --git a/snap-core/src/main/java/org/esa/snap/core/dataop/resamp/NonNegative.java b/snap-core/src/main/java/org/esa/snap/core/dataop/resamp/NonNegative.java
new file mode 100644
index 00000000000..b6c42ae19d7
--- /dev/null
+++ b/snap-core/src/main/java/org/esa/snap/core/dataop/resamp/NonNegative.java
@@ -0,0 +1,51 @@
+package org.esa.snap.core.dataop.resamp;
+
+/**
+ * Resampling decorator. Ensures that the decorated resampling yields non-negative values
+ * by clipping any negative value to zero.
+ *
+ * @author Ralf Quast
+ */
+public class NonNegative implements Resampling {
+
+ private final Resampling resampling;
+
+ /**
+ * Creates a new "non-negative" resampling from a given resampling type.
+ *
+ * @param resampling the resampling type.
+ */
+ public NonNegative(Resampling resampling) {
+ this.resampling = resampling;
+ }
+
+ @Override
+ public String getName() {
+ return String.format("NON_NEGATIVE_%s", resampling.getName());
+ }
+
+ @Override
+ public Index createIndex() {
+ return resampling.createIndex();
+ }
+
+ @Override
+ public void computeIndex(double x, double y, int width, int height, Index index) {
+ resampling.computeIndex(x, y, width, height, index);
+ }
+
+ @Override
+ public void computeCornerBasedIndex(double x, double y, int width, int height, Index index) {
+ resampling.computeCornerBasedIndex(x, y, width, height, index);
+ }
+
+ @Override
+ public double resample(Raster raster, Index index) throws Exception {
+ return Math.max(0.0, resampling.resample(raster, index));
+ }
+
+ @Override
+ public int getKernelSize() {
+ return resampling.getKernelSize();
+ }
+}
diff --git a/snap-core/src/main/java/org/esa/snap/core/dataop/resamp/Resampling.java b/snap-core/src/main/java/org/esa/snap/core/dataop/resamp/Resampling.java
index 11206066c93..7cb56f429b7 100644
--- a/snap-core/src/main/java/org/esa/snap/core/dataop/resamp/Resampling.java
+++ b/snap-core/src/main/java/org/esa/snap/core/dataop/resamp/Resampling.java
@@ -43,11 +43,17 @@ public interface Resampling {
Resampling BISINC_5_POINT_INTERPOLATION = new BiSinc5PointInterpolationResampling();
Resampling BISINC_11_POINT_INTERPOLATION = new BiSinc11PointInterpolationResampling();
Resampling BISINC_21_POINT_INTERPOLATION = new BiSinc21PointInterpolationResampling();
+
/**
* The bicubic spline interpolation resampling method.
*/
Resampling BICUBIC_INTERPOLATION = new BiCubicInterpolationResampling();
+ /**
+ * The non-negative bicubic spline interpolation resampling method.
+ */
+ Resampling NON_NEGATIVE_BICUBIC_INTERPOLATION = new NonNegative(BICUBIC_INTERPOLATION);
+
/**
* Gets a unique identifier for this resampling method, e.g. "BILINEAR_INTERPOLATION".
*
diff --git a/snap-core/src/main/java/org/esa/snap/core/dataop/resamp/ResamplingFactory.java b/snap-core/src/main/java/org/esa/snap/core/dataop/resamp/ResamplingFactory.java
index 9baee624842..3745f16d1a2 100644
--- a/snap-core/src/main/java/org/esa/snap/core/dataop/resamp/ResamplingFactory.java
+++ b/snap-core/src/main/java/org/esa/snap/core/dataop/resamp/ResamplingFactory.java
@@ -30,6 +30,7 @@ public final class ResamplingFactory {
public static final String BISINC_11_POINT_INTERPOLATION_NAME = "BISINC_11_POINT_INTERPOLATION";
public static final String BISINC_21_POINT_INTERPOLATION_NAME = "BISINC_21_POINT_INTERPOLATION";
public static final String BICUBIC_INTERPOLATION_NAME = "BICUBIC_INTERPOLATION";
+ public static final String NON_NEGATIVE_BICUBIC_INTERPOLATION_NAME = "NON_NEGATIVE_BICUBIC_INTERPOLATION";
public static final String[] resamplingNames = new String[]{
NEAREST_NEIGHBOUR_NAME,
@@ -70,6 +71,8 @@ public static Resampling createResampling(final String resamplingName) {
return Resampling.BISINC_21_POINT_INTERPOLATION;
case BICUBIC_INTERPOLATION_NAME:
return Resampling.BICUBIC_INTERPOLATION;
+ case NON_NEGATIVE_BICUBIC_INTERPOLATION_NAME:
+ return Resampling.NON_NEGATIVE_BICUBIC_INTERPOLATION;
default:
return null;
}
diff --git a/snap-core/src/test/java/org/esa/snap/core/dataop/resamp/NonNegativeBicubicInterpolationResamplingTest.java b/snap-core/src/test/java/org/esa/snap/core/dataop/resamp/NonNegativeBicubicInterpolationResamplingTest.java
new file mode 100644
index 00000000000..80f20f79045
--- /dev/null
+++ b/snap-core/src/test/java/org/esa/snap/core/dataop/resamp/NonNegativeBicubicInterpolationResamplingTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 Brockmann Consult GmbH (info@brockmann-consult.de)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see http://www.gnu.org/licenses/
+ */
+
+package org.esa.snap.core.dataop.resamp;
+
+import junit.framework.TestCase;
+
+public class NonNegativeBicubicInterpolationResamplingTest extends TestCase {
+
+ final Resampling resampling = Resampling.NON_NEGATIVE_BICUBIC_INTERPOLATION;
+ final TestRaster raster = new TestRaster();
+
+ public void testCreateIndex() {
+ final Resampling.Index index = resampling.createIndex();
+ assertNotNull(index);
+ assertNotNull(index.i);
+ assertNotNull(index);
+ assertNotNull(index.i);
+ assertNotNull(index.j);
+ assertNotNull(index.ki);
+ assertNotNull(index.kj);
+ assertEquals(2, index.i.length);
+ assertEquals(2, index.j.length);
+ assertEquals(1, index.ki.length);
+ assertEquals(1, index.kj.length);
+ }
+
+ public void testIndexAndSample() throws Exception {
+ final Resampling.Index index = resampling.createIndex();
+
+ testIndexAndSample(
+ index,
+ 2.2f, 2.3f,
+ 1.0, 2.0,
+ 1.0, 2.0,
+ 0.7f,
+ 0.8f,
+ 25.0616f);
+ }
+
+ private void testIndexAndSample(
+ final Resampling.Index index,
+ float x, float y,
+ double i1Exp, double i2Exp,
+ double j1Exp, double j2Exp,
+ float ki1Exp,
+ float kj1Exp,
+ float sampleExp) throws Exception {
+
+ resampling.computeIndex(x, y, raster.getWidth(), raster.getHeight(), index);
+
+ assertEquals(i1Exp, index.i[0]);
+ assertEquals(i2Exp, index.i[1]);
+
+ assertEquals(j1Exp, index.j[0]);
+ assertEquals(j2Exp, index.j[1]);
+
+ assertEquals(ki1Exp, index.ki[0], 1e-5f);
+ assertEquals(kj1Exp, index.kj[0], 1e-5f);
+
+ double sample = resampling.resample(raster, index);
+ assertEquals(sampleExp, sample, 1e-5f);
+ }
+
+ public void testCornerBasedIndex() throws Exception {
+ testCornerIndex(2.2f, 2.3f);
+ }
+
+ private void testCornerIndex(final float x, final float y) {
+
+ final Resampling.Index index = resampling.createIndex();
+ resampling.computeCornerBasedIndex(x, y, raster.getWidth(), raster.getHeight(), index);
+
+ final Resampling.Index indexExp = resampling.createIndex();
+ computeExpectedIndex(x, y, raster.getWidth(), raster.getHeight(), indexExp);
+
+ assertEquals(indexExp.i[0], index.i[0]);
+ assertEquals(indexExp.i[1], index.i[1]);
+ assertEquals(indexExp.j[0], index.j[0]);
+ assertEquals(indexExp.j[1], index.j[1]);
+ assertEquals(indexExp.ki[0], index.ki[0]);
+ assertEquals(indexExp.kj[0], index.kj[0]);
+ }
+
+ private void computeExpectedIndex(
+ final double x, final double y, final int width, final int height, final Resampling.Index index) {
+ index.x = x;
+ index.y = y;
+ index.width = width;
+ index.height = height;
+
+
+ final int i0 = (int) Math.floor(x);
+ final int j0 = (int) Math.floor(y);
+
+ index.i0 = i0;
+ index.j0 = j0;
+
+ index.i[0] = Resampling.Index.crop(i0, width - 1);
+ index.i[1] = Math.min(i0 + 1, width - 1);
+ index.ki[0] = x - i0;
+ index.j[0] = Resampling.Index.crop(j0, height - 1);
+ index.j[1] = Math.min(j0 + 1, height - 1);
+ index.kj[0] = y - j0;
+ }
+}
diff --git a/snap-gpf/pom.xml b/snap-gpf/pom.xml
index 89d21ea63f4..e8c7961e3e5 100644
--- a/snap-gpf/pom.xml
+++ b/snap-gpf/pom.xml
@@ -26,6 +26,7 @@
snap-gpf
nbm
+
SNAP Graph Processing Framework (GPF)
The basic framework for processing using Operators and the GPT.
diff --git a/snap-gpf/src/main/java/org/esa/snap/core/gpf/internal/TileImpl.java b/snap-gpf/src/main/java/org/esa/snap/core/gpf/internal/TileImpl.java
index 8f7414381fc..8d35f8ba177 100644
--- a/snap-gpf/src/main/java/org/esa/snap/core/gpf/internal/TileImpl.java
+++ b/snap-gpf/src/main/java/org/esa/snap/core/gpf/internal/TileImpl.java
@@ -121,6 +121,17 @@ public double toGeoPhysical(double sample) {
return rasterDataNode.scale(sample);
}
+ private int toRaw(int sample) {
+ final double rawSample = rasterDataNode.scaleInverse(sample);
+ if (rawSample < -2147483648.0) {
+ return Integer.MIN_VALUE;
+ }
+ if (rawSample > 2147483647.0) {
+ return Integer.MAX_VALUE;
+ }
+ return (int) Math.round(rawSample);
+ }
+
@Override
public float toRaw(float sample) {
return (float) rasterDataNode.scaleInverse(sample);
@@ -492,7 +503,6 @@ public int getSampleInt(int x, int y) {
int sample = raster.getSample(x, y, 0);
// handle unsigned data types, see also [BEAM-1147] (nf - 20100527)
if (signedByte) {
- //noinspection SillyAssignment
sample = (byte) sample;
}
if (scaled) {
@@ -504,17 +514,39 @@ public int getSampleInt(int x, int y) {
@Override
public void setSample(int x, int y, int sample) {
if (scaled) {
- sample = (int) Math.floor(toRaw((double) sample) + 0.5);
+ sample = toRaw(sample);
+ }
+ switch (rasterDataNode.getDataType()) {
+ case ProductData.TYPE_INT8:
+ sample = clipOrRound(sample, -128, 127);
+ break;
+ case ProductData.TYPE_INT16:
+ sample = clipOrRound(sample, -32768, 32767);
+ break;
+ case ProductData.TYPE_UINT8:
+ sample = clipOrRound(sample, 0, 255);
+ break;
+ case ProductData.TYPE_UINT16:
+ sample = clipOrRound(sample, 0, 65535);
+ break;
}
writableRaster.setSample(x, y, 0, sample);
}
+ private int clipOrRound(int sample, int lowerBound, int upperBound) {
+ if (sample < lowerBound) {
+ sample = lowerBound;
+ } else if (sample > upperBound) {
+ sample = upperBound;
+ }
+ return sample;
+ }
+
@Override
public float getSampleFloat(int x, int y) {
float sample = raster.getSampleFloat(x, y, 0);
// handle unsigned data types, see also [BEAM-1147] (nf - 20100527)
if (signedByte) {
- //noinspection SillyAssignment
sample = (byte) sample;
}
if (scaled) {
@@ -528,16 +560,38 @@ public void setSample(int x, int y, float sample) {
if (scaled) {
sample = toRaw(sample);
}
+ switch (rasterDataNode.getDataType()) {
+ case ProductData.TYPE_INT8:
+ sample = clipOrRound(sample, -128.0f, 127.0f);
+ break;
+ case ProductData.TYPE_INT16:
+ sample = clipOrRound(sample, -32768.0f, 32767.0f);
+ break;
+ case ProductData.TYPE_UINT8:
+ sample = clipOrRound(sample, 0.0f, 255.0f);
+ break;
+ case ProductData.TYPE_UINT16:
+ sample = clipOrRound(sample, 0.0f, 65535.0f);
+ break;
+ }
writableRaster.setSample(x, y, 0, sample);
}
+ private float clipOrRound(float sample, float lowerBound, float upperBound) {
+ if (sample < lowerBound) {
+ return lowerBound;
+ }
+ if (sample > upperBound) {
+ return upperBound;
+ }
+ return (float) Math.rint(sample);
+ }
@Override
public double getSampleDouble(int x, int y) {
double sample = raster.getSampleDouble(x, y, 0);
// handle unsigned data types, see also [BEAM-1147] (nf - 20100527)
if (signedByte) {
- //noinspection SillyAssignment
sample = (byte) sample;
}
if (scaled) {
@@ -551,9 +605,33 @@ public void setSample(int x, int y, double sample) {
if (scaled) {
sample = toRaw(sample);
}
+ switch (rasterDataNode.getDataType()) {
+ case ProductData.TYPE_INT8:
+ sample = clipOrRound(sample, -128.0, 127.0);
+ break;
+ case ProductData.TYPE_INT16:
+ sample = clipOrRound(sample, -32768.0, 32767.0);
+ break;
+ case ProductData.TYPE_UINT8:
+ sample = clipOrRound(sample, 0.0, 255.0);
+ break;
+ case ProductData.TYPE_UINT16:
+ sample = clipOrRound(sample, 0.0, 65535.0);
+ break;
+ }
writableRaster.setSample(x, y, 0, sample);
}
+ private double clipOrRound(double sample, double lowerBound, double upperBound) {
+ if (sample < lowerBound) {
+ return lowerBound;
+ }
+ if (sample > upperBound) {
+ return upperBound;
+ }
+ return Math.rint(sample);
+ }
+
@Override
public boolean getSampleBit(int x, int y, int bitIndex) {
long sample = raster.getSample(x, y, 0);
diff --git a/snap-gpf/src/test/java/org/esa/snap/core/gpf/internal/TileImplTest.java b/snap-gpf/src/test/java/org/esa/snap/core/gpf/internal/TileImplTest.java
index 5f4be3fbc8b..66ef0142fee 100644
--- a/snap-gpf/src/test/java/org/esa/snap/core/gpf/internal/TileImplTest.java
+++ b/snap-gpf/src/test/java/org/esa/snap/core/gpf/internal/TileImplTest.java
@@ -277,8 +277,174 @@ public void testSetSamples() {
samples = tile.getSamplesDouble();
assertNotNull(samples);
assertEquals(N, samples.length);
- assertEquals(10.0, samples[0], 1.0e-10);
- assertEquals(10.0, samples[N - 1], 1.0e-10);
+ assertEquals(12.5, samples[0], 0.0);
+ assertEquals(12.5, samples[N - 1], 0.0);
+ }
+
+ @Test
+ public void testSetSamples_Double_INT8() {
+ final double factor = 0.015;
+ final Tile tile = createScaledTile(ProductData.TYPE_INT8, factor);
+
+ tile.setSample(0, 0, 1.0);
+ assertEquals(1.0, tile.getSampleDouble(0,0), 0.015 / 2.0);
+
+ tile.setSample(0, 0, -1.0);
+ assertEquals(-1.0, tile.getSampleDouble(0,0), 0.015 / 2.0);
+
+ final double upperBound = 127.0;
+ final double tooBig = upperBound * (factor + 0.001);
+ tile.setSample(0, 0, tooBig);
+ assertEquals(upperBound * factor, tile.getSampleDouble(0,0), 0.0);
+
+ final double negativeTooBig = -tooBig;
+ final double lowerBound = -upperBound - 1.0;
+ tile.setSample(0, 0, negativeTooBig);
+ assertEquals(lowerBound * factor, tile.getSampleDouble(0,0), 0.0);
+ }
+
+ @Test
+ public void testSetSamples_Double_INT16() {
+ final double factor = 0.015;
+ final Tile tile = createScaledTile(ProductData.TYPE_INT16, factor);
+
+ tile.setSample(0, 0, 1.0);
+ assertEquals(1.0, tile.getSampleDouble(0,0), 0.015 / 2.0);
+
+ tile.setSample(0, 0, -1.0);
+ assertEquals(-1.0, tile.getSampleDouble(0,0), 0.015 / 2.0);
+
+ final double upperBound = 32767.0;
+ final double tooBig = upperBound * (factor + 0.001);
+ tile.setSample(0, 0, tooBig);
+ assertEquals(upperBound * factor, tile.getSampleDouble(0,0), 0.0);
+
+ final double lowerBound = -upperBound - 1.0;
+ tile.setSample(0, 0, -tooBig);
+ assertEquals(lowerBound * factor, tile.getSampleDouble(0,0), 0.0);
+ }
+
+ @Test
+ public void testSetSamples_Double_UINT8() {
+ final double factor = 0.015;
+ final Tile tile = createScaledTile(ProductData.TYPE_UINT8, factor);
+
+ tile.setSample(0, 0, 1.0);
+ assertEquals(1.0, tile.getSampleDouble(0,0), 0.015 / 2.0);
+
+ tile.setSample(0, 0, 0.0);
+ assertEquals(0.0, tile.getSampleDouble(0,0), 0.0);
+
+ final double upperBound = 255.0;
+ final double tooBig = upperBound * (factor + 0.001);
+ tile.setSample(0, 0, tooBig);
+ assertEquals(upperBound * factor, tile.getSampleDouble(0,0), 0.0);
+
+ tile.setSample(0, 0, -1.0);
+ assertEquals(0.0, tile.getSampleDouble(0,0), 0.0);
+ }
+
+ @Test
+ public void testSetSamples_Double_UINT16() {
+ final double factor = 0.015;
+ final Tile tile = createScaledTile(ProductData.TYPE_UINT16, factor);
+
+ tile.setSample(0, 0, 1.0);
+ assertEquals(1.0, tile.getSampleDouble(0,0), 0.015 / 2.0);
+
+ tile.setSample(0, 0, 0.0);
+ assertEquals(0.0, tile.getSampleDouble(0,0), 0.0);
+
+ final double upperBound = 65535.0;
+ final double tooBig = upperBound * (factor + 0.001);
+ tile.setSample(0, 0, tooBig);
+ assertEquals(upperBound * factor, tile.getSampleDouble(0,0), 0.0);
+
+ tile.setSample(0, 0, -1.0);
+ assertEquals(0.0, tile.getSampleDouble(0,0), 0.0);
+ }
+
+ @Test
+ public void testSetSamples_Float_INT8() {
+ final double factor = 0.015;
+ final Tile tile = createScaledTile(ProductData.TYPE_INT8, factor);
+
+ tile.setSample(0, 0, 1.0f);
+ assertEquals(1.0, tile.getSampleFloat(0,0), 0.015 / 2.0);
+
+ tile.setSample(0, 0, -1.0f);
+ assertEquals(-1.0, tile.getSampleFloat(0,0), 0.015 / 2.0);
+
+ final double upperBound = 127.0;
+ final double tooBig = upperBound * (factor + 0.001);
+ tile.setSample(0, 0, (float) tooBig);
+ assertEquals((float) (upperBound * factor), tile.getSampleFloat(0,0), 0.0);
+
+ final double negativeTooBig = -tooBig;
+ final double lowerBound = -upperBound - 1.0;
+ tile.setSample(0, 0, (float) negativeTooBig);
+ assertEquals((float) (lowerBound * factor), tile.getSampleFloat(0,0), 0.0);
+ }
+
+ @Test
+ public void testSetSamples_Float_INT16() {
+ final double factor = 0.015;
+ final Tile tile = createScaledTile(ProductData.TYPE_INT16, factor);
+
+ tile.setSample(0, 0, 1.0f);
+ assertEquals(1.0, tile.getSampleFloat(0,0), 0.015 / 2.0);
+
+ tile.setSample(0, 0, -1.0f);
+ assertEquals(-1.0, tile.getSampleFloat(0,0), 0.015 / 2.0);
+
+ final double upperBound = 32767.0;
+ final double tooBig = upperBound * (factor + 0.001);
+ tile.setSample(0, 0, (float) tooBig);
+ assertEquals((float) (upperBound * factor), tile.getSampleFloat(0,0), 0.0);
+
+ final double lowerBound = -upperBound - 1.0;
+ tile.setSample(0, 0, (float) -tooBig);
+ assertEquals((float) (lowerBound * factor), tile.getSampleFloat(0,0), 0.0);
+ }
+
+ @Test
+ public void testSetSamples_Float_UINT8() {
+ final double factor = 0.015;
+ final Tile tile = createScaledTile(ProductData.TYPE_UINT8, factor);
+
+ tile.setSample(0, 0, 1.0f);
+ assertEquals(1.0, tile.getSampleFloat(0,0), 0.015 / 2.0);
+
+ tile.setSample(0, 0, 0.0f);
+ assertEquals(0.0, tile.getSampleFloat(0,0), 0.0);
+
+ final double upperBound = 255.0;
+ final double tooBig = upperBound * (factor + 0.001);
+ tile.setSample(0, 0, (float) tooBig);
+ assertEquals((float) (upperBound * factor), tile.getSampleFloat(0,0), 0.0);
+
+ tile.setSample(0, 0, -1.0f);
+ assertEquals(0.0, tile.getSampleFloat(0,0), 0.0);
+ }
+
+ @Test
+ public void testSetSamples_Float_UINT16() {
+ final double factor = 0.015;
+ final Tile tile = createScaledTile(ProductData.TYPE_UINT16, factor);
+
+ tile.setSample(0, 0, 1.0f);
+ assertEquals(1.0, tile.getSampleFloat(0,0), 0.015 / 2.0);
+
+ tile.setSample(0, 0, 0.0f);
+ assertEquals(0.0, tile.getSampleFloat(0,0), 0.0);
+
+ final double upperBound = 65535.0;
+ final double tooBig = upperBound * (factor + 0.001);
+ tile.setSample(0, 0, (float) tooBig);
+ assertEquals((float) (upperBound * factor), tile.getSampleFloat(0,0), 0.0);
+
+ tile.setSample(0, 0, -1.0f);
+ assertEquals(0.0, tile.getSampleFloat(0,0), 0.0);
}
static Tile createRawTile(int type) {