/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2015, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotools.renderer.lite.gridcoverage2d; import it.geosolutions.imageio.plugins.arcgrid.AsciiGridsImageReader; import it.geosolutions.imageio.plugins.arcgrid.spi.AsciiGridsImageReaderSpi; import it.geosolutions.imageio.utilities.ImageIOUtilities; import it.geosolutions.jaiext.classifier.LinearColorMap; import it.geosolutions.jaiext.classifier.LinearColorMapElement; import it.geosolutions.jaiext.piecewise.TransformationException; import it.geosolutions.jaiext.range.RangeFactory; import java.awt.Color; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import javax.imageio.stream.FileImageInputStream; import javax.media.jai.RasterFactory; import javax.media.jai.RenderedOp; import org.geotools.TestData; import org.geotools.image.ImageWorker; import org.geotools.resources.image.ComponentColorModelJAI; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.opengis.referencing.operation.TransformException; /** * * @author Simone Giannecchini, GeoSolutions * * * * * @source $URL$ */ public class TestLinearClassifier extends Assert { @Before public void before() throws Exception { // check that it exisits File file = TestData.copy(this, "arcgrid/arcgrid.zip"); assertTrue(file.exists()); // unzip it TestData.unzipFile(this, "arcgrid/arcgrid.zip"); } private static final int TEST_NUM = 1; /** * Synthetic with Double Sample Model! * * @throws IOException */ @Test public void Synthetic_Double() throws IOException { // ///////////////////////////////////////////////////////////////////// // // This test is interesting since it can be used to force the // creation of a sample model that uses a USHORT datatype since the // number of requested colors is pretty high. We are also using some // synthetic data where there is no NoData. // // ///////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////// // // Set the pixel values. Because we use only one tile with one band, // the // code below is pretty similar to the code we would have if we were // just setting the values in a matrix. // // ///////////////////////////////////////////////////////////////////// final BufferedImage image = getSynthetic_Double(); for (int i = 0; i < TEST_NUM; i++) { // ///////////////////////////////////////////////////////////////////// // // Build the categories // // ///////////////////////////////////////////////////////////////////// final LinearColorMapElement c0 = LinearColorMapElement .create("c0", Color.BLACK, RangeFactory.create( Double.NEGATIVE_INFINITY, false, 10, true), 0); final LinearColorMapElement c1 = LinearColorMapElement .create("c2", Color.blue, RangeFactory.create(10.0, false, 100.0, true), 1); final LinearColorMapElement c3 = LinearColorMapElement .create("c3", Color.green, RangeFactory.create(100.0, false, 300.0, true), 2); final LinearColorMapElement c4 = LinearColorMapElement .create("c4", new Color[] { Color.green, Color.red }, RangeFactory.create(300.0, false, 400, true), RangeFactory.create(3, 1000)); final LinearColorMapElement c5 = LinearColorMapElement .create("c5", new Color[] { Color.red, Color.white }, RangeFactory.create(400.0, false, 1000, true), RangeFactory.create(1001, 2000)); final LinearColorMapElement c6 = LinearColorMapElement .create("c6", Color.red, 1001.0, 2001); final LinearColorMapElement c7 = LinearColorMapElement .create("nodata", new Color(0, 0, 0, 0), RangeFactory.create( Double.NaN, Double.NaN), 2201); final LinearColorMap list = new LinearColorMap("", new LinearColorMapElement[] { c0, c1, c3, c4, c5, c6 }, new LinearColorMapElement[] { c7 }); ImageWorker w = new ImageWorker(image); //final ParameterBlockJAI pbj = new ParameterBlockJAI( //RasterClassifierOpImage.OPERATION_NAME); //pbj.addSource(image); //pbj.setParameter("Domain1D", list); final RenderedOp finalimage = w.classify(list, null).getRenderedOperation(); //JAI.create( //RasterClassifierOpImage.OPERATION_NAME, pbj); if (TestData.isInteractiveTest()) ImageIOUtilities.visualize(finalimage, "synthetic"); else finalimage.getTiles(); finalimage.dispose(); } } /** * Synthetic with Float Sample Model! * * @return {@linkplain BufferedImage} */ private BufferedImage getSynthetic_Double() { final int width = 500; final int height = 500; final WritableRaster raster = RasterFactory.createBandedRaster( DataBuffer.TYPE_DOUBLE, width, height, 1, null); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.setSample(x, y, 0, (x + y)); } } final ColorModel cm = new ComponentColorModelJAI(ColorSpace .getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_DOUBLE); final BufferedImage image = new BufferedImage(cm, raster, false, null); return image; } /** * Building a synthetic image upon a DOUBLE sample-model. * * @return {@linkplain BufferedImage} * @throws IOException */ @Test public void Synthetic_Float() throws IOException { // ///////////////////////////////////////////////////////////////////// // // This test is interesting since it can be used to force the // creation of a sample model that uses a USHORT datatype since the // number of requested colors is pretty high. We are also using some // synthetic data where there is no NoData. // // ///////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////// // // Set the pixel values. Because we use only one tile with one band, // the // code below is pretty similar to the code we would have if we were // just setting the values in a matrix. // // ///////////////////////////////////////////////////////////////////// final BufferedImage image = getSynthetic_Float(); for (int i = 0; i < TEST_NUM; i++) { // ///////////////////////////////////////////////////////////////////// // // Build the categories // // ///////////////////////////////////////////////////////////////////// final LinearColorMapElement c0 = LinearColorMapElement .create("c0", Color.BLACK, RangeFactory.create( Double.NEGATIVE_INFINITY, false, 10, true), 0); final LinearColorMapElement c1 = LinearColorMapElement .create("c2", Color.blue, RangeFactory.create(10.0f, false, 100.0f, true), 1); final LinearColorMapElement c3 = LinearColorMapElement .create("c3", Color.green, RangeFactory.create(100.0f, false, 300.0f, true), 2); final LinearColorMapElement c4 = LinearColorMapElement .create("c4", new Color[] { Color.green, Color.red }, RangeFactory.create(300.0f, false, 400.0f, true), RangeFactory.create(3, 1000)); final LinearColorMapElement c5 = LinearColorMapElement .create("c5", new Color[] { Color.red, Color.white }, RangeFactory.create(400.0f, false, 1000.0f, true), RangeFactory.create(1001, 2000)); final LinearColorMapElement c6 = LinearColorMapElement .create("c6", Color.red, 1001.0f, 2001); final LinearColorMapElement c7 = LinearColorMapElement .create("nodata", new Color(0, 0, 0, 0), RangeFactory.create( Double.NaN, Double.NaN), 2201); final LinearColorMap list = new LinearColorMap("", new LinearColorMapElement[] { c0, c1, c3, c4, c5, c6 }, new LinearColorMapElement[] { c7 }); ImageWorker w = new ImageWorker(image); final RenderedOp finalimage = w.classify(list, null).getRenderedOperation(); if (TestData.isInteractiveTest()) ImageIOUtilities.visualize(finalimage, "synthetic"); else finalimage.getTiles(); finalimage.dispose(); } } /** * Building a synthetic image upon a FLOAT sample-model. * * @return {@linkplain BufferedImage} */ private BufferedImage getSynthetic_Float() { final int width = 500; final int height = 500; final WritableRaster raster = RasterFactory.createBandedRaster( DataBuffer.TYPE_FLOAT, width, height, 1, null); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { raster.setSample(x, y, 0, (x + y)); } } final ColorModel cm = new ComponentColorModelJAI(ColorSpace .getInstance(ColorSpace.CS_GRAY), false, false, Transparency.OPAQUE, DataBuffer.TYPE_FLOAT); final BufferedImage image = new BufferedImage(cm, raster, false, null); return image; } /** * Spearfish test-case. * * @throws IOException */ @Test public void spearfish() throws IOException { // ///////////////////////////////////////////////////////////////////// // // This test is quite standard since the NoData category specified // is for NoData values since the input file is a GRASS ascii file // where the missing values are represented by * which are substituted // with NaN during reads. The only strange thing that we try here is // that we map two different classes to the same color with the same // index. // // ///////////////////////////////////////////////////////////////////// final RenderedImage image = getSpearfhisDemo(); for (int i = 0; i < TEST_NUM; i++) { final LinearColorMapElement c0 = LinearColorMapElement .create("c0", Color.yellow, RangeFactory.create( Double.NEGATIVE_INFINITY, false, 1100, true), 5); final LinearColorMapElement c1 = LinearColorMapElement .create("c2", Color.blue, RangeFactory.create(1100.0, false, 1200.0, true), 1); final LinearColorMapElement c3 = LinearColorMapElement .create("c3", Color.green, RangeFactory.create(1200.0, false, 1400.0, true), 7); final LinearColorMapElement c4 = LinearColorMapElement .create("c4", Color.blue, RangeFactory.create(1400.0, false, 1600, true), 1); final LinearColorMapElement c5 = LinearColorMapElement .create("c4", Color.CYAN, RangeFactory.create(1600.0, false, Double.POSITIVE_INFINITY, true), 11); final LinearColorMapElement c6 = LinearColorMapElement .create("nodata", new Color(0, 0, 0, 0), RangeFactory.create( Double.NaN, Double.NaN), 0); final LinearColorMap list = new LinearColorMap("", new LinearColorMapElement[] { c0, c1, c3, c4, c5 }, new LinearColorMapElement[] { c6 }); ImageWorker w = new ImageWorker(image); final RenderedOp finalimage = w.classify(list, null).getRenderedOperation(); if (TestData.isInteractiveTest()) ImageIOUtilities.visualize(finalimage, "spearfish"); else finalimage.getTiles(); finalimage.dispose(); } } /** * Building an image based on Spearfish data. * * @return {@linkplain BufferedImage} * * @throws IOException * @throws FileNotFoundException */ private RenderedImage getSpearfhisDemo() throws IOException, FileNotFoundException { final AsciiGridsImageReader reader = (AsciiGridsImageReader) new AsciiGridsImageReaderSpi() .createReaderInstance(); reader.setInput(new FileImageInputStream(TestData.file(this, "arcgrid/spearfish_dem.arx"))); final RenderedImage image = reader.readAsRenderedImage(0, null); return image; } /** * SWAN test-case. * * @throws IOException */ @Test public void SWAN() throws IOException { // ///////////////////////////////////////////////////////////////////// // // This test is interesting since it can be used to simulate the // case where someone specifies a ColorMap that overlaps with the native // NoData value. For this SWAN data the NoData value is -9999.0 and no // NaN which falls right into the first category. // // We overcome this problem by simply giving higher priority to the // NoData category over the other categories when doing a search for // the right category given a certain value. This force us to // first evaluate the no data category and then evaluate a possible // provided overlapping value. // // This test is also interesting since we create a color map by // providing output indexes that are not ordered and also that are not // all contained in a closed natural interval. As you can notice by // inspecting the different classes below there is an index, 51, which // is way outside the range of the others. // // ///////////////////////////////////////////////////////////////////// final RenderedImage image = getSWAN(); for (int i = 0; i < TEST_NUM; i++) { final LinearColorMapElement c0 = LinearColorMapElement .create("c0", Color.green, RangeFactory.create( Double.NEGATIVE_INFINITY, 0.3), 51); final LinearColorMapElement c1 = LinearColorMapElement .create("c2", Color.yellow, RangeFactory.create(0.3, false, 0.6, true), 1); final LinearColorMapElement c1b = LinearColorMapElement .create("c2", Color.BLACK, RangeFactory.create(0.3, false, 0.6, true), 1); final LinearColorMapElement c1c = LinearColorMapElement .create("c2", Color.yellow, RangeFactory.create(0.3, false, 0.6, true), 1); assertFalse(c1.equals(c1b)); assertTrue(c1.equals(c1c)); final LinearColorMapElement c3 = LinearColorMapElement .create("c3", Color.red, RangeFactory.create(0.60, false, 0.90, true), 2); final LinearColorMapElement c4 = LinearColorMapElement .create("c4", Color.BLUE, RangeFactory.create(0.9, false, Double.POSITIVE_INFINITY, true), 3); final LinearColorMapElement nodata = LinearColorMapElement .create("nodata", new Color(0, 0, 0, 0), RangeFactory.create( -9.0, -9.0), 4); final LinearColorMap list = new LinearColorMap("testSWAN", new LinearColorMapElement[] { c0, c1, c3, c4 }, new LinearColorMapElement[] { nodata }, new Color(0,0,0)); assertEquals(list.getSourceDimensions(), 1); assertEquals(list.getTargetDimensions(), 1); assertEquals(list.getName().toString(), "testSWAN"); assertNotNull(c0.toString()); ImageWorker w = new ImageWorker(image); boolean exceptionThrown = false; try { // // // forcing a bad band selection ... // // final RenderedOp d = w.classify(list, new Integer(2)).getRenderedOperation(); d.getTiles(); // we should not be here! } catch (Exception e) { // // // ... ok, Exception wanted! // // exceptionThrown = true; } assertTrue(exceptionThrown); //pbj.setParameter("bandIndex", new Integer(0)); final RenderedOp finalImage = w.classify(list, new Integer(0)).getRenderedOperation(); //JAI.create( //RasterClassifierOpImage.OPERATION_NAME, pbj); if (TestData.isInteractiveTest()) ImageIOUtilities.visualize(finalImage, "testSWAN1"); else finalImage.getTiles(); finalImage.dispose(); } } /** * SWAN test-case. * * @throws IOException */ @Test public void SWANGAP() throws IOException { // ///////////////////////////////////////////////////////////////////// // // This test is interesting since it can be used to simulate the // case where someone specifies a ColorMap that overlaps with the native // NoData value. For this SWAN data the NoData value is -9999.0 and no // NaN which falls right into the first category. // // We overcome this problem by simply giving higher priority to the // NoData category over the other categories when doing a search for // the right category given a certain value. This force us to // first evaluate the no data category and then evaluate a possible // provided overlapping value. // // This test is also interesting since we create a color map by // providing output indexes that are not ordered and also that are not // all contained in a closed natural interval. As you can notice by // inspecting the different classes below there is an index, 51, which // is way outside the range of the others. // // ///////////////////////////////////////////////////////////////////// final RenderedImage image = getSWAN(); for (int i = 0; i < TEST_NUM; i++) { final LinearColorMapElement c0 = LinearColorMapElement .create("c0", Color.green, RangeFactory.create( Double.NEGATIVE_INFINITY, 0.3), 51); final LinearColorMapElement c1 = LinearColorMapElement .create("c2", Color.yellow, RangeFactory.create(0.3, false, 0.6, true), 1); final LinearColorMapElement c3 = LinearColorMapElement .create("c3", Color.red, RangeFactory.create(0.70, false, 0.90, true), 2); final LinearColorMapElement c4 = LinearColorMapElement .create("c4", Color.BLUE, RangeFactory.create(0.9, false, Double.POSITIVE_INFINITY, true), 3); final LinearColorMapElement nodata = LinearColorMapElement .create("nodata", Color.red, RangeFactory.create( -9.0, -9.0), 4); final LinearColorMap list = new LinearColorMap("testSWAN", new LinearColorMapElement[] { c0, c1, c3, c4 }, new LinearColorMapElement[] { nodata }, new Color(0,0,0,0)); ImageWorker w = new ImageWorker(image); boolean exceptionThrown = false; try { // // // forcing a bad band selection ... // // final RenderedOp d = w.classify(list, new Integer(2)).getRenderedOperation(); d.getTiles(); // we should not be here! } catch (Exception e) { // // // ... ok, Exception wanted! // // exceptionThrown = true; } assertTrue(exceptionThrown); final RenderedOp finalImage = w.classify(list, new Integer(0)).getRenderedOperation(); final IndexColorModel icm=(IndexColorModel) finalImage.getColorModel(); assertEquals(icm.getRed(4),255); assertEquals(icm.getRed(2),255); if (TestData.isInteractiveTest()) ImageIOUtilities.visualize(finalImage, "testSWANGAP"); else finalImage.getTiles(); finalImage.dispose(); } } /** * Building an image based on SWAN data. * * @return {@linkplain BufferedImage} * * @throws IOException * @throws FileNotFoundException */ private RenderedImage getSWAN() throws IOException, FileNotFoundException { final AsciiGridsImageReader reader = (AsciiGridsImageReader) new AsciiGridsImageReaderSpi() .createReaderInstance(); reader.setInput(new FileImageInputStream(TestData.file(this, "arcgrid/SWAN_NURC_LigurianSeaL07_HSIGN.asc"))); final RenderedImage image = reader.readAsRenderedImage(0, null); return image; } /** * NoData only test-case. * * @throws IOException * @throws TransformException * @throws TransformationException */ @Test public void noDataOnly() throws IOException, TransformException, TransformationException { // ///////////////////////////////////////////////////////////////////// // // We are covering here a case that can often be verified, i.e. the case // when only NoData values are known and thus explicitly mapped by the // user to a defined nodata DomainElement, but not the same for the // others. // In such case we want CatrgoryLists automatically map unknown data to // a Passthrough DomainElement, which identically maps raster data to // category // data. // // ///////////////////////////////////////////////////////////////////// for (int i = 0; i < TEST_NUM; i++) { final LinearColorMapElement n0 = LinearColorMapElement .create("nodata", new Color(0, 0, 0, 0), RangeFactory.create( Double.NaN, Double.NaN), 9999); final LinearColorMap list = new LinearColorMap("", new LinearColorMapElement[] { n0 }); double testNum = Math.random(); try{ assertEquals(list.transform(testNum), testNum, 0.0); assertTrue(false); }catch (Exception e) { // TODO: handle exception } assertEquals(list.transform(Double.NaN), 9999, 0.0); } } }