/* (c) 2014 - 2016 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.legendgraphic;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.util.HashMap;
import javax.imageio.ImageIO;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.GeoServerResourceLoader;
import org.geoserver.platform.ServiceException;
import org.geoserver.platform.resource.Paths;
import org.geoserver.platform.resource.Resource;
import org.geoserver.platform.resource.Resource.Type;
import org.geoserver.wms.GetLegendGraphicRequest;
import org.geoserver.wms.legendgraphic.Cell.ColorMapEntryLegendBuilder;
import org.geoserver.wms.legendgraphic.ColorMapLegendCreator.Builder;
import org.geoserver.wms.map.ImageUtils;
import org.geotools.styling.ChannelSelection;
import org.geotools.styling.ColorMap;
import org.geotools.styling.ColorMapEntry;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.RasterSymbolizer;
import org.geotools.styling.Rule;
import org.geotools.styling.Style;
import org.geotools.styling.Symbolizer;
/**
* Helper class to create legends for raster styles by parsing the rastersymbolizer element.
*
* @author Simone Giannecchini, GeoSolutions SAS
*/
@SuppressWarnings("deprecation")
public class RasterLayerLegendHelper {
/**
* The default legend is a simple image with an R within it which stands for Raster.
*/
private final static BufferedImage defaultLegend;
static {
BufferedImage imgShape = null;
try {
GeoServerResourceLoader loader = GeoServerExtensions.bean(GeoServerResourceLoader.class);
Resource rasterLegend = loader.get( Paths.path("styles","rasterLegend.png"));
if (rasterLegend.getType() == Type.RESOURCE ){
imgShape = ImageIO.read(rasterLegend.file());
}
} catch (Throwable e) {
imgShape = null;
}
// set the default legend
defaultLegend = imgShape;
}
private BufferedImage image;
private RasterSymbolizer rasterSymbolizer;
private int width;
private int height;
private boolean transparent;
private Color bgColor;
private ColorMapLegendCreator cMapLegendCreator;
/**
* Constructor for a RasterLayerLegendHelper.
*
* <p>
* It takes a {@link GetLegendGraphicRequest} object in order to do its magic.
*
* @param request
* the {@link GetLegendGraphicRequest} for which we want to build a legend
* @param style the {@link Style} for which we want to build a legend
* @param rule the {@link Rule} to use for rendering
*/
public RasterLayerLegendHelper(final GetLegendGraphicRequest request,Style style,String ruleName) {
PackagedUtils.ensureNotNull(request, "The provided GetLegendGraphicRequest is null");
parseRequest(request,style,ruleName);
}
@SuppressWarnings("unchecked")
private void parseRequest(final GetLegendGraphicRequest request,Style gt2Style,String ruleName) {
// get the requested layer
// and check that it is actually a grid
// final FeatureType layer = request.getLayer();
// boolean found =false;
// found = LegendUtils.checkGridLayer(layer);
// if(!found)
// throw new IllegalArgumentException("Unable to create legend for non raster style");
final FeatureTypeStyle[] ftStyles = gt2Style.getFeatureTypeStyles();
final double scaleDenominator = request.getScale();
final Rule[] applicableRules;
if (ruleName != null) {
Rule rule = LegendUtils.getRule(ftStyles, ruleName);
if (rule == null) {
throw new ServiceException("Specified style does not contains a rule named " + ruleName);
}
applicableRules = new Rule[] {rule};
} else {
applicableRules = LegendUtils.getApplicableRules(ftStyles, scaleDenominator);
}
// no rules means no legend has to be produced
if (applicableRules.length != 0) {
// final NumberRange<Double> scaleRange = NumberRange.create(scaleDenominator,
// scaleDenominator);
// get width and height
width = request.getWidth();
height = request.getHeight();
if (width <= 0 || height <= 0)
throw new IllegalArgumentException(
"Invalid width and or height for the GetLegendGraphicRequest");
final Symbolizer[] symbolizers = applicableRules[0].getSymbolizers();
if (symbolizers == null || symbolizers.length != 1 | symbolizers[0] == null)
throw new IllegalArgumentException(
"Unable to create a legend for this style, we need exactly 1 Symbolizer");
final Symbolizer symbolizer = symbolizers[0];
if (!(symbolizer instanceof RasterSymbolizer))
throw new IllegalArgumentException(
"Unable to create a legend for this style, we need a RasterSymbolizer");
rasterSymbolizer = (RasterSymbolizer) symbolizer;
// is background transparent?
transparent = request.isTransparent();
// background bkgColor
bgColor = LegendUtils.getBackgroundColor(request);
// colormap element
final ColorMap cmap = rasterSymbolizer.getColorMap();
final Builder cmapLegendBuilder = new ColorMapLegendCreator.Builder();
if (cmap != null && cmap.getColorMapEntries() != null
&& cmap.getColorMapEntries().length > 0) {
// passing additional options
cmapLegendBuilder.setAdditionalOptions(request.getLegendOptions());
// setting type of colormap
cmapLegendBuilder.setColorMapType(cmap.getType());
// is this colormap using extended colors
cmapLegendBuilder.setExtended(cmap.getExtendedColors());
// setting the requested colormap entries
cmapLegendBuilder.setRequestedDimension(new Dimension(width, height));
// setting transparency and background bkgColor
cmapLegendBuilder.setTransparent(transparent);
cmapLegendBuilder.setBackgroundColor(bgColor);
// setting band
// Setting label font and font bkgColor
cmapLegendBuilder.setLabelFont(LegendUtils.getLabelFont(request));
cmapLegendBuilder.setLabelFontColor(LegendUtils.getLabelFontColor(request));
// Setting layout parameters
cmapLegendBuilder.setLayout(LegendUtils.getLayout(request));
cmapLegendBuilder.setColumnHeight(LegendUtils.getColumnHeight(request));
cmapLegendBuilder.setRowWidth(LegendUtils.getRowWidth(request));
cmapLegendBuilder.setColumns(LegendUtils.getColumns(request));
cmapLegendBuilder.setRows(LegendUtils.getRows(request));
cmapLegendBuilder.setLabelFontColor(LegendUtils.getLabelFontColor(request));
// set band
final ChannelSelection channelSelection = rasterSymbolizer.getChannelSelection();
cmapLegendBuilder.setBand(channelSelection != null ? channelSelection.getGrayChannel()
: null);
// check the additional options before proceeding
cmapLegendBuilder.checkAdditionalOptions();
// adding the colormap entries
final ColorMapEntry[] colorMapEntries = cmap.getColorMapEntries();
ColorMapEntryLegendBuilder lastEntry = null;
boolean first = true;
for (ColorMapEntry ce : colorMapEntries) {
if(ce == null) {
continue;
}
final Double qty = ce.getQuantity().evaluate(null, Double.class);
if(cmap.getType() == ColorMap.TYPE_INTERVALS && first && qty < 0 && Double.isInfinite(qty)) {
continue;
}
lastEntry = cmapLegendBuilder.addColorMapEntry(ce);
first = false;
}
if(lastEntry != null) {
lastEntry.setLastRow();
}
// instantiate the creator
cMapLegendCreator = cmapLegendBuilder.create();
} else
cMapLegendCreator = null;
}
}
/**
* Retrieves the legend for the provided request.
*
* @return a {@link BufferedImage} that represents the legend for the provided request.
*/
public BufferedImage getLegend() {
if(rasterSymbolizer == null) {
return null;
}
return createResponse();
}
private synchronized BufferedImage createResponse() {
if (image == null) {
if (cMapLegendCreator != null)
// creating a legend
image = cMapLegendCreator.getLegend();
else {
image = ImageUtils.createImage(width, height, (IndexColorModel) null, transparent);
final Graphics2D graphics = ImageUtils.prepareTransparency(transparent, bgColor,
image, new HashMap<RenderingHints.Key, Object>());
if (defaultLegend == null) {
drawRasterIcon(graphics);
} else {
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
graphics.drawImage(defaultLegend, 0, 0, width, height, null);
}
graphics.dispose();
}
}
return image;
}
/**
* Builds a geosilk like raster icon
*/
void drawRasterIcon(Graphics2D graphics) {
Color blue = new Color(Integer.parseInt("5e72be", 16));
Color cyan = new Color(Integer.parseInt("a2c0eb", 16));
// color bg in blue
graphics.setColor(blue);
graphics.fillRect(0, 0, width, height);
// create checkerboard with cyan
graphics.setColor(cyan);
for(int j = 0; j < height / 2; j++) {
for(int i = (j % 2); i < width / 2; i+=2) {
graphics.fillRect(i * 2, j * 2, 2, 2);
}
}
// overlay a shade of blue that becomes more solid on the lower right corner
GradientPaint paint = new GradientPaint(new Point(0,0), new Color(0, 0, 255, 0),
new Point(width, height), new Color(0, 0, 255, 100));
graphics.setPaint(paint);
graphics.fillRect(0, 0, width, height);
// blue border
graphics.setColor(Color.BLUE);
graphics.drawRect(0, 0, width - 1, height -1 );
}
protected ColorMapLegendCreator getcMapLegendCreator() {
return cMapLegendCreator;
}
}