Package org.geoserver.wms

Source Code of org.geoserver.wms.DefaultWebMapService

/* 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;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.geoserver.catalog.LayerGroupInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.platform.GeoServerExtensions;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.referencing.operation.projection.ProjectionException;
import org.geotools.styling.NamedLayer;
import org.geotools.styling.Style;
import org.geotools.styling.StyleFactory;
import org.geotools.styling.StyledLayer;
import org.geotools.styling.StyledLayerDescriptor;
import org.geotools.styling.visitor.DuplicatingStyleVisitor;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.TransformException;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.vfny.geoserver.wms.WmsException;
import org.vfny.geoserver.wms.requests.DescribeLayerRequest;
import org.vfny.geoserver.wms.requests.GetFeatureInfoRequest;
import org.vfny.geoserver.wms.requests.GetLegendGraphicRequest;
import org.vfny.geoserver.wms.requests.GetMapRequest;
import org.vfny.geoserver.wms.requests.GetStylesRequest;
import org.vfny.geoserver.wms.requests.WMSCapabilitiesRequest;
import org.vfny.geoserver.wms.responses.DescribeLayerResponse;
import org.vfny.geoserver.wms.responses.GetFeatureInfoResponse;
import org.vfny.geoserver.wms.responses.GetLegendGraphicResponse;
import org.vfny.geoserver.wms.responses.GetMapResponse;
import org.vfny.geoserver.wms.responses.WMSCapabilitiesResponse;
import org.vfny.geoserver.wms.responses.map.kml.KMLReflector;
import org.vfny.geoserver.wms.servlets.Capabilities;
import org.vfny.geoserver.wms.servlets.DescribeLayer;
import org.vfny.geoserver.wms.servlets.GetFeatureInfo;
import org.vfny.geoserver.wms.servlets.GetLegendGraphic;

import com.vividsolutions.jts.geom.Envelope;

public class DefaultWebMapService implements WebMapService,
        ApplicationContextAware {
    /**
     * default for 'format' parameter.
     */
    public static String FORMAT = "image/png";

    /**
     * default for 'styles' parameter.
     */
    public static List STYLES = Collections.EMPTY_LIST;

    /**
     * longest side for the preview
     */
    public static int MAX_SIDE = 512;
   
    /**
     * minimum height to have a decent looking OL preview
     */
    public static int MIN_OL_HEIGHT = 330;

    /**
     * default for 'srs' parameter.
     */
    public static String SRS = "EPSG:4326";

    /**
     * default for 'transparent' parameter.
     */
    public static Boolean TRANSPARENT = Boolean.TRUE;
   
    /**
     * default for 'bbox' paramter
     */
    public static ReferencedEnvelope BBOX = new ReferencedEnvelope(
            new Envelope(-180, 180, -90, 90), DefaultGeographicCRS.WGS84);

    /**
     * wms configuration
     */
    WMS wms;
   
    /**
     * Application context
     */
    ApplicationContext context;
   
    /**
     * Temporary field that handles the usage of the line width optimization code
     */
    private static Boolean OPTIMIZE_LINE_WIDTH = null;
   
    /**
     * Temporary field that handles the choice of renderer to be used
     */
    private static Boolean USE_STREAMING_RENDERER = null;
   
    /**
     * Max number of rule filters to be used against the data source
     */
    public static Integer MAX_FILTER_RULES = null;
   
    /**
     * Activate advanced projection handling
     */
    private static Boolean ADVANCED_PROJECTION_HANDLING = null;


    public DefaultWebMapService( WMS wms ) {
        this.wms = wms;
    }
   
    /**
     * @see WebMapService#getServiceInfo()
     */
    public WMSInfo getServiceInfo() {
        return wms.getServiceInfo();
    }
   
    /**
     * @see ApplicationContextAware#setApplicationContext(ApplicationContext)
     */
    public void setApplicationContext(ApplicationContext context)
            throws BeansException {
        this.context = context;
       
        // first time initialization of line width optimization flag
        if (OPTIMIZE_LINE_WIDTH == null) {
            String enabled = GeoServerExtensions.getProperty("OPTIMIZE_LINE_WIDTH", context);
            // default to true, but allow switching off
            if(enabled == null)
                OPTIMIZE_LINE_WIDTH = false;
            else
                OPTIMIZE_LINE_WIDTH = Boolean.valueOf(enabled);
        }
       
        // initialization of the renderer choice flag
        if (USE_STREAMING_RENDERER == null) {
            String enabled = GeoServerExtensions.getProperty("USE_STREAMING_RENDERER", context);
            // default to true, but allow switching off
            if(enabled == null)
                USE_STREAMING_RENDERER = false;
            else
                USE_STREAMING_RENDERER = Boolean.valueOf(enabled);
        }
       
        // initialization of the renderer choice flag
        if (MAX_FILTER_RULES == null) {
            String rules = GeoServerExtensions.getProperty("MAX_FILTER_RULES", context);
            // default to true, but allow switching off
            if(rules == null)
                MAX_FILTER_RULES = 20;
            else
                MAX_FILTER_RULES = Integer.valueOf(rules);
        }
       
        // first time initialization of advanced projection handling
        if (ADVANCED_PROJECTION_HANDLING == null) {
            String enabled = GeoServerExtensions.getProperty("ADVANCED_PROJECTION_HANDLING", context);
            // default to true, but allow switching off
            if(enabled == null)
                ADVANCED_PROJECTION_HANDLING = false;
            else
                ADVANCED_PROJECTION_HANDLING = Boolean.valueOf(enabled);
        }
    }
   
    /**
     * Checks wheter the line width optimization is enabled, or not (defaults to true
     * unless the user sets the OPTIMIZE_LINE_WIDTH property to false)
     * @return
     */
    public static boolean isLineWidthOptimizationEnabled() {
        return OPTIMIZE_LINE_WIDTH;
    }
   
    /**
     * Checks wheter the line streaming renderer is enabled, or not (defaults to false
     * unless the user sets the USE_STREAMING_RENDERER property to true)
     * @return
     */
    public static boolean useStreamingRenderer() {
        return USE_STREAMING_RENDERER;
    }
   
    /**
     * If true (default) use the sld rule filters to compose the query to the DB,
     * otherwise don't and get down only with the bbox and eventual definition filter)
     * @return
     */
    public static int getMaxFilterRules() {
        return MAX_FILTER_RULES;
    }
   
    /**
     * Returns true if projection geometry cutting and wrapping is enabled
     * @return
     */
    public static boolean isAdvancedProjectionHandlingEnabled() {
        return ADVANCED_PROJECTION_HANDLING;
    }

    /**
     * @see WebMapService#getCapabilities(WMSCapabilitiesRequest)
     */
    public WMSCapabilitiesResponse getCapabilities(
            WMSCapabilitiesRequest request) {
        Capabilities capabilities = (Capabilities) context
                .getBean("wmsGetCapabilities");

        return (WMSCapabilitiesResponse) capabilities.getResponse();
    }

    /**
     * @see WebMapService#capabilities(WMSCapabilitiesRequest)
     */
    public WMSCapabilitiesResponse capabilities(WMSCapabilitiesRequest request) {
        return getCapabilities(request);
    }

    /**
     * @see WebMapService#describeLayer(DescribeLayerRequest)
     */
    public DescribeLayerResponse describeLayer(DescribeLayerRequest request) {
        DescribeLayer describeLayer = (DescribeLayer) context
                .getBean("wmsDescribeLayer");

        return (DescribeLayerResponse) describeLayer.getResponse();
    }

    /**
     * @see WebMapService#getMap(GetMapRequest)
     */
    public GetMapResponse getMap(GetMapRequest request) {
        return new GetMapResponse(WMSExtensions.findMapProducers(context));
    }

    /**
     * @see WebMapService#map(GetMapRequest)
     */
    public GetMapResponse map(GetMapRequest request) {
        return getMap(request);
    }

    /**
     * @see WebMapService#getFeatureInfo(GetFeatureInfoRequest)
     */
    public GetFeatureInfoResponse getFeatureInfo(GetFeatureInfoRequest request) {
        GetFeatureInfo getFeatureInfo =
            (GetFeatureInfo) context.getBean("wmsGetFeatureInfo");

        return (GetFeatureInfoResponse) getFeatureInfo.getResponse();
    }

    /**
     * @see WebMapService#getLegendGraphic(GetLegendGraphicRequest)
     */
    public GetLegendGraphicResponse getLegendGraphic(
            GetLegendGraphicRequest request) {
        GetLegendGraphic getLegendGraphic =
            (GetLegendGraphic) context.getBean("wmsGetLegendGraphic");

        return (GetLegendGraphicResponse) getLegendGraphic.getResponse();
    }

    public void kml(GetMapRequest getMap, HttpServletResponse response){
        try{
            KMLReflector.doWms(getMap, response, this, wms);
            // return response;
        } catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    /**
     * @see WebMapService#reflect(GetMapRequest)
     */
    public GetMapResponse reflect(GetMapRequest request) {
        return getMapReflect(request);
    }
   
    public StyledLayerDescriptor getStyles(GetStylesRequest request) {
        if(request.getSldVer() != null
                && "".equals(request.getSldVer()) && !"1.0.0".equals(request.getSldVer()))
            throw new WmsException("SLD version " + request.getSldVer() + " not supported");
       
        try {
            StyleFactory factory = CommonFactoryFinder.getStyleFactory(null);
            List<StyledLayer> layers = new ArrayList<StyledLayer>();
            for(String layerName : request.getLayers()) {
                NamedLayer namedLayer = factory.createNamedLayer();
                layers.add(namedLayer);
                namedLayer.setName(layerName);
                LayerGroupInfo group = wms.getLayerGroupByName(layerName);
                LayerInfo layer = wms.getLayerByName(layerName);
                if(group != null) {
                    // nothing to do, groups have no style
                } else if(layer != null) {
                    Style style = layer.getDefaultStyle().getStyle();
                    // add the default style first
                    style = cloneStyle(style);
                    style.setDefault(true);
                    style.setName(layer.getDefaultStyle().getName());
                    namedLayer.styles().add(style);
                    // add alternate styles
                    for(StyleInfo si : layer.getStyles()) {
                        style = cloneStyle(si.getStyle());
                        style.setName(si.getName());
                        namedLayer.styles().add(style);
                    }
                } else {
                    // we should really add a code and a locator...
                    throw new WmsException("Unknown layer " + layerName);
                }
            }
           
           
            StyledLayerDescriptor sld = factory.createStyledLayerDescriptor();
            sld.setStyledLayers((StyledLayer[]) layers.toArray(new StyledLayer[layers.size()]));
           
            return sld;
        } catch(IOException e) {
            throw new WmsException(e);
        }
    }

    private Style cloneStyle(Style style) {
        DuplicatingStyleVisitor cloner = new DuplicatingStyleVisitor();
        style.accept(cloner);
        style = (Style) cloner.getCopy();
        return style;
    }

    /**s
     * @see WebMapService#getMapReflect(GetMapRequest)
     */
    public GetMapResponse getMapReflect(GetMapRequest request) {
        GetMapRequest getMap = (GetMapRequest) request;

        // set the defaults
        if (getMap.getFormat() == null) {
            getMap.setFormat(FORMAT);
        }

        if ((getMap.getStyles() == null) || getMap.getStyles().isEmpty()) {
            // set styles to be the defaults for the specified layers
            // TODO: should this be part of core WMS logic? is so lets throw
            // this
            // into the GetMapKvpRequestReader
            if ((getMap.getLayers() != null) && (getMap.getLayers().length > 0)) {
                ArrayList styles = new ArrayList(getMap.getLayers().length);

                for (int i = 0; i < getMap.getLayers().length; i++) {
                    styles.add(getMap.getLayers()[i].getDefaultStyle());
                }

                getMap.setStyles(styles);
            } else {
                getMap.setStyles(STYLES);
            }
        }

        // auto-magic missing info configuration
        autoSetBoundsAndSize(getMap);

        return getMap(getMap);
    }

    /**
     * This method tries to automatically determine SRS, bounding box and output
     * size based on the layers provided by the user and any other parameters.
     *
     * If bounds are not specified by the user, they are automatically se to the
     * union of the bounds of all layers.
     *
     * The size of the output image defaults to 512 pixels, the height is
     * automatically determined based on the width to height ratio of the
     * requested layers. This is also true if either height or width are
     * specified by the user. If both height and width are specified by the
     * user, the automatically determined bounding box will be adjusted to fit
     * inside these bounds.
     *
     * General idea
     * 1) Figure out whether SRS has been specified, fall back to EPSG:4326
     * 2) Determine whether all requested layers use the same SRS,
     *   - if so, try to do bounding box calculations in native coordinates
     * 3) Aggregate the bounding boxes (in EPSG:4326 or native)
     * 4a) If bounding box has been specified, adjust height of image to match
     * 4b) If bounding box has not been specified, but height has, adjust bounding box
     */
    public static void autoSetBoundsAndSize(GetMapRequest getMap) {
        // Get the layers
        MapLayerInfo[] layers = getMap.getLayers();       
       
        /** 1) Check what SRS has been requested */
        String reqSRS = getMap.getSRS();
       
        // if none, try to determine which SRS to use
        // and keep track of whether we can use native all the way
        boolean useNativeBounds = true;
        if(reqSRS == null) {
            reqSRS = guessCommonSRS(layers);
            forceSRS(getMap, reqSRS);
        }
       
        /** 2) Compare requested SRS */
        for (int i = 0; useNativeBounds && i < layers.length; i++) {
            if (layers[i] != null) {
                String layerSRS = layers[i].getSRS();
                useNativeBounds = reqSRS.equalsIgnoreCase(layerSRS)
                    && layers[i].getResource().getNativeBoundingBox() != null;
            } else {
                useNativeBounds = false;
            }
        }
       
        CoordinateReferenceSystem reqCRS;
        try {
            reqCRS = CRS.decode(reqSRS);
        } catch(Exception e) {
            throw new WmsException(e);
        }
       
        // Ready to determine the bounds based on the layers, if not specified
        Envelope aggregateBbox = getMap.getBbox();
        boolean specifiedBbox = true;

        // If bbox is not specified by request
        if (aggregateBbox == null) {
            specifiedBbox = false;

            // Get the bounding box from the layers
            for (int i = 0; i < layers.length; i++) {
                MapLayerInfo layerInfo = layers[i];
                ReferencedEnvelope curbbox;
                try{
                    curbbox = layerInfo.getLatLongBoundingBox();
                    if(useNativeBounds){
                        ReferencedEnvelope nativeBbox = layerInfo.getBoundingBox();
                        if(nativeBbox == null){
                            try {
                                CoordinateReferenceSystem nativeCrs = layerInfo.getCoordinateReferenceSystem();
                                nativeBbox = curbbox.transform(nativeCrs, true);
                            } catch(Exception e) {
                                throw new WmsException("Best effort native bbox computation failed", "", e);
                            }
                        }
                        curbbox = nativeBbox;
                    }
                }catch(Exception e){
                    throw new RuntimeException(e);
                }
                if (aggregateBbox != null) {
                    aggregateBbox.expandToInclude(curbbox);
                } else {
                    aggregateBbox = curbbox;
                }
            }
  
            ReferencedEnvelope ref = null;
            // Reproject back to requested SRS if we have to
            if (!useNativeBounds && ! reqSRS.equalsIgnoreCase(SRS)) {
                try {
                   ref = new ReferencedEnvelope(aggregateBbox,
                            CRS.decode("EPSG:4326"));
                    aggregateBbox = ref.transform(reqCRS, true);
                } catch (ProjectionException pe) {
                    ref.expandBy( -1 * ref.getWidth() / 50, -1 * ref.getHeight() / 50);
                    try {
                        aggregateBbox = ref.transform(reqCRS, true);
                    } catch (FactoryException e) {
                        e.printStackTrace();
                    } catch (TransformException e) {
                        e.printStackTrace();
                    }
                    // And again...
                } catch (NoSuchAuthorityCodeException e) {
                    e.printStackTrace();
                } catch (TransformException e) {
                    e.printStackTrace();
                } catch (FactoryException e) {
                    e.printStackTrace();
                }
            }
        }

        // Just in case
        if (aggregateBbox == null) {
            forceSRS(getMap, DefaultWebMapService.SRS);
            aggregateBbox = DefaultWebMapService.BBOX;  
        }

        // Start the processing of adjust either the bounding box
        // or the pixel height / width
       
        double bbheight = aggregateBbox.getHeight();
        double bbwidth = aggregateBbox.getWidth();
        double bbratio = bbwidth / bbheight;

        double mheight = getMap.getHeight();
        double mwidth = getMap.getWidth();
       
        if (mheight > 0.5 && mwidth > 0.5 && specifiedBbox) {
            // This person really doesnt want our help,
            // we'll warp it any way they like it...
        } else {
            if (mheight > 0.5 && mwidth > 0.5) {
                // Fully specified, need to adjust bbox
                double mratio = mwidth / mheight;
                // Adjust bounds to be less than ideal to meet spec
                if (bbratio > mratio) {
                    // Too wide, need to increase height of bb
                    double diff = ((bbwidth / mratio) - bbheight) / 2;
                    aggregateBbox.expandBy(0, diff);
                } else {
                    // Too tall, need to increase width of bb
                    double diff = ((bbheight * mratio) - bbwidth) / 2;
                    aggregateBbox.expandBy(diff, 0);
                }
               
                adjustBounds(reqSRS, aggregateBbox);
               
            } else if (mheight > 0.5) {
                mwidth = bbratio * mheight;
            } else {
                if (mwidth > 0.5) {
                    mheight = (mwidth / bbratio >= 1) ? mwidth / bbratio : 1;
                } else {
                    if(bbratio > 1) {
                        mwidth = MAX_SIDE;
                        mheight = (mwidth / bbratio >= 1) ? mwidth / bbratio : 1;
                    } else {
                        mheight = MAX_SIDE;
                        mwidth = (mheight * bbratio >= 1) ? mheight * bbratio : 1;
                    }
                   
                    // make sure OL output height is sufficient to show the OL scale bar fully
                    if(mheight < MIN_OL_HEIGHT && (
                            "application/openlayers".equalsIgnoreCase(getMap.getFormat())
                            || "openlayers".equalsIgnoreCase(getMap.getFormat()))) {
                        mheight = MIN_OL_HEIGHT;
                        mwidth = (mheight * bbratio >= 1) ? mheight * bbratio : 1;
                    }
                       
                }
               
            }

            // Actually set the bounding box and size of image
            getMap.setBbox(aggregateBbox);
            getMap.setWidth((int) mwidth);
            getMap.setHeight((int) mheight);
        }
    }
   
    private static String guessCommonSRS(MapLayerInfo[] layers) {
        String SRS = null;
        for (MapLayerInfo layer : layers) {
            String layerSRS = layer.getSRS();
            if(SRS == null) {
                SRS = layerSRS.toUpperCase();
            } else if(!SRS.equals(layerSRS)) {
                // layers with mixed native SRS, let's just use the default
                return DefaultWebMapService.SRS;
            }
        }
        if(SRS == null) {
            return DefaultWebMapService.SRS;
        }
        return SRS;
    }

    private static void forceSRS(GetMapRequest getMap, String srs) {
        getMap.setSRS(srs);
       
        try {
            getMap.setCrs( CRS.decode(srs) );
        } catch (NoSuchAuthorityCodeException e) {
            e.printStackTrace();
        } catch (FactoryException e) {
            e.printStackTrace();
        }
    }
   
    /**
     * This adjusts the bounds by zooming out 2%, but also ensuring that
     * the maximum bounds do not exceed the world bounding box
     *
     * This only applies if the SRS is EPSG:4326 or EPSG:900913
     *
     * @param reqSRS the SRS
     * @param bbox the current bounding box
     * @return the adjusted bounding box
     */
    private static Envelope adjustBounds(String reqSRS, Envelope bbox) {       
        if(reqSRS.equalsIgnoreCase("EPSG:4326")) {
            bbox.expandBy(bbox.getWidth() / 100, bbox.getHeight() / 100);
            Envelope maxEnv = new Envelope(
                    -180.0,-90.0,
                    180.0,90.0 );
            return bbox.intersection(maxEnv);
           
        } else if(reqSRS.equalsIgnoreCase("EPSG:900913")) {
            bbox.expandBy(bbox.getWidth() / 100, bbox.getHeight() / 100);
            Envelope maxEnv = new Envelope(
                    -20037508.33, -20037508.33,
                    20037508.33, 20037508.33 );
            return bbox.intersection(maxEnv);
        }
        return bbox;
    }
}
TOP

Related Classes of org.geoserver.wms.DefaultWebMapService

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.