/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geotools.process.raster;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.geotools.process.raster.FilterFunction_svgColorMap.MAX_PALETTE_COLORS;
import java.util.HashMap;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.transform.TransformerException;
import org.custommonkey.xmlunit.SimpleNamespaceContext;
import org.custommonkey.xmlunit.XMLUnit;
import org.custommonkey.xmlunit.XpathEngine;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CoverageDimensionInfo;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.CoverageStoreInfo;
import org.geoserver.data.test.MockData;
import org.geoserver.data.test.SystemTestData;
import org.geoserver.data.test.SystemTestData.LayerProperty;
import org.geoserver.test.GeoServerSystemTestSupport;
import org.geoserver.wms.map.GetMapKvpRequestReader;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.styling.ColorMap;
import org.geotools.styling.ColorMapEntry;
import org.geotools.styling.SLDTransformer;
import org.geotools.util.NumberRange;
import org.junit.Test;
import org.opengis.coverage.grid.GridCoverageReader;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.expression.Function;
import it.geosolutions.imageio.utilities.ImageIOUtilities;
public class DynamicColorMapTest extends GeoServerSystemTestSupport {
private static final String COVERAGE_NAME = "watertemp_dynamic";
private static final double TOLERANCE = 0.01;
GetMapKvpRequestReader requestReader;
protected static XpathEngine xp;
@Override
protected void onSetUp(SystemTestData testData) throws Exception {
super.onSetUp(testData);
Catalog catalog = getCatalog();
Map<String, String> namespaces = new HashMap<String, String>();
namespaces.put("html", "http://www.w3.org/1999/xhtml");
namespaces.put("sld", "http://www.opengis.net/sld");
namespaces.put("ogc", "http://www.opengis.net/ogc");
namespaces.put("atom", "http://www.w3.org/2005/Atom");
XMLUnit.setXpathNamespaceContext(new SimpleNamespaceContext(namespaces));
xp = XMLUnit.newXpathEngine();
testData.addStyle("style_rgb","test-data/style_rgb.sld",DynamicColorMapTest.class, catalog);
Map<LayerProperty, Object> properties = new HashMap<>();
properties.put(LayerProperty.STYLE, "style_rgb");
testData.addRasterLayer(new QName(MockData.DEFAULT_URI, "watertemp_dynamic", MockData.DEFAULT_PREFIX),
"test-data/watertemp_dynamic.zip",null, properties, DynamicColorMapTest.class, catalog);
// setup manual statistics
CoverageInfo coverage = getCatalog().getCoverageByName("watertemp_dynamic");
CoverageDimensionInfo di = coverage.getDimensions().get(0);
di.setRange(new NumberRange<Double>(Double.class, 0., 0.5));
getCatalog().save(coverage);
}
@Test
public void testGridCoverageStats() throws Exception {
// check the coverage is actually there
Catalog catalog = getCatalog();
CoverageStoreInfo storeInfo = catalog.getCoverageStoreByName(COVERAGE_NAME);
assertNotNull(storeInfo);
CoverageInfo ci = catalog.getCoverageByName(COVERAGE_NAME);
assertNotNull(ci);
assertEquals(storeInfo, ci.getStore());
// Test on the GridCoverageStats
FilterFunction_gridCoverageStats funcStat = new FilterFunction_gridCoverageStats();
GridCoverageReader reader = ci.getGridCoverageReader(null, null);
GridCoverage2D gridCoverage = (GridCoverage2D) reader.read(null);
double min = (Double) funcStat.evaluate(gridCoverage, "minimum");
double max = (Double) funcStat.evaluate(gridCoverage, "maximum");
assertEquals(min, 13.1369, TOLERANCE);
assertEquals(max, 20.665, TOLERANCE);
ImageIOUtilities.disposeImage(gridCoverage.getRenderedImage());
}
@Test
public void testGridBandStats() throws Exception {
// check the coverage is actually there
Catalog catalog = getCatalog();
CoverageStoreInfo storeInfo = catalog.getCoverageStoreByName(COVERAGE_NAME);
assertNotNull(storeInfo);
CoverageInfo ci = catalog.getCoverageByName(COVERAGE_NAME);
assertNotNull(ci);
assertEquals(storeInfo, ci.getStore());
// Test on the GridCoverageStats
final FilterFactory ff = CommonFactoryFinder.getFilterFactory();
Function minStat = ff.function("bandStats", ff.literal(0), ff.literal("minimum"));
Function maxStat = ff.function("bandStats", ff.literal(0), ff.literal("maximum"));
GridCoverageReader reader = ci.getGridCoverageReader(null, null);
GridCoverage2D gridCoverage = (GridCoverage2D) reader.read(null);
double min = (Double) minStat.evaluate(gridCoverage);
double max = (Double) maxStat.evaluate(gridCoverage);
assertEquals(min, 0, TOLERANCE);
assertEquals(max, 0.5, TOLERANCE);
ImageIOUtilities.disposeImage(gridCoverage.getRenderedImage());
}
@Test
public void testSvgColorMapFilterFunctionRGB() throws Exception {
final FilterFunction_svgColorMap func = new FilterFunction_svgColorMap();
final ColorMap colorMap = (ColorMap) func.evaluate("rgb(0,0,255);rgb(0,255,0);rgb(255,0,0)", 10, 100, null, null, false, MAX_PALETTE_COLORS);
final ColorMapEntry[] entries = colorMap.getColorMapEntries();
check(entries);
}
@Test
public void testSvgColorMapFilterFunctionHEX() throws Exception {
final FilterFunction_svgColorMap func = new FilterFunction_svgColorMap();
final ColorMap colorMap = (ColorMap) func.evaluate("#0000FF;#00FF00;#FF0000", 10, 100, null, null, false, MAX_PALETTE_COLORS);
final ColorMapEntry[] entries = colorMap.getColorMapEntries();
check(entries);
}
private void check(ColorMapEntry[] entries) {
assertEquals(5, entries.length);
assertColorMapEntry(entries[0], "#000000", 0.0, 10d);
assertColorMapEntry(entries[1], "#0000FF", 1.0, 10d);
assertColorMapEntry(entries[2], "#00FF00", 1.0, 55d);
assertColorMapEntry(entries[3], "#FF0000", 1.0, 100d);
assertColorMapEntry(entries[4], "#000000", 0.0, 100d);
}
@Test
public void testSvgColorMapFilterFunctionRGBWithExpression() throws Exception {
FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
checkFunction(ff.function("colormap", ff.literal("rgb(0,0,255);rgb(0,255,0);rgb(255,0,0)"),
ff.literal(10), ff.literal(100)));
checkFunction(ff.function("colormap", ff.literal("rgb(0,0,255);rgb(0,255,0);rgb(255,0,0)"),
ff.literal(10), ff.literal(100), ff.literal(null), ff.literal(null), ff.literal("false"),
ff.literal(MAX_PALETTE_COLORS)));
}
private void checkFunction(Function colorMapFunction) {
ColorMap colorMap = (ColorMap) colorMapFunction.evaluate(null);
final ColorMapEntry[] entries = colorMap.getColorMapEntries();
check(entries);
}
@Test
public void testBeforeAfterColor() throws Exception {
final FilterFunction_svgColorMap func = new FilterFunction_svgColorMap();
final ColorMap colorMap = (ColorMap) func.evaluate("#0000FF;#00FF00;#FF0000", 10, 100, "#FFFFFF", "#000000", false, MAX_PALETTE_COLORS);
final ColorMapEntry[] entries = colorMap.getColorMapEntries();
assertColorMapEntry(entries[0], "#FFFFFF", 1.0, 9.99);
assertColorMapEntry(entries[1], "#0000FF", 1.0, 10d);
assertColorMapEntry(entries[2], "#00FF00", 1.0, 55d);
assertColorMapEntry(entries[3], "#FF0000", 1.0, 100d);
assertColorMapEntry(entries[4], "#000000", 1.0, 100d);
}
void assertColorMapEntry(ColorMapEntry cme, String expectedColor, Double expectedOpacity, Double expectedValue) {
if(expectedColor != null) {
assertEquals(expectedColor, cme.getColor().evaluate(null, String.class));
}
if(expectedOpacity != null) {
if(cme.getOpacity() == null || cme.getOpacity().evaluate(null) == null) {
assertEquals(expectedOpacity, 1d, TOLERANCE);
} else {
assertEquals(expectedOpacity, cme.getOpacity().evaluate(null, Double.class), TOLERANCE);
}
}
if(expectedValue != null) {
assertEquals(expectedValue, cme.getQuantity().evaluate(null, Double.class), TOLERANCE);
}
}
@Test
public void testLogarithmic() throws Exception {
final FilterFunction_svgColorMap func = new FilterFunction_svgColorMap();
final ColorMap colorMap = (ColorMap) func.evaluate("#0000FF;#00FF00;#FF0000", 10, 100, null, null, true, MAX_PALETTE_COLORS);
final ColorMapEntry[] entries = colorMap.getColorMapEntries();
assertEquals(FilterFunction_svgColorMap.LOG_SAMPLING_DEFAULT + 2, entries.length);
// first and last are transparent
assertEquals(0, entries[0].getOpacity().evaluate(null, Double.class), TOLERANCE);
assertEquals(0, entries[FilterFunction_svgColorMap.LOG_SAMPLING_DEFAULT + 1].getOpacity().evaluate(null, Double.class), TOLERANCE);
// check the logaritmic progression
double logMin = Math.log(10);
double logMax = Math.log(100);
double step = (logMax - logMin) / FilterFunction_svgColorMap.LOG_SAMPLING_DEFAULT;
for(int i = 0; i < FilterFunction_svgColorMap.LOG_SAMPLING_DEFAULT - 1; i++) {
final double v = logMin + step * i;
double expected = Math.exp(v);
assertEquals("Failed at " + i, expected, entries[i + 1].getQuantity().evaluate(null, Double.class),TOLERANCE);
}
assertEquals(100, entries[FilterFunction_svgColorMap.LOG_SAMPLING_DEFAULT].getQuantity().evaluate(null, Double.class),TOLERANCE);
}
@Test
public void testOneColor() throws Exception {
final FilterFunction_svgColorMap func = new FilterFunction_svgColorMap();
final ColorMap colorMap = (ColorMap) func.evaluate("#0000FF;#00FF00;#FF0000", 10, 100, null, null, false, 1);
final ColorMapEntry[] entries = colorMap.getColorMapEntries();
assertEquals(3, entries.length);
assertColorMapEntry(entries[0], "#000000", 0.0, 10d);
assertColorMapEntry(entries[1], "#FF0000", 1.0, 100d);
assertColorMapEntry(entries[2], "#000000", 0.0, Double.POSITIVE_INFINITY);
}
@Test
public void testTwoColors() throws Exception {
final FilterFunction_svgColorMap func = new FilterFunction_svgColorMap();
final ColorMap colorMap = (ColorMap) func.evaluate("#0000FF;#00FF00;#FF0000", 10, 100, null, null, false, 2);
// logColorMap(colorMap);
final ColorMapEntry[] entries = colorMap.getColorMapEntries();
assertEquals(4, entries.length);
assertColorMapEntry(entries[0], "#000000", 0.0, 10d);
assertColorMapEntry(entries[1], "#0000FF", 1.0, 55d);
assertColorMapEntry(entries[2], "#FF0000", 1.0, 100d);
assertColorMapEntry(entries[3], "#000000", 0.0, Double.POSITIVE_INFINITY);
}
void logColorMap(ColorMap colorMap) {
SLDTransformer tx = new SLDTransformer();
tx.setIndentation(2);
try {
tx.transform(colorMap, System.out);
} catch (TransformerException e) {
e.printStackTrace();
}
}
@Test
public void testThreeColors() throws Exception {
final FilterFunction_svgColorMap func = new FilterFunction_svgColorMap();
final ColorMap colorMap = (ColorMap) func.evaluate("#0000FF;#00FF00;#FF0000", 10, 100, null, null, false, 3);
// logColorMap(colorMap);
final ColorMapEntry[] entries = colorMap.getColorMapEntries();
assertEquals(5, entries.length);
assertColorMapEntry(entries[0], "#000000", 0.0, 10d);
assertColorMapEntry(entries[1], "#0000FF", 1.0, 40d);
assertColorMapEntry(entries[2], "#00A956", 1.0, 70d);
assertColorMapEntry(entries[3], "#FF0000", 1.0, 100d);
assertColorMapEntry(entries[4], "#000000", 0.0, Double.POSITIVE_INFINITY);
}
}