// ... base image memory
long memory = getDrawingSurfaceMemoryUse(paintArea.width, paintArea.height, palette,
transparent);
// .. use a fake streaming renderer to evaluate the extra back buffers used when rendering
// multiple featureTypeStyles against the same layer
StreamingRenderer testRenderer = new StreamingRenderer();
testRenderer.setMapContent(mapContent);
memory += testRenderer.getMaxBackBufferMemory(paintArea.width, paintArea.height);
if (maxMemory > 0 && memory > maxMemory) {
long kbUsed = memory / KB;
long kbMax = maxMemory / KB;
throw new ServiceException("Rendering request would use " + kbUsed + "KB, whilst the "
+ "maximum memory allowed is " + kbMax + "KB");
}
final MapDecorationLayout layout = findDecorationLayout(request, tiled);
// TODO: allow rendering to continue with vector layers
// TODO: allow rendering to continue with layout
// TODO: handle rotated rasters
// TODO: handle color conversions
// TODO: handle meta-tiling
// TODO: how to handle timeout here? I guess we need to move it into the dispatcher?
RenderedImage image = null;
// fast path for pure coverage rendering
if (DefaultWebMapService.isDirectRasterPathEnabled() &&
mapContent.layers().size() == 1
&& mapContent.getAngle() == 0.0
&& (layout == null || layout.isEmpty())) {
List<GridCoverage2D> renderedCoverages = new ArrayList<GridCoverage2D>(2);
try {
image = directRasterRender(mapContent, 0, renderedCoverages);
} catch (Exception e) {
throw new ServiceException("Error rendering coverage on the fast path", e);
}
if (image != null) {
return buildMap(mapContent, image);
}
}
// we use the alpha channel if the image is transparent or if the meta tiler
// is enabled, since apparently the Crop operation inside the meta-tiler
// generates striped images in that case (see GEOS-
boolean useAlpha = transparent || MetatileMapOutputFormat.isRequestTiled(request, this);
final RenderedImage preparedImage = prepareImage(paintArea.width, paintArea.height,
palette, useAlpha);
final Map<RenderingHints.Key, Object> hintsMap = new HashMap<RenderingHints.Key, Object>();
final Graphics2D graphic = getGraphics(transparent, bgColor, preparedImage, hintsMap);
// set up the antialias hints
if (AA_NONE.equals(antialias)) {
hintsMap.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
if (preparedImage.getColorModel() instanceof IndexColorModel) {
// otherwise we end up with dithered colors where the match is
// not 100%
hintsMap.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
}
} else if (AA_TEXT.equals(antialias)) {
hintsMap.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
hintsMap.put(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
} else {
if (antialias != null && !AA_FULL.equals(antialias)) {
LOGGER.warning("Unrecognized antialias setting '" + antialias
+ "', valid values are " + AA_SETTINGS);
}
hintsMap.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
// these two hints improve text layout in diagonal labels and reduce artifacts
// in line rendering (without hampering performance)
hintsMap.put(RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
hintsMap.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
// turn off/on interpolation rendering hint
if (wms != null) {
if (WMSInterpolation.Nearest.equals(wms.getInterpolation())) {
hintsMap.put(JAI.KEY_INTERPOLATION, NN_INTERPOLATION);
hintsMap.put(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
} else if (WMSInterpolation.Bilinear.equals(wms.getInterpolation())) {
hintsMap.put(JAI.KEY_INTERPOLATION, BIL_INTERPOLATION);
hintsMap.put(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
} else if (WMSInterpolation.Bicubic.equals(wms.getInterpolation())) {
hintsMap.put(JAI.KEY_INTERPOLATION, BIC_INTERPOLATION);
hintsMap.put(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
}
}
// make sure the hints are set before we start rendering the map
graphic.setRenderingHints(hintsMap);
RenderingHints hints = new RenderingHints(hintsMap);
StreamingRenderer renderer = new StreamingRenderer();
renderer .setThreadPool(DefaultWebMapService.getRenderingPool());
renderer.setMapContent(mapContent);
renderer.setJava2DHints(hints);
// setup the renderer hints
Map<Object, Object> rendererParams = new HashMap<Object, Object>();
rendererParams.put("optimizedDataLoadingEnabled", new Boolean(true));
rendererParams.put("renderingBuffer", new Integer(mapContent.getBuffer()));
rendererParams.put("maxFiltersToSendToDatastore", DefaultWebMapService.getMaxFilterRules());
rendererParams.put(StreamingRenderer.SCALE_COMPUTATION_METHOD_KEY,
mapContent.getRendererScaleMethod());
if (AA_NONE.equals(antialias)) {
rendererParams.put(StreamingRenderer.TEXT_RENDERING_KEY,
StreamingRenderer.TEXT_RENDERING_STRING);
} else {
// used to be TEXT_RENDERING_ADAPTIVE always, but since java 7 calling drawGlyphVector
// just generates very ugly results
rendererParams.put(StreamingRenderer.TEXT_RENDERING_KEY,
StreamingRenderer.TEXT_RENDERING_OUTLINE);
}
if (DefaultWebMapService.isLineWidthOptimizationEnabled()) {
rendererParams.put(StreamingRenderer.LINE_WIDTH_OPTIMIZATION_KEY, true);
}
// turn on advanced projection handling
rendererParams.put(StreamingRenderer.ADVANCED_PROJECTION_HANDLING_KEY, true);
if(DefaultWebMapService.isContinuousMapWrappingEnabled()) {
rendererParams.put(StreamingRenderer.CONTINUOUS_MAP_WRAPPING, true);
}
// see if the user specified a dpi
if (request.getFormatOptions().get("dpi") != null) {
rendererParams.put(StreamingRenderer.DPI_KEY, (request
.getFormatOptions().get("dpi")));
}
boolean kmplacemark = false;
if (request.getFormatOptions().get("kmplacemark") != null)
kmplacemark = ((Boolean) request.getFormatOptions().get("kmplacemark"))
.booleanValue();
if (kmplacemark) {
// create a StyleVisitor that copies a style, but removes the
// PointSymbolizers and TextSymbolizers
KMLStyleFilteringVisitor dupVisitor = new KMLStyleFilteringVisitor();
// Remove PointSymbolizers and TextSymbolizers from the
// layers' Styles to prevent their rendering on the
// raster image. Both are better served with the
// placemarks.
List<Layer> layers = mapContent.layers();
for (int i = 0; i < layers.size(); i++) {
if (layers.get(i) instanceof StyleLayer) {
StyleLayer layer = (StyleLayer) layers.get(i);
Style style = layer.getStyle();
style.accept(dupVisitor);
Style copy = (Style) dupVisitor.getCopy();
layer.setStyle(copy);
}
}
}
renderer.setRendererHints(rendererParams);
// if abort already requested bail out
// if (this.abortRequested) {
// graphic.dispose();
// return null;
// }
// enforce no more than x rendering errors
int maxErrors = wms.getMaxRenderingErrors();
MaxErrorEnforcer errorChecker = new MaxErrorEnforcer(renderer, maxErrors);
// Add a render listener that ignores well known rendering exceptions and reports back non
// ignorable ones
final RenderExceptionStrategy nonIgnorableExceptionListener;
nonIgnorableExceptionListener = new RenderExceptionStrategy(renderer);
renderer.addRenderListener(nonIgnorableExceptionListener);
onBeforeRender(renderer);
// setup the timeout enforcer (the enforcer is neutral when the timeout is 0)
int maxRenderingTime = wms.getMaxRenderingTime() * 1000;
RenderingTimeoutEnforcer timeout = new RenderingTimeoutEnforcer(maxRenderingTime, renderer,
graphic);
timeout.start();
try {
// finally render the image;
renderer.paint(graphic, paintArea, mapContent.getRenderingArea(),
mapContent.getRenderingTransform());
// apply watermarking
if (layout != null) {
try {