Commit b814c2e4 authored by Mauro Bartolomeoli's avatar Mauro Bartolomeoli
Browse files

GEOS-6965: added interpolations vendor parameter for WMS GetMap

parent 922dbe40
......@@ -253,3 +253,23 @@ The following code shows how to specify the meta-tiling parameters:
{buffer: 0}
);
interpolations
--------------
The ``interpolations`` parameter allows choosing a specific resampling (interpolation) method.
It can be used in the ``GetMap`` operation.
If more than one layer is specified in the ``layers`` parameter, then a separate interpolation method can be specified for each layer, separated by commas.
The syntax is::
interpolations=method1,method2,...
method<n> values can be one of the following:
* **nearest neighbor**
* **bilinear**
* **bicubic**
or empty if the default method has to be used for the related layer.
The parameter allows to override the global WMS Raster Rendering Options setting (see :ref:`WMS Settings<webadmin_wms>` for more info), on a layer by layer basis.
/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
/* (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.
......@@ -18,6 +18,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.media.jai.Interpolation;
import org.geoserver.catalog.SLDHandler;
import org.geoserver.ows.util.CaseInsensitiveMap;
import org.geotools.styling.Style;
......@@ -140,6 +142,15 @@ public class GetMapRequest extends WMSRequest implements Cloneable {
public List<Style> getStyles() {
return this.mandatoryParams.styles;
}
/**
* Gets a list of the interpolation methods to be returned by the server.
*
* @return A list of {@link Interpolation}
*/
public List<Interpolation> getInterpolations() {
return this.optionalParams.interpolationMethods;
}
/**
* Gets the url specified by the "SLD" parameter.
......@@ -410,6 +421,16 @@ public class GetMapRequest extends WMSRequest implements Cloneable {
: new ArrayList<Style>(styles);
}
/**
* Sets interpolations methods for layers.
*
* @param interpolations
*/
public void setInterpolations(List<Interpolation> interpolations) {
this.optionalParams.interpolationMethods = interpolations == null ? Collections.EMPTY_LIST
: interpolations;
}
/**
* Sets the url specified by the "SLD" parameter.
*/
......@@ -770,7 +791,10 @@ public class GetMapRequest extends WMSRequest implements Cloneable {
/** map rotation */
double angle;
/** by layer interpolation methods **/
List<Interpolation> interpolationMethods = Collections.EMPTY_LIST;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
......
/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
/* (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.
......@@ -21,6 +21,7 @@ import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.media.jai.Interpolation;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.collections.EnumerationUtils;
......@@ -67,7 +68,6 @@ import org.geotools.styling.StyleFactory;
import org.geotools.styling.StyledLayer;
import org.geotools.styling.StyledLayerDescriptor;
import org.geotools.styling.UserLayer;
import org.geotools.util.Version;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.FeatureType;
......@@ -87,6 +87,15 @@ public class GetMapKvpRequestReader extends KvpRequestReader implements HttpServ
*/
private HttpServletRequest httpRequest;
private static Map<String, Integer> interpolationMethods;
static {
interpolationMethods = new HashMap<String, Integer>();
interpolationMethods.put("NEAREST NEIGHBOR", Interpolation.INTERP_NEAREST);
interpolationMethods.put("BILINEAR", Interpolation.INTERP_BILINEAR);
interpolationMethods.put("BICUBIC", Interpolation.INTERP_BICUBIC);
}
/**
* style factory
*/
......@@ -243,6 +252,17 @@ public class GetMapKvpRequestReader extends KvpRequestReader implements HttpServ
styleNameList.addAll(KvpUtils.readFlat(stylesParam));
}
// raw interpolations parameter
String interpolationParam = (String) kvp.get("INTERPOLATIONS");
List<String> interpolationList = new ArrayList<String>();
if (interpolationParam != null) {
interpolationList.addAll(KvpUtils.readFlat(interpolationParam));
}
if(interpolationList.size() > 0) {
getMap.setInterpolations(parseInterpolations(interpolationList));
}
// pre parse filters
List<Filter> filters = parseFilters(getMap);
......@@ -486,6 +506,23 @@ public class GetMapKvpRequestReader extends KvpRequestReader implements HttpServ
return getMap;
}
private List<Interpolation> parseInterpolations(List<String> interpolationList) {
List<Interpolation> interpolations = new ArrayList<Interpolation>();
for (String interpolation : interpolationList) {
if ("".equals(interpolation)) {
// return null, this should flag request reader to use default for
// the associated layer
interpolations.add(null);
} else {
interpolations.add(Interpolation.getInstance(interpolationMethods.get(interpolation
.toUpperCase())));
}
}
return interpolations;
}
private Style getDefaultStyle (LayerInfo layer) throws IOException{
if (layer.getResource() instanceof WMSLayerInfo) {
// NamedStyle is a subclass of Style -> we use it as a way to convey
......
/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
/* (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.
......@@ -333,7 +333,11 @@ public class RenderedImageMapOutputFormat extends AbstractMapOutputFormat {
&& (layout == null || layout.isEmpty())) {
List<GridCoverage2D> renderedCoverages = new ArrayList<GridCoverage2D>(2);
try {
image = directRasterRender(mapContent, 0, renderedCoverages);
Interpolation interpolation = null;
if(request.getInterpolations() != null && request.getInterpolations().size() > 0) {
interpolation = request.getInterpolations().get(0);
}
image = directRasterRender(mapContent, 0, renderedCoverages, interpolation);
} catch (Exception e) {
throw new ServiceException("Error rendering coverage on the fast path", e);
}
......@@ -396,6 +400,8 @@ public class RenderedImageMapOutputFormat extends AbstractMapOutputFormat {
}
}
// make sure the hints are set before we start rendering the map
graphic.setRenderingHints(hintsMap);
......@@ -464,6 +470,21 @@ public class RenderedImageMapOutputFormat extends AbstractMapOutputFormat {
}
}
}
if(request.getInterpolations() != null && !request.getInterpolations().isEmpty()) {
int count = 0;
List<Interpolation> interpolations = request.getInterpolations();
for(Layer layer : mapContent.layers()) {
if(count < interpolations.size()) {
Interpolation interpolation = interpolations.get(count);
if(interpolation != null) {
layer.getUserData().put(StreamingRenderer.BYLAYER_INTERPOLATION, interpolation);
}
}
count++;
}
}
renderer.setRendererHints(rendererParams);
// if abort already requested bail out
......@@ -755,7 +776,7 @@ public class RenderedImageMapOutputFormat extends AbstractMapOutputFormat {
* @throws FactoryException
*/
private RenderedImage directRasterRender(WMSMapContent mapContent, int layerIndex,
List<GridCoverage2D> renderedCoverages) throws IOException, FactoryException {
List<GridCoverage2D> renderedCoverages, Interpolation layerInterpolation) throws IOException, FactoryException {
//
// extract the raster symbolizers and the eventual rendering transformation
......@@ -804,18 +825,22 @@ public class RenderedImageMapOutputFormat extends AbstractMapOutputFormat {
// Grab the interpolation
//
final Interpolation interpolation;
if (wms != null) {
if (WMSInterpolation.Nearest.equals(wms.getInterpolation())) {
interpolation = Interpolation.getInstance(Interpolation.INTERP_NEAREST);
} else if (WMSInterpolation.Bilinear.equals(wms.getInterpolation())) {
interpolation = Interpolation.getInstance(Interpolation.INTERP_BILINEAR);
} else if (WMSInterpolation.Bicubic.equals(wms.getInterpolation())) {
interpolation = Interpolation.getInstance(Interpolation.INTERP_BICUBIC);
if(layerInterpolation != null) {
interpolation = layerInterpolation;
} else {
if (wms != null) {
if (WMSInterpolation.Nearest.equals(wms.getInterpolation())) {
interpolation = Interpolation.getInstance(Interpolation.INTERP_NEAREST);
} else if (WMSInterpolation.Bilinear.equals(wms.getInterpolation())) {
interpolation = Interpolation.getInstance(Interpolation.INTERP_BILINEAR);
} else if (WMSInterpolation.Bicubic.equals(wms.getInterpolation())) {
interpolation = Interpolation.getInstance(Interpolation.INTERP_BICUBIC);
} else {
interpolation = Interpolation.getInstance(Interpolation.INTERP_NEAREST);
}
} else {
interpolation = Interpolation.getInstance(Interpolation.INTERP_NEAREST);
}
} else {
interpolation = Interpolation.getInstance(Interpolation.INTERP_NEAREST);
}
//
......
/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
/* (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.
......@@ -17,6 +17,9 @@ import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import javax.media.jai.InterpolationBicubic;
import javax.media.jai.InterpolationBilinear;
import junit.framework.Test;
import org.geoserver.catalog.CatalogBuilder;
......@@ -219,6 +222,32 @@ public class GetMapKvpRequestReaderTest extends KvpRequestReaderTestSupport {
assertEquals(buildings.getDefaultStyle().getStyle(), request.getStyles().get(1));
}
public void testInterpolations() throws Exception {
HashMap kvp = new HashMap();
kvp.put("layers", getLayerId(MockData.BASIC_POLYGONS));
kvp.put("interpolations", "bicubic");
GetMapRequest request = (GetMapRequest) reader.createRequest();
request = (GetMapRequest) reader.read(request, parseKvp(kvp), caseInsensitiveKvp(kvp));
assertNotNull(request.getInterpolations());
assertEquals(1, request.getInterpolations().size());
assertNotNull(request.getInterpolations().get(0));
assertTrue(request.getInterpolations().get(0) instanceof InterpolationBicubic);
kvp.put("layers", getLayerId(MockData.BASIC_POLYGONS)+","+getLayerId(MockData.BASIC_POLYGONS)+","+getLayerId(MockData.BASIC_POLYGONS));
kvp.put("interpolations", "bicubic,,bilinear");
request = (GetMapRequest) reader.createRequest();
request = (GetMapRequest) reader.read(request, parseKvp(kvp), caseInsensitiveKvp(kvp));
assertNotNull(request.getInterpolations());
assertEquals(3, request.getInterpolations().size());
assertNotNull(request.getInterpolations().get(0));
assertNull(request.getInterpolations().get(1));
assertNotNull(request.getInterpolations().get(2));
assertTrue(request.getInterpolations().get(2) instanceof InterpolationBilinear);
}
public void testFilter() throws Exception {
HashMap kvp = new HashMap();
kvp.put("layers", getLayerId(MockData.BASIC_POLYGONS));
......
/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
/* (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.
......@@ -6,6 +6,7 @@
package org.geoserver.wms.map;
import static org.geoserver.data.test.SystemTestData.STREAMS;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
......@@ -15,37 +16,48 @@ import java.awt.geom.NoninvertibleTransformException;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
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.geoserver.catalog.Catalog;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.LayerInfo;
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.GetMapRequest;
import org.geoserver.wms.WMS;
import org.geoserver.wms.WMSMapContent;
import org.geoserver.wms.WMSTestSupport;
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.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.resources.coverage.FeatureUtilities;
import org.geotools.resources.image.ImageUtilities;
import org.geotools.styling.Style;
import org.geotools.util.logging.Logging;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;
......@@ -161,7 +173,108 @@ public class RenderedImageMapOutputFormatTest extends WMSTestSupport {
imageMap.dispose();
assertNotBlank("testBlueLake", image);
}
@Override
protected void onSetUp(SystemTestData testData) throws Exception {
super.onSetUp(testData);
testData.addDefaultRasterLayer(MockData.TASMANIA_DEM, getCatalog());
}
@Test
public void testInterpolationBicubic() 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();
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);
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());
RenderedImageMap imageMap = this.rasterMapProducer.produceMap(map);
BufferedImage image = (BufferedImage)imageMap.getImage();
imageMap.dispose();
assertNotBlank("testInterpolation", image);
// bicubic creates shades of gray instead of white
assertTrue(getPixelColor(image, 200, 200).getRGB() < Color.DARK_GRAY.getRGB());
assertPixelIsTransparent(image, 50 , 650);
assertTrue(getPixelColor(image, 200, 200).getRGB() < Color.WHITE.getRGB());
}
@Test
public void testInterpolationNearest() 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();
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);
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);
BufferedImage image = (BufferedImage)imageMap.getImage();
imageMap.dispose();
assertNotBlank("testInterpolation", image);
// nearest neighbor creates "almost black" / white images
assertTrue(getPixelColor(image, 200, 200).getRGB() < Color.DARK_GRAY.getRGB());
assertPixelIsTransparent(image, 50 , 650);
assertPixel(image, 100, 350, Color.WHITE);
}
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());
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment