/* (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.Font; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.RenderingHints.Key; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import java.awt.image.RenderedImage; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.logging.Logger; import org.apache.commons.collections.map.CaseInsensitiveMap; import org.geoserver.wms.legendgraphic.Cell.ClassesEntryLegendBuilder; import org.geoserver.wms.legendgraphic.Cell.ColorMapEntryLegendBuilder; import org.geoserver.wms.legendgraphic.Cell.RampColorMapEntryLegendBuilder; import org.geoserver.wms.legendgraphic.Cell.SingleColorMapEntryLegendBuilder; import org.geoserver.wms.legendgraphic.LegendUtils.HAlign; import org.geoserver.wms.legendgraphic.LegendUtils.LegendLayout; import org.geoserver.wms.legendgraphic.LegendUtils.VAlign; import org.geoserver.wms.map.ImageUtils; import org.geotools.styling.ColorMap; import org.geotools.styling.ColorMapEntry; import org.geotools.styling.SelectedChannelType; /** * This class is responsible for building a legend out of a {@link ColorMap} SLD 1.0 element. * * <p> * Notice that the {@link ColorMapLegendCreator} is immutable. * * @author Simone Giannecchini, GeoSolutions. */ @SuppressWarnings("deprecation") public class ColorMapLegendCreator { private static final Logger LOGGER = org.geotools.util.logging.Logging .getLogger(ColorMapLegendCreator.class); /** * Builder class for building a {@link ColorMapLegendCreator}. * * <p> * The builder is not threa-safe. * * <p> * The correct way to use it is as follows: * * <code> * // 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)); //set band final ChannelSelection channelSelection = rasterSymbolizer.getChannelSelection(); cmapLegendBuilder.setBand(channelSelection!=null?channelSelection.getGrayChannel():null); // adding the colormap entries final ColorMapEntry[] colorMapEntries = cmap.getColorMapEntries(); for (ColorMapEntry ce : colorMapEntries) if (ce != null) cmapLegendBuilder.addColorMapEntry(ce); cMapLegendCreator=cmapLegendBuilder.create(); } * </code> * * @author Simone Giannecchini, GeoSolutions SAS * */ public static class Builder { private final Queue<ColorMapEntryLegendBuilder> bodyRows = new LinkedList<ColorMapEntryLegendBuilder>(); private ColorMapType colorMapType; private ColorMapEntry previousCMapEntry; private final CaseInsensitiveMap additionalOptions = new CaseInsensitiveMap(); private Color backgroundColor; private LegendLayout layout; private int columnHeight; private int rowWidth; private int columns; private int rows ; private String grayChannelName = LegendUtils.DEFAULT_CHANNEL_NAME; private boolean extended = false; private Color borderColor = LegendUtils.DEFAULT_BORDER_COLOR; private boolean fontAntiAliasing = true; private HAlign hAlign = HAlign.LEFT; private Font labelFont; private Color labelFontColor; private Dimension requestedDimension; private boolean transparent; private VAlign vAlign = VAlign.BOTTOM; private boolean forceRule = false; private double rowMarginPercentage = LegendUtils.rowPaddingFactor; private double vMarginPercentage = LegendUtils.marginFactor; private double columnMarginPercentage = LegendUtils.columnPaddingFactor; private double hMarginPercentage = LegendUtils.marginFactor; private boolean absoluteMargins = true; private boolean border = false; private boolean borderLabel = false; private boolean borderRule = false; private boolean bandInformation = false; private String unit = ""; private int digits; private boolean alternativeColorMapEntryBuilder = false; /** * Adds a {@link ColorMapEntry} element to this builder so that it can take it into account for building the legend. * * @param cEntry a {@link ColorMapEntry} element for this builder so that it can take it into account for building the legend. It must be not * <code>null</code>. */ public ColorMapEntryLegendBuilder addColorMapEntry(final ColorMapEntry cEntry) { PackagedUtils.ensureNotNull(cEntry, "cEntry"); // build a ColorMapEntryLegendBuilder for the specified colorMapEntry final ColorMapEntryLegendBuilder element; // // //NOTE for using the unit and digits values, the field "alternativeColorMapEntryBuilder" must be set to //TRUE with the method "setalternativeColorMapEntryBuilder()" // switch (colorMapType) { case UNIQUE_VALUES: element = new SingleColorMapEntryLegendBuilder( Arrays.asList(cEntry), hAlign, vAlign, backgroundColor, 1.0, grayChannelName, requestedDimension, labelFont, labelFontColor, extended, borderColor, unit, digits, alternativeColorMapEntryBuilder); break; case RAMP: element = new RampColorMapEntryLegendBuilder( Arrays.asList(previousCMapEntry, cEntry), hAlign, vAlign, backgroundColor, 1.0, grayChannelName, requestedDimension, labelFont, labelFontColor, extended, borderColor, unit, digits, alternativeColorMapEntryBuilder); break; case CLASSES: element = new ClassesEntryLegendBuilder(Arrays.asList( previousCMapEntry, cEntry), hAlign, vAlign, backgroundColor, 1.0, grayChannelName, requestedDimension, labelFont, labelFontColor, extended, borderColor, unit, digits, alternativeColorMapEntryBuilder); break; default: throw new IllegalArgumentException("Unrecognized colormap type"); } // add to the table, we can use matrix algebra knowing that W==3 for this matrix bodyRows.add(element); // set last used element previousCMapEntry = cEntry; return element; } /** * @param legendOptions * @uml.property name="additionalOptions" */ public void setAdditionalOptions(final Map<String, Object> legendOptions) { this.additionalOptions.putAll(legendOptions); } /** * @param backGroundColor * @uml.property name="backgroundColor" */ public void setBackgroundColor(final Color backGroundColor) { PackagedUtils.ensureNotNull(backGroundColor, "backGroundColor"); this.backgroundColor = backGroundColor; } public void setBand(final SelectedChannelType grayChannel) { if (grayChannel != null) this.grayChannelName = grayChannel.getChannelName(); if (grayChannelName == null) this.grayChannelName = LegendUtils.DEFAULT_CHANNEL_NAME; } /** * Sets the {@link ColorMapType} for this legend builder in order to instruct it on how to build the legend. * * @param colorMapType the {@link ColorMapType} for this legend builder in order to instruct it on how to build the legend. */ public void setColorMapType(final ColorMapType colorMapType) { this.colorMapType = colorMapType; } /** * Sets the {@link ColorMapType} for this legend builder in order to instruct it on how to build the legend. * * @param colorMapType a int representing a {@link ColorMapType} for this legend builder in order to instruct it on how to build the legend. */ public void setColorMapType(final int type) { this.colorMapType = ColorMapType.create(type); } /** * @param extended * @uml.property name="extended" */ public void setExtended(final boolean extended) { this.extended = extended; } /** * @param labelFont * @uml.property name="labelFont" */ public void setLabelFont(final Font labelFont) { PackagedUtils.ensureNotNull(labelFont, "labelFont"); this.labelFont = labelFont; } /** * @param labelFontColor * @uml.property name="labelFontColor" */ public void setLabelFontColor(final Color labelFontColor) { PackagedUtils.ensureNotNull(labelFontColor, "labelFontColor"); this.labelFontColor = labelFontColor; } /** * @param dimension * @uml.property name="requestedDimension" */ public void setRequestedDimension(final Dimension dimension) { this.requestedDimension = (Dimension) dimension.clone(); } /** * @param transparent * @uml.property name="transparent" */ public void setTransparent(final boolean transparent) { this.transparent = transparent; } public void setBorderLabel(boolean borderLabel) { this.borderLabel = borderLabel; } public void setBorderRule(boolean borderRule) { this.borderRule = borderRule; } public void setLayout(LegendLayout layout) { this.layout = layout; } public void setColumnHeight(int columnHeight) { this.columnHeight = columnHeight; } public void setRowWidth(int rowWidth) { this.rowWidth = rowWidth; } public void setColumns(int columns) { this.columns = columns; } public void setRows(int rows) { this.rows = rows; } /** * Creates a {@link ColorMapLegendCreator} using the provided elements. * * @return a {@link ColorMapLegendCreator}. */ public ColorMapLegendCreator create() { return new ColorMapLegendCreator(this); } public void checkAdditionalOptions() { fontAntiAliasing = false; if (additionalOptions.get("fontAntiAliasing") instanceof String) { String aaVal = (String) additionalOptions.get("fontAntiAliasing"); if (aaVal.equalsIgnoreCase("on") || aaVal.equalsIgnoreCase("true") || aaVal.equalsIgnoreCase("yes") || aaVal.equalsIgnoreCase("1")) { fontAntiAliasing = true; } } if (additionalOptions.get("dx") instanceof String) { columnMarginPercentage = Double.parseDouble((String) additionalOptions.get("dx")); } if (additionalOptions.get("absoluteMargins") instanceof String) { absoluteMargins = Boolean.parseBoolean((String) additionalOptions.get("absoluteMargins")); } if (additionalOptions.get("bandInfo") instanceof String) { bandInformation = Boolean.parseBoolean((String) additionalOptions.get("bandInfo")); } if (additionalOptions.get("dy") instanceof String) { rowMarginPercentage = Double.parseDouble((String) additionalOptions.get("dy")); } if (additionalOptions.get("mx") instanceof String) { hMarginPercentage = Double.parseDouble((String) additionalOptions.get("mx")); } if (additionalOptions.get("my") instanceof String) { vMarginPercentage = Double.parseDouble((String) additionalOptions.get("my")); } if (additionalOptions.get("borderColor") instanceof String) { borderColor = LegendUtils.color((String) additionalOptions.get("borderColor")); } if (additionalOptions.get("border") instanceof String) { border = Boolean.valueOf((String) additionalOptions.get("border")); } if (additionalOptions.get("forceRule") instanceof String) { forceRule = Boolean.parseBoolean((String) additionalOptions.get("forceRule")); } // if all the labels are null, we MUST draw the rules if (!forceRule) for (ColorMapEntryLegendBuilder row : bodyRows) { // // row number i // // label final Cell labelM = row.getLabelManager(); if (labelM == null) forceRule = true; else { forceRule = false; break; } } } public void setBandInformation(boolean bandInformation) { this.bandInformation = bandInformation; } public void setUnit(String unit) { this.unit = unit; } public void setDigits(int digits) { this.digits = digits; } public void setAlternativeColorMapEntryBuilder(boolean alternativeColorMapEntryBuilder){ this.alternativeColorMapEntryBuilder = alternativeColorMapEntryBuilder; } public Queue<ColorMapEntryLegendBuilder> getBodyRows() { return bodyRows; } } /** * * @author Simone Giannecchini, GeoSolutions SAS * */ enum ColorMapType { UNIQUE_VALUES, RAMP, CLASSES; public static ColorMapType create(final String value) { if (value.equalsIgnoreCase("intervals")) return CLASSES; else if (value.equalsIgnoreCase("ramp")) { return RAMP; } else if (value.equalsIgnoreCase("values")) { return UNIQUE_VALUES; } else return ColorMapType.valueOf(value); } public static ColorMapType create(final int value) { switch (value) { case ColorMap.TYPE_INTERVALS: return ColorMapType.CLASSES; case ColorMap.TYPE_RAMP: return ColorMapType.RAMP; case ColorMap.TYPE_VALUES: return ColorMapType.UNIQUE_VALUES; default: throw new IllegalArgumentException("Unable to create ColorMapType for value " + value); } } } private ColorMapType colorMapType; private boolean extended = false; private boolean transparent; private Dimension requestedDimension; private Color backgroundColor; private Font labelFont; private Color labelFontColor; private final Queue<ColorMapEntryLegendBuilder> bodyRows = new LinkedList<ColorMapEntryLegendBuilder>(); private final List<Cell> footerRows = new ArrayList<Cell>(); private HAlign hAlign = HAlign.LEFT; private VAlign vAlign = VAlign.BOTTOM; private double vMarginPercentage = LegendUtils.marginFactor; private double hMarginPercentage = LegendUtils.marginFactor; private double rowMarginPercentage = LegendUtils.rowPaddingFactor; private double columnMarginPercentage = LegendUtils.columnPaddingFactor; private boolean absoluteMargins = true; private Color borderColor = LegendUtils.DEFAULT_BORDER_COLOR; private boolean borderLabel = false; private boolean borderRule = false; private double margin; private double rowH; private double colorW; private double ruleW; private double labelW; private double footerW; private String grayChannelName = "1"; private boolean fontAntiAliasing = true; private boolean forceRule = false; private BufferedImage legend; private boolean border = false; private double dx; private double dy; private boolean bandInformation; private LegendLayout layout; private int columnHeight; private int rowWidth; private int columns; private int rows ; public ColorMapLegendCreator(final Builder builder) { this.backgroundColor = builder.backgroundColor; this.bodyRows.addAll(builder.bodyRows); this.border = builder.border; this.borderColor = builder.borderColor; this.borderLabel = builder.borderLabel; this.borderRule = builder.borderRule; this.colorMapType = builder.colorMapType; this.columnMarginPercentage = builder.columnMarginPercentage; this.extended = builder.extended; this.fontAntiAliasing = builder.fontAntiAliasing; this.forceRule = builder.forceRule; this.grayChannelName = builder.grayChannelName; this.hAlign = builder.hAlign; this.vAlign = builder.vAlign; this.labelFont = builder.labelFont; this.labelFontColor = builder.labelFontColor; this.rowMarginPercentage = builder.rowMarginPercentage; this.columnMarginPercentage = builder.columnMarginPercentage; this.absoluteMargins = builder.absoluteMargins; this.hMarginPercentage = builder.hMarginPercentage; this.vMarginPercentage = builder.vMarginPercentage; this.requestedDimension = (Dimension) builder.requestedDimension.clone(); this.transparent = builder.transparent; this.bandInformation = builder.bandInformation; this.layout = builder.layout; this.rowWidth = builder.rowWidth; this.rows = builder.rows; this.columnHeight = builder.columnHeight; this.columns = builder.columns; } public synchronized BufferedImage getLegend() { // do we laraedy have a legend if (legend == null) { // init all the values init(); // now build the individuals legends // // header // // XXX no header for the moment // // body // final Queue<BufferedImage> body = createBody(); // // footer // if (bandInformation) { final Queue<BufferedImage> footer = createFooter(); body.addAll(footer); } // now merge them legend = mergeRows(body); } return legend; } private void init() { // // create a sample image for computing dimensions of text strings // BufferedImage image = ImageUtils.createImage(1, 1, (IndexColorModel) null, transparent); final Map<Key, Object> hintsMap = new HashMap<Key, Object>(); Graphics2D graphics = ImageUtils.prepareTransparency(transparent, backgroundColor, image, hintsMap); // elements used to compute maximum dimensions for rows and cells rowH = 0; colorW = 0; ruleW = 0; labelW = 0; // // BODY // // cycle over all the body elements cycleBodyRows(graphics); // // FOOTER // // set footer strings if (bandInformation) { final String bandNameString = "Band selection is " + this.grayChannelName; footerRows.add(new Cell.TextManager(bandNameString, vAlign, hAlign, backgroundColor, requestedDimension, labelFont, labelFontColor, fontAntiAliasing, borderColor)); // set footer strings final String colorMapTypeString = "ColorMap type is " + this.colorMapType.toString(); footerRows.add(new Cell.TextManager(colorMapTypeString, vAlign, hAlign, backgroundColor, requestedDimension, labelFont, labelFontColor, fontAntiAliasing, borderColor)); // extended colors or not final String extendedCMapString = "ColorMap is " + (this.extended ? "" : "not") + " extended"; footerRows.add(new Cell.TextManager(extendedCMapString, vAlign, hAlign, backgroundColor, requestedDimension, labelFont, labelFontColor, fontAntiAliasing, borderColor)); cycleFooterRows(graphics); } // // compute dimensions // this. // final dimension are different between ramp and others since ramp does not have margin for rows final double maxW = Math.max(colorW + ruleW + labelW, footerW); dx = absoluteMargins ? columnMarginPercentage : (maxW * columnMarginPercentage); dy = colorMapType == ColorMapType.RAMP ? 0 : rowH * rowMarginPercentage; final double mx = maxW * hMarginPercentage; final double my = rowH * vMarginPercentage; margin = Math.max(mx, my); } private void cycleFooterRows(Graphics2D graphics) { int numRows = this.footerRows.size(), i = 0; footerW = Double.NEGATIVE_INFINITY; for (i = 0; i < numRows; i++) { // // row number i // // color element final Cell cell = this.footerRows.get(i); final Dimension cellDim = cell.getPreferredDimension(graphics); rowH = Math.max(rowH, cellDim.getHeight()); footerW = Math.max(footerW, cellDim.getWidth()); } } /** * @param graphics */ private void cycleBodyRows(Graphics2D graphics) { for (ColorMapEntryLegendBuilder row : bodyRows) { // // row number i // // color element final Cell cm = row.getColorManager(); final Dimension colorDim = cm.getPreferredDimension(graphics); rowH = Math.max(rowH, colorDim.getHeight()); colorW = Math.max(colorW, colorDim.getWidth()); // rule if (forceRule) { final Cell ruleM = row.getRuleManager(); final Dimension ruleDim = ruleM.getPreferredDimension(graphics); rowH = Math.max(rowH, ruleDim.getHeight()); ruleW = Math.max(ruleW, ruleDim.getWidth()); } // label final Cell labelM = row.getLabelManager(); if (labelM == null) continue; final Dimension labelDim = labelM.getPreferredDimension(graphics); rowH = Math.max(rowH, labelDim.getHeight()); labelW = Math.max(labelW, labelDim.getWidth()); } } private Queue<BufferedImage> createFooter() { // creating a backbuffer image on which we should draw the bkgColor for this colormap element final BufferedImage image = ImageUtils.createImage(1, 1, (IndexColorModel) null, transparent); final Map<Key, Object> hintsMap = new HashMap<Key, Object>(); final Graphics2D graphics = ImageUtils.prepareTransparency(transparent, backgroundColor, image, hintsMap); // list where we store the rows for the footer final Queue<BufferedImage> queue = new LinkedList<BufferedImage>(); // //the height is already fixed // final int rowHeight=(int)Math.round(rowH); final int rowWidth = (int) Math.round(footerW); // final Rectangle clipboxA=new Rectangle(0,0,rowWidth,rowHeight); // // footer // // // draw the various bodyCells for (Cell cell : footerRows) { // get dim final Dimension dim = cell.getPreferredDimension(graphics); // final int rowWidth=(int)Math.round(dim.getWidth()); final int rowHeight = (int) Math.round(dim.getHeight()); final Rectangle clipboxA = new Rectangle(0, 0, rowWidth, rowHeight); // draw it final BufferedImage colorCellLegend = new BufferedImage(rowWidth, rowHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D rlg = colorCellLegend.createGraphics(); rlg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); cell.draw(rlg, clipboxA, border); rlg.dispose(); queue.add(colorCellLegend); } graphics.dispose(); return queue;// mergeRows(queue); } private Queue<BufferedImage> createBody() { final Queue<BufferedImage> queue = new LinkedList<BufferedImage>(); // // draw the various elements // // create the boxes for drawing later final int rowHeight = (int) Math.round(rowH); final int colorWidth = (int) Math.round(colorW); final int ruleWidth = (int) Math.round(ruleW); final int labelWidth = (int) Math.round(labelW); final Rectangle clipboxA = new Rectangle(0, 0, colorWidth, rowHeight); final Rectangle clipboxB = new Rectangle(0, 0, ruleWidth, rowHeight); final Rectangle clipboxC = new Rectangle(0, 0, labelWidth, rowHeight); // // Body // // // draw the various bodyCells for (ColorMapEntryLegendBuilder row : bodyRows) { // // row number i // // get element for color default behavior final Cell colorCell = row.getColorManager(); // draw it final BufferedImage colorCellLegend = new BufferedImage(colorWidth, rowHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D rlg = colorCellLegend.createGraphics(); rlg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rlg.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); colorCell.draw(rlg, clipboxA, border); rlg.dispose(); BufferedImage ruleCellLegend = null; if (forceRule) { // get element for rule final Cell ruleCell = row.getRuleManager(); // draw it ruleCellLegend = new BufferedImage(ruleWidth, rowHeight, BufferedImage.TYPE_INT_ARGB); rlg = ruleCellLegend.createGraphics(); rlg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rlg.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); ruleCell.draw(rlg, clipboxB, borderRule); rlg.dispose(); } // draw it if it is present if (labelWidth > 0) { // get element for label final Cell labelCell = row.getLabelManager(); if (labelCell != null) { final BufferedImage labelCellLegend = new BufferedImage(labelWidth, rowHeight, BufferedImage.TYPE_INT_ARGB); rlg = labelCellLegend.createGraphics(); rlg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rlg.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); labelCell.draw(rlg, clipboxC, borderLabel); rlg.dispose(); // // merge the bodyCells for this row // // final Map<Key, Object> hintsMap = new HashMap<Key, Object>(); queue.add(LegendUtils.hMergeBufferedImages(colorCellLegend, ruleCellLegend, labelCellLegend, hintsMap, transparent, backgroundColor, dx)); } else { final Map<Key, Object> hintsMap = new HashMap<Key, Object>(); queue.add(LegendUtils.hMergeBufferedImages(colorCellLegend, ruleCellLegend, null, hintsMap, transparent, backgroundColor, dx)); } } else { // // merge the bodyCells for this row // // final Map<Key, Object> hintsMap = new HashMap<Key, Object>(); queue.add(LegendUtils.hMergeBufferedImages(colorCellLegend, ruleCellLegend, null, hintsMap, transparent, backgroundColor, dx)); } } // return the list of legends return queue;// mergeRows(queue); } private BufferedImage mergeRows(Queue<BufferedImage> legendsQueue) { // I am doing a straight cast since I know that I built this // dimension object by using the widths and heights of the various // bufferedimages for the various bkgColor map entries. final Dimension finalDimension = new Dimension(); final int numRows = legendsQueue.size(); finalDimension.setSize(Math.max(footerW, colorW + ruleW + labelW) + 2 * dx + 2 * margin, rowH * numRows + 2 * margin + (numRows - 1) * dy); final int totalWidth = (int) finalDimension.getWidth(); final int totalHeight = (int) finalDimension.getHeight(); BufferedImage finalLegend = ImageUtils.createImage(totalWidth, totalHeight, (IndexColorModel) null, transparent); /* * For RAMP type, only HORIZONTAL or VERTICAL condition is valid */ if (colorMapType == ColorMapType.RAMP) { final Map<Key, Object> hintsMap = new HashMap<Key, Object>(); Graphics2D finalGraphics = ImageUtils.prepareTransparency(transparent, backgroundColor, finalLegend, hintsMap); hintsMap.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); finalGraphics.setRenderingHints(hintsMap); int topOfRow = (int) (margin + 0.5); for (int i = 0; i < numRows; i++) { final BufferedImage img = legendsQueue.remove(); // draw the image finalGraphics.drawImage(img, (int) (margin + 0.5), topOfRow, null); topOfRow += img.getHeight() + dy; } if (this.layout == LegendLayout.HORIZONTAL) { BufferedImage newImage = new BufferedImage(totalHeight, totalWidth, finalLegend.getType()); Graphics2D g2 = newImage.createGraphics(); g2.rotate(-Math.PI / 2, 0, 0); g2.drawImage(finalLegend, null, -totalWidth, 0); finalLegend = newImage; g2.dispose(); finalGraphics.dispose(); } } else { List<RenderedImage> imgs = new ArrayList<RenderedImage>(legendsQueue); LegendMerger.MergeOptions options = new LegendMerger.MergeOptions(imgs, (int) dx, (int) dy, (int) margin, 0, backgroundColor, transparent, true, layout, rowWidth, rows, columnHeight, columns, null ,false, false); finalLegend = LegendMerger.mergeRasterLegends(options); } return finalLegend; } protected Queue<ColorMapEntryLegendBuilder> getBodyRows() { return bodyRows; } }