org.geoserver.wms.map.RenderedImageMapOutputFormatTest.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.wms.map.RenderedImageMapOutputFormatTest.java

Source

/* (c) 2014 - 2015 Open Source Geospatial Foundation - all rights reserved
 * (c) 2001 - 2013 OpenPlans
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */
package org.geoserver.wms.map;

import static org.geoserver.data.test.CiteTestData.STREAMS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.media.jai.Interpolation;
import javax.media.jai.RenderedOp;
import javax.xml.namespace.QName;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.RegexFileFilter;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogBuilder;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.CoverageStoreInfo;
import org.geoserver.catalog.CoverageView;
import org.geoserver.catalog.CoverageView.CompositionType;
import org.geoserver.catalog.CoverageView.CoverageBand;
import org.geoserver.catalog.CoverageView.InputCoverageBand;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.LayerInfo.WMSInterpolation;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.data.test.MockData;
import org.geoserver.data.test.SystemTestData;
import org.geoserver.platform.ServiceException;
import org.geoserver.security.decorators.DecoratingFeatureSource;
import org.geoserver.wms.CachedGridReaderLayer;
import org.geoserver.wms.GetMapRequest;
import org.geoserver.wms.MapLayerInfo;
import org.geoserver.wms.WMS;
import org.geoserver.wms.WMSInfo;
import org.geoserver.wms.WMSMapContent;
import org.geoserver.wms.WMSPartialMapException;
import org.geoserver.wms.WMSTestSupport;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.factory.FactoryRegistryException;
import org.geotools.feature.IllegalAttributeException;
import org.geotools.feature.SchemaException;
import org.geotools.filter.IllegalFilterException;
import org.geotools.gce.imagemosaic.ImageMosaicReader;
import org.geotools.geometry.jts.LiteShape2;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.image.test.ImageAssert;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.parameter.Parameter;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.renderer.lite.LabelCache;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.resources.coverage.FeatureUtilities;
import org.geotools.resources.image.ImageUtilities;
import org.geotools.styling.ChannelSelection;
import org.geotools.styling.ChannelSelectionImpl;
import org.geotools.styling.RasterSymbolizer;
import org.geotools.styling.SelectedChannelType;
import org.geotools.styling.SelectedChannelTypeImpl;
import org.geotools.styling.Style;
import org.geotools.styling.StyleBuilder;
import org.geotools.styling.TextSymbolizer;
import org.geotools.util.NumberRange;
import org.geotools.util.URLs;
import org.geotools.util.logging.Logging;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.opengis.feature.Feature;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;

import com.vividsolutions.jts.geom.Envelope;

public class RenderedImageMapOutputFormatTest extends WMSTestSupport {

    public static QName TAZ_BYTE = new QName(MockData.WCS_URI, "tazbyte", MockData.WCS_PREFIX);

    private static final Logger LOGGER = org.geotools.util.logging.Logging
            .getLogger(RenderedImageMapOutputFormatTest.class.getPackage().getName());

    private RenderedImageMapOutputFormat rasterMapProducer;

    private String mapFormat = "image/gif";

    private static final ThreadLocal<Boolean> usedCustomLabelCache = new ThreadLocal<Boolean>();

    public static class CustomLabelCache implements LabelCache {
        public CustomLabelCache() {

        }

        @Override
        public void clear() {

        }

        @Override
        public void clear(String arg0) {

        }

        @Override
        public void disableLayer(String arg0) {

        }

        @Override
        public void enableLayer(String arg0) {

        }

        @Override
        public void end(Graphics2D arg0, Rectangle arg1) {
            usedCustomLabelCache.set(true);
        }

        @Override
        public void endLayer(String arg0, Graphics2D arg1, Rectangle arg2) {

        }

        @Override
        public List orderedLabels() {
            return null;
        }

        @Override
        public void put(Rectangle2D arg0) {

        }

        @Override
        public void put(String arg0, TextSymbolizer arg1, Feature arg2, LiteShape2 arg3, NumberRange<Double> arg4) {

        }

        @Override
        public void start() {

        }

        @Override
        public void startLayer(String arg0) {

        }

        @Override
        public void stop() {

        }

    };

    @Before
    public void setRasterMapProducer() throws Exception {
        Logging.getLogger("org.geotools.rendering").setLevel(Level.OFF);
        this.rasterMapProducer = getProducerInstance();

        getTestData().addDefaultRasterLayer(SystemTestData.MULTIBAND, getCatalog());
    }

    protected RenderedImageMapOutputFormat getProducerInstance() {
        return new DummyRasterMapProducer(getWMS());
    }

    @After
    public void unsetRasterMapProducer() throws Exception {
        this.rasterMapProducer = null;
    }

    public String getMapFormat() {
        return this.mapFormat;
    }

    @Test
    public void testSimpleGetMapQuery() throws Exception {

        Catalog catalog = getCatalog();
        final FeatureSource fs = catalog
                .getFeatureTypeByName(MockData.BASIC_POLYGONS.getPrefix(), MockData.BASIC_POLYGONS.getLocalPart())
                .getFeatureSource(null, null);

        final Envelope env = fs.getBounds();

        LOGGER.info("about to create map ctx for BasicPolygons with bounds " + env);

        GetMapRequest request = new GetMapRequest();
        final WMSMapContent map = new WMSMapContent();
        map.getViewport().setBounds(new ReferencedEnvelope(env, DefaultGeographicCRS.WGS84));
        map.setMapWidth(300);
        map.setMapHeight(300);
        map.setBgColor(Color.red);
        map.setTransparent(false);
        map.setRequest(request);

        StyleInfo styleByName = catalog.getStyleByName("Default");
        Style basicStyle = styleByName.getStyle();
        map.addLayer(new FeatureLayer(fs, basicStyle));

        request.setFormat(getMapFormat());
        RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
        BufferedImage image = (BufferedImage) imageMap.getImage();
        imageMap.dispose();
        assertNotBlank("testSimpleGetMapQuery", image);
    }

    /**
     * Test to make sure the "direct" raster path and the "nondirect" raster path
     * produce matching results. This test was originally created after fixes to GEOS-7270
     * where there were issues with images generated during the direct raster path but not
     * in the normal path, stemming from not setting the background color the same way
     */
    @Test
    public void testDirectVsNonDirectRasterRender() throws Exception {
        Catalog catalog = getCatalog();
        CoverageInfo ci = catalog.getCoverageByName(SystemTestData.MULTIBAND.getPrefix(),
                SystemTestData.MULTIBAND.getLocalPart());

        final Envelope env = ci.boundingBox();

        LOGGER.info("about to create map ctx for BasicPolygons with bounds " + env);

        GetMapRequest request = new GetMapRequest();
        CoordinateReferenceSystem crs = DefaultGeographicCRS.WGS84;
        ReferencedEnvelope bbox = new ReferencedEnvelope(new Envelope(-116.90673461649858211,
                -114.30988665660261461, 32.07093728218402617, 33.89032847348440214), crs);
        request.setBbox(bbox);
        request.setSRS("urn:x-ogc:def:crs:EPSG:4326");
        request.setFormat("image/png");

        final WMSMapContent map = new WMSMapContent(request);
        map.setMapWidth(300);
        map.setMapHeight(300);
        map.setBgColor(Color.red);
        map.setTransparent(false);
        map.getViewport().setBounds(bbox);

        StyleBuilder builder = new StyleBuilder();
        GridCoverage2DReader reader = (GridCoverage2DReader) ci.getGridCoverageReader(null, null);
        reader.getCoordinateReferenceSystem();
        Layer l = new CachedGridReaderLayer(reader, builder.createStyle(builder.createRasterSymbolizer()));
        map.addLayer(l);

        RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
        ImageAssert.assertEquals(new File("src/test/resources/org/geoserver/wms/map/direct-raster-expected.tif"),
                imageMap.getImage(), 0);
        imageMap.dispose();
    }

    @Test
    public void testTimeoutOption() throws Exception {
        Catalog catalog = getCatalog();
        GetMapRequest request = new GetMapRequest();
        final WMSMapContent map = new WMSMapContent();

        StyleInfo styleByName = catalog.getStyleByName("Default");
        Style basicStyle = styleByName.getStyle();

        //Build up a complex map so that we can reasonably guarantee a 1 ms timout
        SimpleFeatureSource fs = (SimpleFeatureSource) catalog
                .getFeatureTypeByName(MockData.BASIC_POLYGONS.getPrefix(), MockData.BASIC_POLYGONS.getLocalPart())
                .getFeatureSource(null, null);
        Envelope env = fs.getBounds();
        SimpleFeatureCollection features = fs.getFeatures();
        SimpleFeatureCollection delayedCollection = new DelayedFeatureCollection(features, 10);
        map.addLayer(new FeatureLayer(delayedCollection, basicStyle));

        LOGGER.info("about to create map ctx for " + map.layers().size() + " layers with bounds " + env);

        map.getViewport().setBounds(new ReferencedEnvelope(env, DefaultGeographicCRS.WGS84));
        map.setMapWidth(1000);
        map.setMapHeight(1000);
        map.setRequest(request);

        request.setFormat(getMapFormat());
        Map formatOptions = new HashMap();
        //1 ms timeout
        formatOptions.put("timeout", 1);
        request.setFormatOptions(formatOptions);
        try {
            RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
            fail("Timeout was not reached");
        } catch (ServiceException e) {
            assertTrue(e.getMessage().startsWith("This request used more time than allowed"));
        }

        //Test partial image exception format
        Map rawKvp = new HashMap();
        rawKvp.put("EXCEPTIONS", "PARTIALMAP");
        request.setRawKvp(rawKvp);

        try {
            RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
            fail("Timeout was not reached");
        } catch (ServiceException e) {
            assertTrue(e instanceof WMSPartialMapException);
            assertTrue(e.getCause().getMessage().startsWith("This request used more time than allowed"));
            RenderedImageMap partialMap = (RenderedImageMap) ((WMSPartialMapException) e).getMap();
            assertNotNull(partialMap);
            assertNotNull(partialMap.getImage());
        }
    }

    @Test
    public void testDefaultStyle() throws Exception {
        List<org.geoserver.catalog.FeatureTypeInfo> typeInfos = getCatalog().getFeatureTypes();

        for (org.geoserver.catalog.FeatureTypeInfo info : typeInfos) {
            if (info.getQualifiedName().getNamespaceURI().equals(MockData.CITE_URI)
                    && info.getFeatureType().getGeometryDescriptor() != null)
                testDefaultStyle(info.getFeatureSource(null, null));
        }
    }

    @Test
    public void testBlueLake() throws IOException, IllegalFilterException, Exception {
        final Catalog catalog = getCatalog();
        org.geoserver.catalog.FeatureTypeInfo typeInfo = catalog
                .getFeatureTypeByName(MockData.LAKES.getNamespaceURI(), MockData.LAKES.getLocalPart());
        Envelope env = typeInfo.getFeatureSource(null, null).getBounds();
        double shift = env.getWidth() / 6;

        env = new Envelope(env.getMinX() - shift, env.getMaxX() + shift, env.getMinY() - shift,
                env.getMaxY() + shift);

        GetMapRequest request = new GetMapRequest();
        final WMSMapContent map = new WMSMapContent();
        int w = 400;
        int h = (int) Math.round((env.getHeight() * w) / env.getWidth());
        map.setMapWidth(w);
        map.setMapHeight(h);
        map.setBgColor(BG_COLOR);
        map.setTransparent(true);
        map.setRequest(request);

        addToMap(map, MockData.FORESTS);
        addToMap(map, MockData.LAKES);
        addToMap(map, MockData.STREAMS);
        addToMap(map, MockData.NAMED_PLACES);
        addToMap(map, MockData.ROAD_SEGMENTS);
        addToMap(map, MockData.PONDS);
        addToMap(map, MockData.BUILDINGS);
        addToMap(map, MockData.DIVIDED_ROUTES);
        addToMap(map, MockData.BRIDGES);
        addToMap(map, MockData.MAP_NEATLINE);

        map.getViewport().setBounds(new ReferencedEnvelope(env, DefaultGeographicCRS.WGS84));

        request.setFormat(getMapFormat());
        RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
        BufferedImage image = (BufferedImage) imageMap.getImage();
        imageMap.dispose();
        assertNotBlank("testBlueLake", image);
    }

    @Test
    public void testCustomLabelCache() throws IOException {
        final Catalog catalog = getCatalog();
        org.geoserver.catalog.FeatureTypeInfo typeInfo = catalog
                .getFeatureTypeByName(MockData.LAKES.getNamespaceURI(), MockData.LAKES.getLocalPart());
        Envelope env = typeInfo.getFeatureSource(null, null).getBounds();
        double shift = env.getWidth() / 6;

        env = new Envelope(env.getMinX() - shift, env.getMaxX() + shift, env.getMinY() - shift,
                env.getMaxY() + shift);

        GetMapRequest request = new GetMapRequest();
        final WMSMapContent map = new WMSMapContent();
        int w = 400;
        int h = (int) Math.round((env.getHeight() * w) / env.getWidth());
        map.setMapWidth(w);
        map.setMapHeight(h);
        map.setBgColor(BG_COLOR);
        map.setTransparent(true);
        map.setRequest(request);

        addToMap(map, MockData.FORESTS);
        addToMap(map, MockData.LAKES);
        addToMap(map, MockData.STREAMS);
        addToMap(map, MockData.NAMED_PLACES);
        addToMap(map, MockData.ROAD_SEGMENTS);
        addToMap(map, MockData.PONDS);
        addToMap(map, MockData.BUILDINGS);
        addToMap(map, MockData.DIVIDED_ROUTES);
        addToMap(map, MockData.BRIDGES);
        addToMap(map, MockData.MAP_NEATLINE);

        map.getViewport().setBounds(new ReferencedEnvelope(env, DefaultGeographicCRS.WGS84));

        request.setFormat(getMapFormat());

        this.rasterMapProducer.setLabelCache(new Function<WMSMapContent, LabelCache>() {
            @Override
            public LabelCache apply(WMSMapContent mapContent) {
                return new CustomLabelCache();
            }
        });
        RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
        BufferedImage image = (BufferedImage) imageMap.getImage();
        imageMap.dispose();
        assertTrue(usedCustomLabelCache.get());
        assertNotBlank("testBlueLake", image);
    }

    @Override
    protected void onSetUp(SystemTestData testData) throws Exception {
        super.onSetUp(testData);
        testData.addDefaultRasterLayer(MockData.TASMANIA_DEM, getCatalog());
        testData.addRasterLayer(TAZ_BYTE, "tazbyte.tiff", null, getCatalog());
    }

    @Test
    public void testInterpolations() throws IOException, IllegalFilterException, Exception {
        final Catalog catalog = getCatalog();
        CoverageInfo coverageInfo = catalog.getCoverageByName(MockData.TASMANIA_DEM.getNamespaceURI(),
                MockData.TASMANIA_DEM.getLocalPart());

        Envelope env = coverageInfo.boundingBox();
        double shift = env.getWidth() / 6;

        env = new Envelope(env.getMinX() - shift, env.getMaxX() + shift, env.getMinY() - shift,
                env.getMaxY() + shift);

        GetMapRequest request = new GetMapRequest();
        WMSMapContent map = new WMSMapContent();
        int w = 400;
        int h = (int) Math.round((env.getHeight() * w) / env.getWidth());
        map.setMapWidth(w);
        map.setMapHeight(h);
        map.setBgColor(BG_COLOR);
        map.setTransparent(true);
        map.setRequest(request);
        addRasterToMap(map, MockData.TASMANIA_DEM);

        map.getViewport().setBounds(new ReferencedEnvelope(env, DefaultGeographicCRS.WGS84));
        request.setInterpolations(Arrays.asList(Interpolation.getInstance(Interpolation.INTERP_NEAREST)));
        request.setFormat(getMapFormat());
        RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
        RenderedOp op = (RenderedOp) imageMap.getImage();
        BufferedImage imageNearest = op.getAsBufferedImage();
        imageMap.dispose();
        assertNotBlank("testInterpolationsNearest", imageNearest);

        request = new GetMapRequest();
        map = new WMSMapContent();
        map.setMapWidth(w);
        map.setMapHeight(h);
        map.setBgColor(BG_COLOR);
        map.setTransparent(true);
        map.setRequest(request);
        addRasterToMap(map, MockData.TASMANIA_DEM);

        map.getViewport().setBounds(new ReferencedEnvelope(env, DefaultGeographicCRS.WGS84));
        request.setInterpolations(Arrays.asList(Interpolation.getInstance(Interpolation.INTERP_BICUBIC)));
        request.setFormat(getMapFormat());

        imageMap = this.rasterMapProducer.produceMap(map);
        op = (RenderedOp) imageMap.getImage();
        BufferedImage imageBicubic = op.getAsBufferedImage();
        imageMap.dispose();
        assertNotBlank("testInterpolationsBicubic", imageBicubic);
        // test some sample pixels to check rendering is different using different interpolations
        assertNotEquals(getPixelColor(imageNearest, 160, 160).getRGB(),
                getPixelColor(imageBicubic, 160, 160).getRGB());
        assertNotEquals(getPixelColor(imageNearest, 300, 450).getRGB(),
                getPixelColor(imageBicubic, 300, 450).getRGB());
    }

    @Test
    public void testInterpolationFromLayerConfig() throws IOException, IllegalFilterException, Exception {
        final Catalog catalog = getCatalog();

        LayerInfo layerInfo = catalog.getLayerByName(MockData.TASMANIA_DEM.getLocalPart());

        MapLayerInfo mapLayer = new MapLayerInfo(layerInfo);
        assertNull(layerInfo.getDefaultWMSInterpolationMethod());

        Envelope env = layerInfo.getResource().boundingBox();
        double shift = env.getWidth() / 6;

        env = new Envelope(env.getMinX() - shift, env.getMaxX() + shift, env.getMinY() - shift,
                env.getMaxY() + shift);

        // set Nearest Neighbor interpolation on layer
        GetMapRequest request = new GetMapRequest();
        request.setFormat(getMapFormat());
        request.setLayers(Arrays.asList(mapLayer));

        layerInfo.setDefaultWMSInterpolationMethod(WMSInterpolation.Nearest);
        assertEquals(WMSInterpolation.Nearest,
                request.getLayers().get(0).getLayerInfo().getDefaultWMSInterpolationMethod());
        assertTrue(request.getInterpolations().isEmpty());

        WMSMapContent map = createWMSMap(env);
        map.setRequest(request);

        RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
        RenderedOp op = (RenderedOp) imageMap.getImage();
        BufferedImage imageNearest = op.getAsBufferedImage();
        imageMap.dispose();
        assertNotBlank("testInterpolationsNearest", imageNearest);

        // set Bicubic interpolation on layer
        request = new GetMapRequest();
        request.setFormat(getMapFormat());
        request.setLayers(Arrays.asList(mapLayer));

        layerInfo.setDefaultWMSInterpolationMethod(WMSInterpolation.Bicubic);
        assertEquals(WMSInterpolation.Bicubic,
                request.getLayers().get(0).getLayerInfo().getDefaultWMSInterpolationMethod());
        assertTrue(request.getInterpolations().isEmpty());

        map = createWMSMap(env);
        map.setRequest(request);

        imageMap = this.rasterMapProducer.produceMap(map);
        op = (RenderedOp) imageMap.getImage();
        BufferedImage imageBicubic = op.getAsBufferedImage();
        imageMap.dispose();
        assertNotBlank("testInterpolationsBicubic", imageBicubic);
        // test some sample pixels to check rendering is different using different interpolations
        assertNotEquals(getPixelColor(imageNearest, 160, 160).getRGB(),
                getPixelColor(imageBicubic, 160, 160).getRGB());
        assertNotEquals(getPixelColor(imageNearest, 300, 450).getRGB(),
                getPixelColor(imageBicubic, 300, 450).getRGB());

        // check also the *non* direct raster render path
        request = new GetMapRequest();
        request.setFormat(getMapFormat());
        // adding layer twice on purpose to disable direct raster render
        request.setLayers(Arrays.asList(mapLayer, mapLayer));

        layerInfo.setDefaultWMSInterpolationMethod(WMSInterpolation.Bicubic);
        assertEquals(WMSInterpolation.Bicubic,
                request.getLayers().get(0).getLayerInfo().getDefaultWMSInterpolationMethod());
        assertTrue(request.getInterpolations().isEmpty());

        map = createWMSMap(env);
        map.setRequest(request);
        // adding layer twice on purpose to disable direct raster render
        addRasterToMap(map, MockData.TASMANIA_DEM);

        imageMap = this.rasterMapProducer.produceMap(map);
        checkByLayerInterpolation(imageMap, Interpolation.getInstance(Interpolation.INTERP_BICUBIC));

        // interpolation method specified in the request overrides service and layer configuration
        request = new GetMapRequest();
        // request says "Bicubic"
        request.setInterpolations(Arrays.asList(Interpolation.getInstance(Interpolation.INTERP_BICUBIC)));
        request.setFormat(getMapFormat());
        // adding layer twice on purpose to disable direct raster render
        request.setLayers(Arrays.asList(mapLayer, mapLayer));

        // layer config says "Bilinear"
        layerInfo.setDefaultWMSInterpolationMethod(WMSInterpolation.Bilinear);
        assertEquals(WMSInterpolation.Bilinear,
                request.getLayers().get(0).getLayerInfo().getDefaultWMSInterpolationMethod());

        // service config says "Nearest"
        assertEquals(WMSInfo.WMSInterpolation.Nearest, getWMS().getServiceInfo().getInterpolation());

        map = createWMSMap(env);
        map.setRequest(request);
        // adding layer twice on purpose to disable direct raster render
        addRasterToMap(map, MockData.TASMANIA_DEM);

        imageMap = this.rasterMapProducer.produceMap(map);
        checkByLayerInterpolation(imageMap, Interpolation.getInstance(Interpolation.INTERP_BICUBIC));

        // if default interpolation method is not specified, service default is used
        request = new GetMapRequest();
        request.setFormat(getMapFormat());
        request.setLayers(Arrays.asList(mapLayer));

        layerInfo.setDefaultWMSInterpolationMethod(null);
        assertEquals(null, request.getLayers().get(0).getLayerInfo().getDefaultWMSInterpolationMethod());
        assertTrue(request.getInterpolations().isEmpty());

        assertEquals(WMSInfo.WMSInterpolation.Nearest, getWMS().getServiceInfo().getInterpolation());

        map = createWMSMap(env);
        map.setRequest(request);

        imageMap = this.rasterMapProducer.produceMap(map);
        op = (RenderedOp) imageMap.getImage();
        BufferedImage imageServiceDefault = op.getAsBufferedImage();
        imageMap.dispose();
        assertNotBlank("testInterpolationServiceDefault", imageServiceDefault);
        // test produced image is equal to imageNearest
        assertEquals(getPixelColor(imageNearest, 200, 200).getRGB(),
                getPixelColor(imageServiceDefault, 200, 200).getRGB());
        assertEquals(getPixelColor(imageNearest, 300, 300).getRGB(),
                getPixelColor(imageServiceDefault, 300, 300).getRGB());
        assertEquals(getPixelColor(imageNearest, 250, 250).getRGB(),
                getPixelColor(imageServiceDefault, 250, 250).getRGB());
        assertEquals(getPixelColor(imageNearest, 150, 150).getRGB(),
                getPixelColor(imageServiceDefault, 150, 150).getRGB());
    }

    /*
     * NOTE: this check is valid only if the direct raster render path is *not* taken
     */
    private void checkByLayerInterpolation(RenderedImageMap imageMap, Interpolation expected) {
        Layer layer = imageMap.getMapContext().layers().get(0);
        Interpolation actual = (Interpolation) layer.getUserData().get(StreamingRenderer.BYLAYER_INTERPOLATION);
        assertEquals(expected, actual);
    }

    private WMSMapContent createWMSMap(Envelope bounds) throws Exception {
        WMSMapContent map = new WMSMapContent();

        int w = 400;
        int h = (int) Math.round((bounds.getHeight() * w) / bounds.getWidth());
        map.setMapWidth(w);
        map.setMapHeight(h);
        map.setBgColor(BG_COLOR);
        map.setTransparent(true);
        addRasterToMap(map, MockData.TASMANIA_DEM);

        map.getViewport().setBounds(new ReferencedEnvelope(bounds, DefaultGeographicCRS.WGS84));

        return map;
    }

    private void addRasterToMap(final WMSMapContent map, final QName typeName)
            throws IOException, FactoryRegistryException, TransformException, SchemaException {
        final CoverageInfo coverageInfo = getCatalog().getCoverageByName(typeName.getNamespaceURI(),
                typeName.getLocalPart());

        List<LayerInfo> layers = getCatalog().getLayers(coverageInfo);
        StyleInfo defaultStyle = layers.get(0).getDefaultStyle();
        Style style = defaultStyle.getStyle();

        SimpleFeatureCollection fc = FeatureUtilities.wrapGridCoverageReader(
                (GridCoverage2DReader) coverageInfo.getGridCoverageReader(null, null),
                new GeneralParameterValue[] {});
        map.addLayer(new FeatureLayer(fc, style));
    }

    private void addToMap(final WMSMapContent map, final QName typeName) throws IOException {
        final FeatureTypeInfo ftInfo = getCatalog().getFeatureTypeByName(typeName.getNamespaceURI(),
                typeName.getLocalPart());

        List<LayerInfo> layers = getCatalog().getLayers(ftInfo);
        StyleInfo defaultStyle = layers.get(0).getDefaultStyle();
        Style style = defaultStyle.getStyle();

        map.addLayer(new FeatureLayer(ftInfo.getFeatureSource(null, null), style));
    }

    private void testDefaultStyle(FeatureSource fSource) throws Exception {
        Catalog catalog = getCatalog();
        Style style = catalog.getStyleByName("Default").getStyle();

        FeatureTypeInfo typeInfo = catalog.getFeatureTypeByName(MockData.LAKES.getNamespaceURI(),
                MockData.LAKES.getLocalPart());
        Envelope env = typeInfo.getFeatureSource(null, null).getBounds();
        env.expandToInclude(fSource.getBounds());

        int w = 400;
        int h = (int) Math.round((env.getHeight() * w) / env.getWidth());

        double shift = env.getWidth() / 6;

        env = new Envelope(env.getMinX() - shift, env.getMaxX() + shift, env.getMinY() - shift,
                env.getMaxY() + shift);

        WMSMapContent map = new WMSMapContent();
        GetMapRequest request = new GetMapRequest();
        map.setRequest(request);
        map.addLayer(new FeatureLayer(fSource, style));
        map.getViewport().setBounds(new ReferencedEnvelope(env, DefaultGeographicCRS.WGS84));
        map.setMapWidth(w);
        map.setMapHeight(h);
        map.setBgColor(BG_COLOR);
        map.setTransparent(false);

        // this.rasterMapProducer.setOutputFormat(getMapFormat());
        // this.rasterMapProducer.setMapContext(map);
        // this.rasterMapProducer.produceMap();
        request.setFormat(getMapFormat());

        RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);

        RenderedImage image = imageMap.getImage();
        imageMap.dispose();
        assertNotNull(image);
        String typeName = fSource.getSchema().getName().getLocalPart();
        assertNotBlank("testDefaultStyle " + typeName, (BufferedImage) image);
    }

    /**
     * Checks {@link RenderedImageMapOutputFormat} makes good use of {@link RenderExceptionStrategy}
     */
    @SuppressWarnings("deprecation")
    @Test
    public void testRenderingErrorsHandling() throws Exception {

        // the ones that are ignorable by the renderer
        assertNotNull(forceRenderingError(new TransformException("fake transform exception")));
        assertNotNull(forceRenderingError(new NoninvertibleTransformException("fake non invertible exception")));
        assertNotNull(forceRenderingError(new IllegalAttributeException("non illegal attribute exception")));
        assertNotNull(forceRenderingError(new FactoryException("fake factory exception")));

        // any other one should make the map producer fail
        try {
            forceRenderingError(new RuntimeException("fake runtime exception"));
            fail("Expected WMSException");
        } catch (ServiceException e) {
            assertTrue(true);
        }

        try {
            forceRenderingError(new IOException("fake IO exception"));
            fail("Expected WMSException");
        } catch (ServiceException e) {
            assertTrue(true);
        }

        try {
            forceRenderingError(new IllegalArgumentException("fake IAE exception"));
            fail("Expected WMSException");
        } catch (ServiceException e) {
            assertTrue(true);
        }
    }

    /**
     * Test to check if we can successfully create a direct rendered image by using
     * a coverage view as a source, and a symbolizer defining which three bands of the
     * input coverage view can be used for RGB coloring, and with what order.
     */
    @Test
    public void testStyleUsingChannelsFromCoverageView() throws Exception {

        GetMapRequest request = new GetMapRequest();
        CoordinateReferenceSystem crs = DefaultGeographicCRS.WGS84;
        ReferencedEnvelope bbox = new ReferencedEnvelope(new Envelope(-116.90673461649858211,
                -114.30988665660261461, 32.07093728218402617, 33.89032847348440214), crs);
        request.setBbox(bbox);
        request.setSRS("urn:x-ogc:def:crs:EPSG:4326");
        request.setFormat("image/png");

        final WMSMapContent map = new WMSMapContent(request);
        map.setMapWidth(300);
        map.setMapHeight(300);
        map.setTransparent(false);
        map.getViewport().setBounds(bbox);

        StyleBuilder styleBuilder = new StyleBuilder();

        Catalog catalog = getCatalog();

        // Source image
        CoverageInfo ci = catalog.getCoverageByName(SystemTestData.MULTIBAND.getPrefix(),
                SystemTestData.MULTIBAND.getLocalPart());

        GridCoverage2DReader reader = (GridCoverage2DReader) ci.getGridCoverageReader(null, null);
        reader.getCoordinateReferenceSystem();

        Layer sl = new CachedGridReaderLayer(reader,
                styleBuilder.createStyle(styleBuilder.createRasterSymbolizer()));
        map.addLayer(sl);

        RenderedImageMap srcImageMap = this.rasterMapProducer.produceMap(map);
        RenderedImage srcImage = srcImageMap.getImage();

        // CoverageView band creation. We create a coverage view with 6 bands, using
        // the original bands from the multiband coverage

        //Note that first three bands are int reverse order of the bands of the source coverage
        final InputCoverageBand ib0 = new InputCoverageBand("multiband", "2");
        final CoverageBand b0 = new CoverageBand(Collections.singletonList(ib0), "multiband@2", 0,
                CompositionType.BAND_SELECT);

        final InputCoverageBand ib1 = new InputCoverageBand("multiband", "1");
        final CoverageBand b1 = new CoverageBand(Collections.singletonList(ib1), "multiband@1", 1,
                CompositionType.BAND_SELECT);

        final InputCoverageBand ib2 = new InputCoverageBand("multiband", "0");
        final CoverageBand b2 = new CoverageBand(Collections.singletonList(ib2), "multiband@0", 2,
                CompositionType.BAND_SELECT);

        final InputCoverageBand ib3 = new InputCoverageBand("multiband", "0");
        final CoverageBand b3 = new CoverageBand(Collections.singletonList(ib3), "multiband@0", 0,
                CompositionType.BAND_SELECT);

        final InputCoverageBand ib4 = new InputCoverageBand("multiband", "1");
        final CoverageBand b4 = new CoverageBand(Collections.singletonList(ib4), "multiband@1", 1,
                CompositionType.BAND_SELECT);

        final InputCoverageBand ib5 = new InputCoverageBand("multiband", "2");
        final CoverageBand b5 = new CoverageBand(Collections.singletonList(ib5), "multiband@2", 2,
                CompositionType.BAND_SELECT);

        final List<CoverageBand> coverageBands = new ArrayList<CoverageBand>(1);
        coverageBands.add(b0);
        coverageBands.add(b1);
        coverageBands.add(b2);

        coverageBands.add(b3);
        coverageBands.add(b4);
        coverageBands.add(b5);

        CoverageView multiBandCoverageView = new CoverageView("multiband_select", coverageBands);

        CoverageStoreInfo storeInfo = catalog.getCoverageStoreByName("multiband");
        CatalogBuilder builder = new CatalogBuilder(catalog);

        // Reordered bands coverage
        CoverageInfo coverageInfo = multiBandCoverageView.createCoverageInfo("multiband_select", storeInfo,
                builder);
        coverageInfo.getParameters().put("USE_JAI_IMAGEREAD", "false");
        catalog.add(coverageInfo);
        final LayerInfo layerInfoView = builder.buildLayer(coverageInfo);
        catalog.add(layerInfoView);

        final Envelope env = ci.boundingBox();

        LOGGER.info("about to create map ctx for BasicPolygons with bounds " + env);

        RasterSymbolizer symbolizer = styleBuilder.createRasterSymbolizer();
        ChannelSelection cs = new ChannelSelectionImpl();
        SelectedChannelType red = new SelectedChannelTypeImpl();
        SelectedChannelType green = new SelectedChannelTypeImpl();
        SelectedChannelType blue = new SelectedChannelTypeImpl();

        // We want to create an image where the RGB channels are in reverse order
        // regarding the band order of the input coverage view
        // Note that channel names start with index "1"
        red.setChannelName("3");
        green.setChannelName("2");
        blue.setChannelName("1");

        cs.setRGBChannels(new SelectedChannelType[] { red, green, blue });
        symbolizer.setChannelSelection(cs);

        reader = (GridCoverage2DReader) coverageInfo.getGridCoverageReader(null, null);
        reader.getCoordinateReferenceSystem();
        Layer dl = new CachedGridReaderLayer(reader, styleBuilder.createStyle(symbolizer));
        map.removeLayer(sl);
        map.addLayer(dl);

        RenderedImageMap dstImageMap = this.rasterMapProducer.produceMap(map);
        RenderedImage destImage = dstImageMap.getImage();

        int dWidth = destImage.getWidth();
        int dHeight = destImage.getHeight();

        int[] destImageRowBand0 = new int[dWidth * dHeight];
        int[] destImageRowBand1 = new int[destImageRowBand0.length];
        int[] destImageRowBand2 = new int[destImageRowBand0.length];
        destImage.getData().getSamples(0, 0, dWidth, dHeight, 0, destImageRowBand0);
        destImage.getData().getSamples(0, 0, dWidth, dHeight, 1, destImageRowBand1);
        destImage.getData().getSamples(0, 0, dWidth, dHeight, 2, destImageRowBand2);

        int sWidth = srcImage.getWidth();
        int sHeight = srcImage.getHeight();

        int[] srcImageRowBand0 = new int[sWidth * sHeight];
        int[] srcImageRowBand2 = new int[srcImageRowBand0.length];

        srcImage.getData().getSamples(0, 0, sWidth, sHeight, 0, srcImageRowBand0);

        // Source and result image first bands should be the same. We have reversed the order
        // of the three first bands of the source coverage and then we re-reversed the three
        // first bands using channel selection on the raster symbolizer used for rendering.
        Assert.assertTrue(Arrays.equals(destImageRowBand0, srcImageRowBand0));
        //Result band 0 should not be equal to source image band 2
        Assert.assertFalse(Arrays.equals(destImageRowBand0, srcImageRowBand2));

        srcImageMap.dispose();
        dstImageMap.dispose();

        map.dispose();
    }

    /**
     * Test to check the case where a {@link org.geotools.coverage.grid.io.AbstractGridFormat#BANDS}
     * reading parameter is passed to a coverage reader that does not support it. Reader should 
     * ignore it, resulting coverage should not be affected.
     */
    @Test
    public void testBandSelectionToNormalCoverage() throws Exception {

        GetMapRequest request = new GetMapRequest();
        CoordinateReferenceSystem crs = DefaultGeographicCRS.WGS84;
        ReferencedEnvelope bbox = new ReferencedEnvelope(new Envelope(-116.90673461649858211,
                -114.30988665660261461, 32.07093728218402617, 33.89032847348440214), crs);
        request.setBbox(bbox);
        request.setSRS("urn:x-ogc:def:crs:EPSG:4326");
        request.setFormat("image/png");

        final WMSMapContent map = new WMSMapContent(request);
        map.setMapWidth(300);
        map.setMapHeight(300);
        map.setBgColor(Color.red);
        map.setTransparent(false);
        map.getViewport().setBounds(bbox);

        StyleBuilder styleBuilder = new StyleBuilder();
        Catalog catalog = getCatalog();

        CoverageInfo ci = catalog.getCoverageByName(SystemTestData.MULTIBAND.getPrefix(),
                SystemTestData.MULTIBAND.getLocalPart());

        GridCoverage2DReader reader = (GridCoverage2DReader) ci.getGridCoverageReader(null, null);
        reader.getCoordinateReferenceSystem();

        final Envelope env = ci.boundingBox();

        final int[] bandIndices = new int[] { 1, 2, 0, 2, 1 };
        //Inject bandIndices read param
        Parameter<int[]> bandIndicesParam = (Parameter<int[]>) AbstractGridFormat.BANDS.createValue();
        bandIndicesParam.setValue(bandIndices);
        List<GeneralParameterValue> paramList = new ArrayList<GeneralParameterValue>();
        paramList.add(bandIndicesParam);
        GeneralParameterValue[] readParams = paramList.toArray(new GeneralParameterValue[paramList.size()]);

        Layer sl = new CachedGridReaderLayer(reader,
                styleBuilder.createStyle(styleBuilder.createRasterSymbolizer()), readParams);
        map.addLayer(sl);

        RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
        ImageAssert.assertEquals(new File("src/test/resources/org/geoserver/wms/map/direct-raster-expected.tif"),
                imageMap.getImage(), 0);
        imageMap.dispose();
    }

    @Test
    public void testGetMapOnByteNodataGrayScale() throws Exception {

        GetMapRequest request = new GetMapRequest();
        CoordinateReferenceSystem crs = DefaultGeographicCRS.WGS84;
        ReferencedEnvelope bbox = new ReferencedEnvelope(new Envelope(145, 146, -43, -41), crs);
        request.setBbox(bbox);
        request.setHeight(768);
        request.setWidth(384);
        request.setSRS("urn:x-ogc:def:crs:EPSG:4326");
        request.setFormat("image/png");
        request.setTransparent(true);

        final WMSMapContent map = new WMSMapContent(request);
        map.setMapHeight(768);
        map.setMapWidth(384);
        map.setBgColor(BG_COLOR);
        map.setTransparent(true);
        map.getViewport().setBounds(bbox);
        addRasterToMap(map, TAZ_BYTE);

        map.getViewport().setBounds(bbox);

        RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
        RenderedOp op = (RenderedOp) imageMap.getImage();
        BufferedImage image = op.getAsBufferedImage();
        imageMap.dispose();

        // check that a pixel in nodata area is transparent
        assertEquals(0, image.getRaster().getSample(40, 400, 0));
        assertEquals(0, image.getRaster().getSample(40, 400, 1));

    }

    /**
     * Sets up a rendering loop and throws {@code renderExceptionToThrow} wrapped to a
     * RuntimeException when the renderer tries to get a Feature to render.
     * <p>
     * If the rendering succeeded returns the image, which is going to be a blank one but means the
     * renderer didn't complain about the exception caught. Otherwise throws back the exception
     * thrown by {@link RenderedImageMapOutputFormat#produceMap()}
     * </p>
     */
    @SuppressWarnings("unchecked")
    private RenderedImage forceRenderingError(final Exception renderExceptionToThrow) throws Exception {

        GetMapRequest request = new GetMapRequest();
        final WMSMapContent map = new WMSMapContent();
        map.setMapWidth(100);
        map.setMapHeight(100);
        map.setRequest(request);
        final ReferencedEnvelope bounds = new ReferencedEnvelope(-180, 180, -90, 90, DefaultGeographicCRS.WGS84);
        map.getViewport().setBounds(bounds);

        final FeatureTypeInfo ftInfo = getCatalog().getFeatureTypeByName(STREAMS.getNamespaceURI(),
                STREAMS.getLocalPart());

        final SimpleFeatureSource featureSource = (SimpleFeatureSource) ftInfo.getFeatureSource(null, null);

        DecoratingFeatureSource source;
        // This source should make the renderer fail when asking for the features
        source = new DecoratingFeatureSource(featureSource) {
            @Override
            public SimpleFeatureCollection getFeatures(Query query) throws IOException {
                throw new RuntimeException(renderExceptionToThrow);
                // return delegate.getFeatures(query);
            }
        };

        StyleInfo someStyle = getCatalog().getStyleByName("line");
        map.addLayer(new FeatureLayer(source, someStyle.getStyle()));
        request.setFormat(getMapFormat());
        RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
        BufferedImage image = (BufferedImage) imageMap.getImage();
        imageMap.dispose();

        return image;
    }

    /**
     * Test to make sure the rendering does not skip on unmatched original envelope and tries anyways to
     * render an output
     */
    @Test
    public void testMosaicExpansion() throws Exception {
        File red1 = URLs.urlToFile(this.getClass().getResource("red_footprint_test/red1.tif"));
        File source = red1.getParentFile();
        File testDataDir = getResourceLoader().getBaseDirectory();
        File directory1 = new File(testDataDir, "redHarvest1");
        File directory2 = new File(testDataDir, "redHarvest2");
        if (directory1.exists()) {
            FileUtils.deleteDirectory(directory1);
        }
        FileUtils.copyDirectory(source, directory1);
        // move all files except red3 to the second dir
        directory2.mkdirs();
        for (File file : FileUtils.listFiles(directory1, new RegexFileFilter("red[^3].*"), null)) {
            assertTrue(file.renameTo(new File(directory2, file.getName())));
        }

        // create the first reader
        URL harvestSingleURL = URLs.fileToUrl(directory1);
        ImageMosaicReader reader = new ImageMosaicReader(directory1, null);

        // now create a second reader that won't be informed of the harvesting changes
        // (simulating changes over a cluster, where the bbox information won't be updated from one node to the other)
        ImageMosaicReader reader2 = new ImageMosaicReader(directory1, null);

        try {
            // harvest the other files with the first reader
            for (File file : directory2.listFiles()) {
                assertTrue(file.renameTo(new File(directory1, file.getName())));
            }
            reader.harvest(null, directory1, null);

            // now use the render to paint a map not hitting the original envelope of reader2
            ReferencedEnvelope renderEnvelope = new ReferencedEnvelope(991000, 992000, 216000, 217000,
                    reader2.getCoordinateReferenceSystem());
            Rectangle rasterArea = new Rectangle(0, 0, 10, 10);
            GetMapRequest request = new GetMapRequest();
            request.setBbox(renderEnvelope);
            request.setSRS("EPSG:6539");
            request.setFormat("image/png");

            final WMSMapContent map = new WMSMapContent(request);
            map.setMapWidth(10);
            map.setMapHeight(10);
            map.setBgColor(Color.BLACK);
            map.setTransparent(false);
            map.getViewport().setBounds(renderEnvelope);

            StyleBuilder builder = new StyleBuilder();
            Style style = builder.createStyle(builder.createRasterSymbolizer());
            Layer l = new CachedGridReaderLayer(reader2, style);
            map.addLayer(l);

            RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
            File reference = new File("src/test/resources/org/geoserver/wms/map/red10.png");
            ImageAssert.assertEquals(reference, imageMap.getImage(), 0);

            // now again, but with a rendering transformation, different code path
            style.featureTypeStyles().get(0).setTransformation(new IdentityCoverageFunction());
            RenderedImageMap imageMap2 = this.rasterMapProducer.produceMap(map);
            ImageAssert.assertEquals(reference, imageMap2.getImage(), 0);
            imageMap.dispose();

        } finally {
            reader.dispose();
            reader2.dispose();
        }
    }

    @Test
    public void testGetMapUntiledBigSize() throws Exception {
        final int mapWidth = 8192;
        final int mapHeight = 8192;
        GetMapRequest request = new GetMapRequest();
        CoordinateReferenceSystem crs = DefaultGeographicCRS.WGS84;
        ReferencedEnvelope bbox = new ReferencedEnvelope(new Envelope(145, 146, -43, -41), crs);
        request.setBbox(bbox);
        request.setHeight(mapHeight);
        request.setWidth(mapWidth);
        request.setSRS("urn:x-ogc:def:crs:EPSG:4326");
        request.setFormat("image/png");
        request.setTransparent(true);

        final WMSMapContent map = new WMSMapContent(request);
        map.setMapHeight(mapHeight);
        map.setMapWidth(mapWidth);
        map.setBgColor(BG_COLOR);
        map.setTransparent(true);
        map.getViewport().setBounds(bbox);
        addRasterToMap(map, TAZ_BYTE);
        map.getViewport().setBounds(bbox);

        RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
        RenderedOp op = (RenderedOp) imageMap.getImage();
        Point[] tileIndices = op.getTileIndices(new Rectangle(0, 0, mapWidth, mapHeight));

        // Assert we are getting more than a single huge tile.
        assertTrue(tileIndices.length > 1);

        Raster tile = op.getTile(0, 0);
        assertNotNull(tile);

        // check that inner tiling has not be set to mapWidth * mapHeight
        assertTrue(tile.getWidth() < mapWidth);
        assertTrue(tile.getHeight() < mapHeight);

        ImageUtilities.disposePlanarImageChain(op);
        imageMap.dispose();

    }

    /**
     * This dummy producer adds no functionality to DefaultRasterMapOutputFormat, just implements a
     * void formatImageOutputStream to have a concrete class over which test that
     * DefaultRasterMapOutputFormat correctly generates the BufferedImage.
     * 
     * @author Gabriel Roldan
     * @version $Id: DefaultRasterMapOutputFormatTest.java 6797 2007-05-16 10:23:50Z aaime $
     */
    static class DummyRasterMapProducer extends RenderedImageMapOutputFormat {

        public DummyRasterMapProducer(WMS wms) {
            super("image/gif", new String[] { "image/gif" }, wms);
        }
    }
}