package org.vfny.geoserver.wms.responses.map;
import static org.geoserver.data.test.MockData.STREAMS;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Panel;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import junit.framework.Test;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.data.test.MockData;
import org.geoserver.security.decorators.DecoratingFeatureSource;
import org.geoserver.wms.WMS;
import org.geoserver.wms.WMSTestSupport;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.IllegalAttributeException;
import org.geotools.filter.IllegalFilterException;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureSourceMapLayer;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.styling.Style;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;
import org.vfny.geoserver.wms.WMSMapContext;
import org.vfny.geoserver.wms.WmsException;
import org.vfny.geoserver.wms.requests.GetMapRequest;
import org.vfny.geoserver.wms.responses.DefaultRasterMapProducer;
import org.vfny.geoserver.wms.responses.RenderExceptionStrategy;
import com.vividsolutions.jts.geom.Envelope;
public class DefaultRasterMapProducerTest extends WMSTestSupport {
/** DOCUMENT ME! */
private static final int SHOW_TIMEOUT = 200;
/** DOCUMENT ME! */
private static final boolean INTERACTIVE = false;
/** DOCUMENT ME! */
private static final Logger LOGGER = org.geotools.util.logging.Logging
.getLogger(DefaultRasterMapProducerTest.class.getPackage().getName());
/** DOCUMENT ME! */
private static final Color BG_COLOR = Color.white;
/** DOCUMENT ME! */
private DefaultRasterMapProducer rasterMapProducer;
/** DOCUMENT ME! */
private String mapFormat = "image/gif";
/**
* This is a READ ONLY TEST so we can use one time setup
*/
public static Test suite() {
return new OneTimeTestSetup(new DefaultRasterMapProducerTest());
}
/**
* DOCUMENT ME!
*
* @throws Exception
* DOCUMENT ME!
*/
public void setUpInternal() throws Exception {
super.setUpInternal();
this.rasterMapProducer = getProducerInstance();
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
protected DefaultRasterMapProducer getProducerInstance() {
return new DummyRasterMapProducer(getWMS());
}
/**
* DOCUMENT ME!
*
* @throws Exception
* DOCUMENT ME!
*/
public void tearDownInternal() throws Exception {
this.rasterMapProducer = null;
super.tearDownInternal();
}
public String getMapFormat()
{
return this.mapFormat;
}
/**
* DOCUMENT ME!
*
* @throws Exception
* DOCUMENT ME!
*/
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);
final WMSMapContext map = new WMSMapContext();
map.setAreaOfInterest(new ReferencedEnvelope(env, DefaultGeographicCRS.WGS84));
map.setMapWidth(300);
map.setMapHeight(300);
map.setBgColor(Color.red);
map.setTransparent(false);
map.setRequest(new GetMapRequest(getWMS()));
StyleInfo styleByName = catalog.getStyleByName("Default");
Style basicStyle = styleByName.getStyle();
map.addLayer(fs, basicStyle);
this.rasterMapProducer.setOutputFormat(getMapFormat());
this.rasterMapProducer.setMapContext(map);
this.rasterMapProducer.produceMap();
assertNotBlank("testSimpleGetMapQuery", this.rasterMapProducer);
}
/**
* DOCUMENT ME!
*
* @throws Exception
* DOCUMENT ME!
*/
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));
}
}
/**
* DOCUMENT ME!
*
* @throws IOException
* DOCUMENT ME!
* @throws IllegalFilterException
* DOCUMENT ME!
* @throws IOException
* DOCUMENT ME!
*/
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);
final WMSMapContext map = new WMSMapContext();
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(new GetMapRequest(getWMS()));
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.setAreaOfInterest(new ReferencedEnvelope(env, DefaultGeographicCRS.WGS84));
this.rasterMapProducer.setOutputFormat(getMapFormat());
this.rasterMapProducer.setMapContext(map);
this.rasterMapProducer.produceMap();
assertNotBlank("testBlueLake", this.rasterMapProducer);
}
private void addToMap(final WMSMapContext 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 FeatureSourceMapLayer(ftInfo.getFeatureSource(null, null), style));
}
/**
* Checks that the image generated by the map producer is not blank.
*
* @param testName
* @param producer
*/
protected void assertNotBlank(String testName, DefaultRasterMapProducer producer) {
BufferedImage image = (BufferedImage) producer.getImage();
assertNotBlank(testName, image, BG_COLOR);
showImage(testName, image);
}
/**
* DOCUMENT ME!
*
* @param frameName
* DOCUMENT ME!
* @param image
* DOCUMENT ME!
*/
protected void showImage(String frameName, final BufferedImage image) {
showImage(frameName, SHOW_TIMEOUT, image);
}
/**
* Shows <code>image</code> in a Frame.
*
* @param frameName
* @param timeOut
* @param image
*/
protected void showImage(String frameName, long timeOut, final BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
if (((System.getProperty("java.awt.headless") == null) || !System.getProperty(
"java.awt.headless").equals("true"))
&& INTERACTIVE) {
Frame frame = new Frame(frameName);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
e.getWindow().dispose();
}
});
Panel p = new Panel(null) { // no layout manager so it respects
// setSize
public void paint(Graphics g) {
g.drawImage(image, 0, 0, this);
}
};
frame.add(p);
p.setSize(width, height);
frame.pack();
frame.setVisible(true);
try {
Thread.sleep(timeOut);
} catch (InterruptedException e) {
e.printStackTrace();
}
frame.dispose();
}
}
/**
* DOCUMENT ME!
*
* @param fSource
* DOCUMENT ME!
*
* @throws Exception
* DOCUMENT ME!
*/
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);
WMSMapContext map = new WMSMapContext();
map.setRequest(new GetMapRequest(getWMS()));
map.addLayer(fSource, style);
map.setAreaOfInterest(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();
RenderedImage image = this.rasterMapProducer.getImage();
String typeName = fSource.getSchema().getName().getLocalPart();
assertNotBlank("testDefaultStyle " + typeName, this.rasterMapProducer);
}
/**
* Checks {@link DefaultRasterMapProducer} makes good use of {@link RenderExceptionStrategy}
*/
@SuppressWarnings("deprecation")
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(WmsException e){
assertTrue(true);
}
try{
forceRenderingError(new IOException("fake IO exception"));
fail("Expected WMSException");
}catch(WmsException e){
assertTrue(true);
}
try{
forceRenderingError(new IllegalArgumentException("fake IAE exception"));
fail("Expected WMSException");
}catch(WmsException e){
assertTrue(true);
}
}
/**
* 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 DefaultRasterMapProducer#produceMap()}
* </p>
*/
@SuppressWarnings("unchecked")
private RenderedImage forceRenderingError(final Exception renderExceptionToThrow) throws Exception {
final WMSMapContext map = new WMSMapContext();
map.setMapWidth(100);
map.setMapHeight(100);
map.setRequest(new GetMapRequest(getWMS()));
final ReferencedEnvelope bounds = new ReferencedEnvelope(-180, 180, -90, 90, DefaultGeographicCRS.WGS84);
map.setAreaOfInterest(bounds);
final FeatureTypeInfo ftInfo = getCatalog().getFeatureTypeByName(STREAMS.getNamespaceURI(), STREAMS.getLocalPart());
final FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = (FeatureSource<SimpleFeatureType, SimpleFeature>) ftInfo.getFeatureSource(null, null);
DecoratingFeatureSource<SimpleFeatureType, SimpleFeature> source;
// This source should make the renderer fail when asking for the features
source = new DecoratingFeatureSource<SimpleFeatureType, SimpleFeature>(featureSource) {
@Override
public FeatureCollection<SimpleFeatureType, SimpleFeature> getFeatures(Query query)
throws IOException {
throw new RuntimeException(renderExceptionToThrow);
// return delegate.getFeatures(query);
}
};
StyleInfo someStyle = getCatalog().getStyles().get(0);
map.addLayer(source, someStyle.getStyle());
this.rasterMapProducer.setOutputFormat(getMapFormat());
this.rasterMapProducer.setMapContext(map);
this.rasterMapProducer.produceMap();
return this.rasterMapProducer.getImage();
}
/**
* This dummy producer adds no functionality to DefaultRasterMapProducer,
* just implements a void formatImageOutputStream to have a concrete class
* over which test that DefaultRasterMapProducer correctly generates the
* BufferedImage.
*
* @author Gabriel Roldan, Axios Engineering
* @version $Id: DefaultRasterMapProducerTest.java 6797 2007-05-16 10:23:50Z
* aaime $
*/
private static class DummyRasterMapProducer extends DefaultRasterMapProducer {
public DummyRasterMapProducer(WMS wms) {
super("image/gif", new String[] { "image/gif" }, wms);
}
public void formatImageOutputStream(RenderedImage image, OutputStream outStream)
throws WmsException, IOException {
/*
* Intentionally left blank, since this class is used just to ensure
* the abstract raster producer correctly generates a BufferedImage.
*/
}
public String getContentDisposition() {
// can be null
return null;
}
}
}