org.polymap.core.data.image.WmsRenderProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.polymap.core.data.image.WmsRenderProcessor.java

Source

/* 
 * polymap.org
 * Copyright 2009, Polymap GmbH, and individual contributors as indicated
 * by the @authors tag.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 *
 * $Id$
 */

package org.polymap.core.data.image;

import java.util.List;
import java.util.Properties;
import java.util.Set;

import java.awt.Color;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geotools.data.ows.Layer;
import org.geotools.data.wms.WebMapServer;
import org.geotools.data.wms.response.GetMapResponse;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.opengis.geometry.BoundingBox;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;

import com.vividsolutions.jts.geom.Envelope;

import net.refractions.udig.catalog.IGeoResource;
import net.refractions.udig.catalog.IService;
import net.refractions.udig.catalog.internal.wms.WMSServiceImpl;

import org.polymap.core.data.pipeline.ITerminalPipelineProcessor;
import org.polymap.core.data.pipeline.ProcessorRequest;
import org.polymap.core.data.pipeline.ProcessorResponse;
import org.polymap.core.data.pipeline.ProcessorSignature;
import org.polymap.core.data.pipeline.PipelineExecutor.ProcessorContext;
import org.polymap.core.project.ILayer;
import org.polymap.core.project.LayerUseCase;

/**
 * 
 *
 * @author <a href="http://www.polymap.de">Falko Braeutigam</a>
 *         <li>19.10.2009: created</li>
 * @version POLYMAP3 ($Revision$)
 * @since 3.0
 */
public class WmsRenderProcessor implements ITerminalPipelineProcessor {

    private static final Log log = LogFactory.getLog(WmsRenderProcessor.class);

    private static final ReferencedEnvelope NILL_BOX = new ReferencedEnvelope(0, 0, 0, 0,
            DefaultGeographicCRS.WGS84);

    private final static ProcessorSignature signature = new ProcessorSignature(
            new Class[] { GetMapRequest.class, GetLegendGraphicRequest.class, GetLayerTypesRequest.class },
            new Class[] {}, new Class[] {},
            new Class[] { EncodedImageResponse.class, GetLayerTypesResponse.class });

    public static ProcessorSignature signature(LayerUseCase usecase) {
        return signature;
    }

    public static boolean isCompatible(IService service) {
        return service instanceof WMSServiceImpl;
    }

    // instance *******************************************

    public void init(Properties props) {
    }

    public void processRequest(ProcessorRequest r, ProcessorContext context) throws Exception {
        // GetMapRequest
        if (r instanceof GetMapRequest) {
            getMap((GetMapRequest) r, context);
        }
        // GetLegendGraphicRequest
        else if (r instanceof GetLegendGraphicRequest) {
            getLegendGraphic((GetLegendGraphicRequest) r, context);
        }
        // GetLayerTypes
        else if (r instanceof GetLayerTypesRequest) {
            getLayerTypes();
            List<LayerType> types = getLayerTypes();
            context.sendResponse(new GetLayerTypesResponse(types));
            context.sendResponse(ProcessorResponse.EOP);
        } else {
            throw new IllegalArgumentException("Unhandled request type: " + r);
        }
    }

    protected void getLegendGraphic(GetLegendGraphicRequest r, ProcessorContext context) {
        throw new RuntimeException("not yet implemented.");
    }

    protected List<LayerType> getLayerTypes() {
        throw new RuntimeException("not yet implemented.");
    }

    protected void getMap(GetMapRequest request, ProcessorContext context) throws Exception {
        int width = request.getWidth();
        int height = request.getHeight();
        BoundingBox bbox = request.getBoundingBox();
        log.debug("bbox=" + bbox + ", imageSize=" + width + "x" + height);

        Set<ILayer> layers = context.getLayers();
        IService service = context.getService();

        // skip if no layers are visible
        if (layers.isEmpty()) {
            log.debug("no layers.");
        }

        WebMapServer wms = service.resolve(WebMapServer.class, null);
        org.geotools.data.wms.request.GetMapRequest getMap = wms.createGetMapRequest();

        getMap.setFormat(request.getFormat());
        getMap.setDimensions(width, height);
        getMap.setTransparent(true);
        Color color = request.getBgColor();
        if (color != null) {
            getMap.setBGColour(String.format("#%02X%02X%02X", color.getRed(), color.getGreen(), color.getBlue()));
        }
        //getMap.setBGColour( "0xFFFFFF" );

        //CRSEnvelope envelop = calculateRequestBBox( )
        // CRSEnvelope envelop = new CRSEnvelope( "EPSG:31468", 4621529.925,5657790.683333333,4623149.175,5659484.016666667 );
        BoundingBox requestBBox = bbox;
        getMap.setBBox(requestBBox.getMinX() + "," + requestBBox.getMinY() //$NON-NLS-1$
                + "," + requestBBox.getMaxX() //$NON-NLS-1$
                + "," + requestBBox.getMaxY()); //$NON-NLS-1$
        // FIXME hack for LRA to support EPSG3857, see GeoServerWms
        // check to see if backend wms supports 900913 or not
        String srs = request.getCRS();
        //String srs = request.getCRS().equals( "EPSG:900913" )
        //        ? "EPSG:3857" : request.getCRS(); 
        getMap.setSRS(srs);

        //getMap.setBBox(  );

        //request.setSRS( "EPSG:4326" );
        //request.setBBox( "-131.13151509433965,46.60532747661736,-117.61620566037737,56.34191403281659" );
        // Note: you can look at the layer metadata to determine a layer's bounding box for a SRS

        //        Map sortedLayersMap = MultiValueMap.decorate( new TreeMap() );
        //        for (Iterator it=visibleLayers.referencedLayers(); it.hasNext();) {
        //            Layer layer = (Layer)availableLayers.get( it.next() );
        //            Integer layerOrder = (Integer)layerOrderMap.get( layer.getName() );
        //            // reverse order; request.addLayer() works that way
        //            Integer key = Integer.valueOf( Integer.MAX_VALUE - layerOrder.intValue() );
        //            sortedLayersMap.put( key, layer );
        //        }
        for (ILayer layer : layers) {
            IGeoResource geores = layer.getGeoResource();
            Layer wmsLayer = geores.resolve(org.geotools.data.ows.Layer.class, null);
            getMap.addLayer(wmsLayer);
            log.debug("    request: layer: " + layer.getLabel());
        }
        log.debug("    WMS URL:" + getMap.getFinalURL());

        InputStream in = null;
        try {
            long start = System.currentTimeMillis();
            GetMapResponse wmsResponse = wms.issueRequest(getMap);
            log.debug("Got repsonse (" + (System.currentTimeMillis() - start) + "ms). providing data: "
                    + wmsResponse.getContentType());

            in = wmsResponse.getInputStream();
            int count = 0;
            byte[] buf = new byte[8 * 1024];
            for (int c = in.read(buf); c != -1; c = in.read(buf)) {
                context.sendResponse(new EncodedImageResponse(buf, c));
                buf = new byte[8 * 2048];
                //log.debug( "    --->data sent: " + c );
                count += c;
            }
            if (count == 0) {
                throw new IOException("WMSResponse is empty.");
            }
            context.sendResponse(ProcessorResponse.EOP);
            log.debug("...all data send.");
        } finally {
            IOUtils.closeQuietly(in);
        }
    }

    public void processResponse(ProcessorResponse reponse, ProcessorContext context) throws Exception {
        throw new IllegalStateException("This is a terminal processor.");
    }

    /**
     * Using the viewport bounds and combined wms layer extents, determines an appropriate bounding
     * box by projecting the viewport into the request CRS, intersecting the bounds, and returning
     * the result.
     * 
     * @param wmsLayers all adjacent wms layers we are requesting
     * @param viewport map editor bounds and crs
     * @param requestCRS coordinate reference system supported by the server
     * @return the bbox to ask the server for
     * @throws MismatchedDimensionException
     * @throws TransformException
     * @throws FactoryException
     */
    public static ReferencedEnvelope calculateRequestBBox(List<Layer> wmsLayers, ReferencedEnvelope viewport,
            CoordinateReferenceSystem requestCRS)
            throws MismatchedDimensionException, TransformException, FactoryException {
        /* The bounds of all wms layers on this server combined */
        Envelope layersBBox = getLayersBoundingBox(requestCRS, wmsLayers);
        if (isEnvelopeNull(layersBBox)) {
            // the wms server has no bounds
            log.debug("Zero width/height envelope: wmsLayers = " + layersBBox); //$NON-NLS-1$
            layersBBox = null;
            // alternatively, we could impose a reprojected -180,180,-90,90
        }

        /* The viewport bounds projected to the request crs */
        ReferencedEnvelope reprojectedViewportBBox = viewport.transform(requestCRS, true);
        if (isEnvelopeNull(reprojectedViewportBBox)) {
            // viewport couldn't be reprojected
            log.debug("Zero width/height envelope: reprojected viewport from " + viewport //$NON-NLS-1$
                    + " to " + requestCRS + " returned " + reprojectedViewportBBox); //$NON-NLS-1$ //$NON-NLS-2$
        }
        // alternative for better accuracy: new
        // ReferencedEnvelope(JTS.transform(viewport, null,
        // CRS.findMathTransform(viewportCRS, crs, true), 4), crs);

        /* The intersection of the viewport and the combined wms layers */
        Envelope interestBBox;
        if (layersBBox == null) {
            interestBBox = reprojectedViewportBBox;
        } else {
            interestBBox = reprojectedViewportBBox.intersection(layersBBox);
        }
        if (isEnvelopeNull(interestBBox)) {
            // outside of bounds, do not draw
            log.debug("Bounds of the data are outside the bounds of the viewscreen."); //$NON-NLS-1$
            return NILL_BOX;
        }

        /* The bounds of the request we are going to make */
        ReferencedEnvelope requestBBox = new ReferencedEnvelope(interestBBox, requestCRS);
        return requestBBox;
    }

    public static Envelope getLayersBoundingBox(CoordinateReferenceSystem crs, List<Layer> layers) {
        Envelope envelope = null;

        for (Layer layer : layers) {

            GeneralEnvelope temp = layer.getEnvelope(crs);

            if (temp != null) {
                Envelope jtsTemp = new Envelope(temp.getMinimum(0), temp.getMaximum(0), temp.getMinimum(1),
                        temp.getMaximum(1));
                if (envelope == null) {
                    envelope = jtsTemp;
                } else {
                    envelope.expandToInclude(jtsTemp);
                }
            }
        }
        return envelope;
    }

    private static boolean isEnvelopeNull(Envelope bbox) {
        if (bbox.getWidth() <= 0 || bbox.getHeight() <= 0) {
            return true;
        }
        return false;
    }

}