Package org.geoserver.wms.map

Source Code of org.geoserver.wms.map.MetatileMapOutputFormat

/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package org.geoserver.wms.map;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.media.jai.JAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.operator.CropDescriptor;

import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.ServiceException;
import org.geoserver.wms.GetMapOutputFormat;
import org.geoserver.wms.GetMapRequest;
import org.geoserver.wms.WMSMapContext;
import org.geoserver.wms.WebMap;
import org.geoserver.wms.map.QuickTileCache.MetaTileKey;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.util.logging.Logging;

/**
* Wrapping map producer that performs on the fly meta tiling wrapping another map producer. It will
* first peek inside a tile cache to see if the requested tile has already been computed, if so,
* it'll encode and return that one, otherwise it'll build a meta tile, split it, and finally encode
* just the requested tile, putting the others in the tile cache.
*
* @author Andrea Aime - TOPP
* @author Simone Giannecchini - GeoSolutions
*/
public final class MetatileMapOutputFormat implements GetMapOutputFormat {

    /** A logger for this class. */
    private static final Logger LOGGER = Logging.getLogger(MetatileMapOutputFormat.class);

    /** Small number for double equality comparison */
    public static final double EPS = 1E-6;

    private static QuickTileCache tileCache;

    private GetMapRequest request;

    private RenderedImageMapOutputFormat delegate;

    public MetatileMapOutputFormat(GetMapRequest request, RenderedImageMapOutputFormat delegate) {
        if (tileCache == null) {
            tileCache = (QuickTileCache) GeoServerExtensions.bean("metaTileCache");
        }
        this.request = request;
        this.delegate = delegate;
    }

    /**
     *
     * @see org.geoserver.wms.GetMapOutputFormat#produceMap(org.geoserver.wms.WMSMapContext)
     */
    public WebMap produceMap(WMSMapContext mapContext) throws ServiceException, IOException {
        // get the key that identifies the meta tile. The cache will make sure
        // two threads asking
        // for the same tile will get the same key, and thus will synchronize
        // with each other
        // (the first eventually builds the meta-tile, the second finds it ready
        // to be used)
        QuickTileCache.MetaTileKey key = tileCache.getMetaTileKey(request);

        synchronized (key) {
            RenderedImage tile = tileCache.getTile(key, request);
            List<GridCoverage2D> renderedCoverages = null;

            if (LOGGER.isLoggable(Level.FINER)) {
                LOGGER.finer("Looked for meta tile " + key.metaTileCoords.x + ", "
                        + key.metaTileCoords.y + "in cache: " + ((tile != null) ? "hit!" : "miss"));
            }

            if (tile == null) {
                // compute the meta-tile
                if (LOGGER.isLoggable(Level.FINER)) {
                    LOGGER.finer("Building meta tile " + key.metaTileCoords.x + ", "
                            + key.metaTileCoords.y);
                }

                // alter the map definition so that we build a meta-tile instead
                // of just the tile
                ReferencedEnvelope origEnv = mapContext.getAreaOfInterest();
                mapContext.setAreaOfInterest(new ReferencedEnvelope(key.getMetaTileEnvelope(),
                        origEnv.getCoordinateReferenceSystem()));
                mapContext.setMapWidth(key.getTileSize() * key.getMetaFactor());
                mapContext.setMapHeight(key.getTileSize() * key.getMetaFactor());
                mapContext.setTileSize(key.getTileSize());

                RenderedImageMap metaTileMap = delegate.produceMap(mapContext);

                RenderedImage metaTile = metaTileMap.getImage();
                RenderedImage[] tiles = split(key, metaTile, mapContext);
                tileCache.storeTiles(key, tiles);
                tile = tileCache.getTile(key, request, tiles);
                renderedCoverages = metaTileMap.getRenderedCoverages();
            }
            RenderedImageMap tileMap = new RenderedImageMap(mapContext, tile, getMimeType());
            tileMap.setRenderedCoverages(renderedCoverages);
            return tileMap;
        }
    }

    /**
     *
     * @see org.geoserver.wms.GetMapOutputFormat#getOutputFormatNames()
     */
    public Set<String> getOutputFormatNames() {
        return delegate.getOutputFormatNames();
    }

    /**
     *
     * @see org.geoserver.wms.GetMapOutputFormat#getMimeType()
     */
    public String getMimeType() {
        return delegate.getMimeType();
    }

    /**
     * True if the request has the tiled hint, is 256x256 image, and the raw delegate is a raster
     * one
     *
     * @param request
     * @param delegate
     * @return
     */
    public static boolean isRequestTiled(GetMapRequest request, GetMapOutputFormat delegate) {
        boolean tiled = request.isTiled();
        Point2D tilesOrigin = request.getTilesOrigin();
        int width = request.getWidth();
        int height = request.getHeight();
        if (tiled && tilesOrigin != null && width == 256 && height == 256
                && delegate instanceof RenderedImageMapOutputFormat) {
            return true;
        }

        return false;
    }

    /**
     * Splits the tile into a set of tiles, numbered from lower right and going up so that first row
     * is 0,1,2,...,metaTileFactor, and so on. In the case of a 3x3 meta-tile, the layout is as
     * follows:
     *
     * <pre>
     *    6 7 8
     *    3 4 5
     *    0 1 2
     * </pre>
     *
     * @param key
     * @param metaTile
     * @param map
     * @return
     */
    private RenderedImage[] split(MetaTileKey key, RenderedImage metaTile, WMSMapContext map) {
        final int metaFactor = key.getMetaFactor();
        final RenderedImage[] tiles = new RenderedImage[key.getMetaFactor() * key.getMetaFactor()];
        final int tileSize = key.getTileSize();
        final RenderingHints no_cache = new RenderingHints(JAI.KEY_TILE_CACHE, null);

        // get tile factors
        final int tileW = metaTile.getTileWidth();
        final int tileH = metaTile.getTileHeight();
        final int tileGridXOffset = metaTile.getTileGridXOffset();
        final int tileGridYOffset = metaTile.getTileGridYOffset();
        final boolean metatilingIsRespected = tileGridXOffset == 0 && tileGridYOffset == 0
                && tileH == tileSize && tileW == tileSize;

        for (int i = 0; i < metaFactor; i++) {
            for (int j = 0; j < metaFactor; j++) {
                int x = j * tileSize;
                int y = (tileSize * (metaFactor - 1)) - (i * tileSize);

                final Raster tile_;
                RenderedImage tile;
                if (metaTile instanceof PlanarImage) {
                    final PlanarImage pImage = (PlanarImage) metaTile;

                    if (metatilingIsRespected) {
                        final int tileX = pImage.XToTileX(x);
                        final int tileY = pImage.YToTileY(y);
                        tile_ = pImage.getTile(tileX, tileY);

                    } else {
                        Rectangle sourceArea = new Rectangle(x, y, tileSize, tileSize);
                        sourceArea = sourceArea.intersection(pImage.getBounds());
                        tile_ = pImage.getData(sourceArea);

                    }
                    WritableRaster wTile = WritableRaster.createWritableRaster(tile_
                            .getSampleModel().createCompatibleSampleModel(tileSize, tileSize),
                            tile_.getDataBuffer(), new Point(0, 0));
                    tile = new BufferedImage(pImage.getColorModel(), wTile, pImage.getColorModel()
                            .isAlphaPremultiplied(), null);

                } else if (metaTile instanceof BufferedImage) {
                    final BufferedImage image = (BufferedImage) metaTile;
                    tile = image.getSubimage(x, y, tileSize, tileSize);
                } else {
                    tile = CropDescriptor.create(metaTile, new Float(x), new Float(y), new Float(
                            tileSize), new Float(tileSize), no_cache);

                }

                tiles[(i * key.getMetaFactor()) + j] = tile;
            }
        }

        return tiles;
    }

}
TOP

Related Classes of org.geoserver.wms.map.MetatileMapOutputFormat

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.