Package org.geoserver.kml

Source Code of org.geoserver.kml.KMLUtils

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

import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.geoserver.catalog.Catalog;
import org.geoserver.config.GeoServer;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.ServiceException;
import org.geoserver.wms.GetMapRequest;
import org.geoserver.wms.MapLayerInfo;
import org.geoserver.wms.WMS;
import org.geoserver.wms.WMSMapContext;
import org.geoserver.wms.WMSRequests;
import org.geotools.data.DataUtilities;
import org.geotools.data.Query;
import org.geotools.data.crs.ReprojectFeatureResults;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureTypes;
import org.geotools.filter.IllegalFilterException;
import org.geotools.filter.visitor.SimplifyingFilterVisitor;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.MapLayer;
import org.geotools.referencing.CRS;
import org.geotools.renderer.lite.LiteFeatureTypeStyle;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.Rule;
import org.geotools.styling.Style;
import org.geotools.util.Converters;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.AttributesImpl;

import com.vividsolutions.jts.geom.Envelope;


/**
* Some convenience methods used by the kml transformers.
*
* @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
*/
public class KMLUtils {
    /**
     * logger
     */
    static Logger LOGGER = org.geotools.util.logging.Logging
            .getLogger("org.geoserver.kml");

    /**
     * Tolerance used to compare doubles for equality
     */
    static final double TOLERANCE = 1e-6;

    private static final CoordinateReferenceSystem WGS84;
    private static final int RULES = 0;
    private static final int ELSE_RULES = 1;

    static {
        try {
            WGS84 = CRS.decode("EPSG:4326");
        } catch (Exception e) {
            throw new RuntimeException(
                    "Cannot decode EPSG:4326, the CRS subsystem must be badly broken...");
        }
    }

    public final static Envelope WORLD_BOUNDS_WGS84 = new Envelope(-180,180,-90,90);   
   
    private final static double[] TILE_RESOLUTIONS = new double[100];

    static {
        for (int i = 0; i < TILE_RESOLUTIONS.length; i++) {
            TILE_RESOLUTIONS[i] = WORLD_BOUNDS_WGS84.getWidth() / ((0x01 << i) * 256);
        }
    }

    /**
     * Factory used to create filter objects
     */
    private static FilterFactory filterFactory = (FilterFactory) CommonFactoryFinder
            .getFilterFactory(null);

    /**
     * Encodes the url of a GetMap request from a map context + map layer.
     * <p>
     * If the <tt>mapLayer</tt> argument is <code>null</code>, the request is
     * made including all layers in the <tt>mapContexT</tt>.
     * </p>
     * <p>
     * If the <tt>bbox</tt> argument is <code>null</code>. {@link WMSMapContext#getAreaOfInterest()}
     * is used for the bbox parameter.
     * </p>
     *
     * @param mapContext The map context.
     * @param mapLayer The Map layer, may be <code>null</code>.
     * @param layerIndex The index of the layer in the request.
     * @param bbox The bounding box of the request, may be <code>null</code>.
     * @param kvp Additional or overiding kvp parameters, may be <code>null</code>
     * @param tile Flag controlling whether the request should be made against tile cache
     * @param geoserver
     *
     * @return The full url for a getMap request.
     * @deprecated use {@link WMSRequests#getGetMapUrl(WMSMapContext, MapLayer, Envelope, String[])}
     */
    public static String getMapUrl(
            WMSMapContext mapContext,
            MapLayer mapLayer,
            int layerIndex,
            Envelope bbox,
            String[] kvp,
            boolean tile, GeoServer geoserver) {
      
        if ( tile ) {
            org.geoserver.wms.GetMapRequest request = mapContext.getRequest();
            return WMSRequests.getTiledGetMapUrl(geoserver, request, mapLayer, layerIndex, bbox, kvp );
        }
       
        return WMSRequests.getGetMapUrl(
                    mapContext.getRequest(),
                    mapLayer,
                    layerIndex,
                    bbox,
                    kvp
                );
    }

    /**
     * Encodes the url of a GetMap request from a map context + map layer.
     * <p>
     * If the <tt>mapLayer</tt> argument is <code>null</code>, the request is
     * made including all layers in the <tt>mapContexT</tt>.
     * </p>
     * @param mapContext The map context.
     * @param mapLayer The Map layer, may be <code>null</code>
     * @param layerIndex The index of the layer in the request.
     * @param kvp Additional or overidding kvp parameters, may be <code>null</code>
     * @param tile Flag controlling wether the request should be made against tile cache
     * @param geoserver
     *
     * @return The full url for a getMap request.
     * @deprecated use {@link WMSRequests#getGetMapUrl(WMSMapContext, MapLayer, int, Envelope, String[])}
     */
    public static String getMapUrl(WMSMapContext mapContext, MapLayer mapLayer, int layerIndex, boolean tile, GeoServer geoserver) {
        return getMapUrl(mapContext, mapLayer, layerIndex, mapContext.getAreaOfInterest(), null, tile, geoserver);
    }

    /**
     * Encodes the url for a GetLegendGraphic request from a map context + map layer.
     *
     * @param mapContext The map context.
     * @param mapLayer The map layer.
     * @param kvp Additional or overidding kvp parameters, may be <code>null</code>
     *
     * @return A map containing all the key value pairs for a GetLegendGraphic request.
     * @deprecated use {@link WMSRequests#getGetLegendGraphicUrl(WMSMapContext, MapLayer, String[])
     */
    public static String getLegendGraphicUrl(WMSMapContext mapContext, MapLayer mapLayer,
        String[] kvp) {
        return WMSRequests.getGetLegendGraphicUrl(mapContext.getRequest(), mapLayer, kvp);
    }

    /**
     * Creates sax attributes from an array of key value pairs.
     *
     * @param nameValuePairs Alternating key value pair array.
     *
     */
    public static Attributes attributes(String[] nameValuePairs) {
        AttributesImpl attributes = new AttributesImpl();

        for (int i = 0; i < nameValuePairs.length; i += 2) {
            String name = nameValuePairs[i];
            String value = nameValuePairs[i + 1];

            attributes.addAttribute("", name, name, "", value);
        }

        return attributes;
    }
   
    /**
     * Filters the rules of <code>featureTypeStyle</code> returnting only
     * those that apply to <code>feature</code>.
     * <p>
     * This method returns rules for which:
     * <ol>
     <li><code>rule.getFilter()</code> matches <code>feature</code>, or:
     *  <li>the rule defines an "ElseFilter", and the feature matches no
     *  other rules.
     * </ol>
     * This method returns an empty array in the case of which no rules
     * match.
     * </p>
     * @param featureTypeStyle The feature type style containing the rules.
     * @param feature The feature being filtered against.
     *
     */
    public static Rule[] filterRules(FeatureTypeStyle featureTypeStyle,
            SimpleFeature feature, double scaleDenominator) {
        Rule[] rules = featureTypeStyle.getRules();

        if ((rules == null) || (rules.length == 0)) {
            return new Rule[0];
        }

        ArrayList filtered = new ArrayList(rules.length);

        //process the rules, keep track of the need to apply an else filters
        boolean match = false;
        boolean hasElseFilter = false;

        for (int i = 0; i < rules.length; i++) {
            Rule rule = rules[i];
            LOGGER.finer(new StringBuffer("Applying rule: ").append(
                    rule.toString()).toString());

            //does this rule have an else filter
            if (rule.hasElseFilter()) {
                hasElseFilter = true;

                continue;
            }

            //is this rule within scale?
            if (!isWithInScale(rule, scaleDenominator)) {
                continue;
            }

            //does this rule have a filter which applies to the feature
            Filter filter = rule.getFilter();

            if ((filter == null) || filter.evaluate(feature)) {
                match = true;

                filtered.add(rule);
            }
        }

        //if no rules mached the feautre, re-run through the rules applying
        // any else filters
        if (!match && hasElseFilter) {
            //loop through again and apply all the else rules
            for (int i = 0; i < rules.length; i++) {
                Rule rule = rules[i];

                //is this rule within scale?
                if (!isWithInScale(rule, scaleDenominator)) {
                    continue;
                }

                if (rule.hasElseFilter()) {
                    filtered.add(rule);
                }
            }
        }

        return (Rule[]) filtered.toArray(new Rule[filtered.size()]);
    }

    /**
     * Checks if a rule can be triggered at the current scale level
     *
     * @param r
     *            The rule
     * @return true if the scale is compatible with the rule settings
     */
    public static boolean isWithInScale(Rule r, double scaleDenominator) {
        return ((r.getMinScaleDenominator() - TOLERANCE) <= scaleDenominator)
                && ((r.getMaxScaleDenominator() + TOLERANCE) > scaleDenominator);
    }
   
    public static int findZoomLevel(Envelope extent){
        double resolution = Math.max(extent.getWidth()/256d, extent.getHeight() / 256d);
       
        int i;
       
        for (i = 1; i < TILE_RESOLUTIONS.length; i++){
            if (resolution > TILE_RESOLUTIONS[i]) {
                i--;
                break;
            }
        }
       
        return i;
    }

    public static Envelope expandToTile(Envelope extent){
        double resolution = Math.max(extent.getWidth() / 256d, extent.getHeight() / 256d);
       
        int i = findZoomLevel(extent);
        
        while (i > 0) {
            resolution = TILE_RESOLUTIONS[i];

            double tilelon = resolution * 256;
            double tilelat = resolution * 256;

            double lon0 = extent.getMinX() - WORLD_BOUNDS_WGS84.getMinX();
            double lon1 = extent.getMaxX() - WORLD_BOUNDS_WGS84.getMinX();

            int col0 = (int) Math.floor(lon0 / tilelon);
            int col1 = (int) Math.floor((lon1 / tilelon) - 1E-9);

            double lat0 = extent.getMinY() - WORLD_BOUNDS_WGS84.getMinY();
            double lat1 = extent.getMaxY() - WORLD_BOUNDS_WGS84.getMinY();

            int row0 = (int) Math.floor(lat0 / tilelat);
            int row1 = (int) Math.floor((lat1 / tilelat) - 1E-9);

            if ((col0 == col1) && (row0 == row1)) {
                double tileoffsetlon = WORLD_BOUNDS_WGS84.getMinX() + (col0 * tilelon);
                double tileoffsetlat = WORLD_BOUNDS_WGS84.getMinY() + (row0 * tilelat);

                return new Envelope(tileoffsetlon, tileoffsetlon + tilelon, tileoffsetlat,
                        tileoffsetlat + tilelat);
            } else {
                i--;
            }
        }
       
        return WORLD_BOUNDS_WGS84;
    }
   
    /**
     * Utility method to convert an int into hex, padded to two characters.
     * handy for generating colour strings.
     *
     * @param i Int to convert
     * @return String a two character hex representation of i
     * NOTE: this is a utility method and should be put somewhere more useful.
     */
    public static String intToHex(int i) {
        String prelim = Integer.toHexString(i);

        if (prelim.length() < 2) {
            prelim = "0" + prelim;
        }

        return prelim;
    }

    /**
     * Utility method to convert a Color and opacity (0,1.0) into a KML
     * color ref.
     *
     * @param c The color to convert.
     * @param opacity Opacity / alpha, double from 0 to 1.0.
     *
     * @return A String of the form "AABBGGRR".
     */
    public static String colorToHex(Color c, double opacity) {
        return new StringBuffer().append(
                intToHex(new Float(255 * opacity).intValue())).append(
                intToHex(c.getBlue())).append(intToHex(c.getGreen())).append(
                intToHex(c.getRed())).toString();
    }

    /**
     * Filters the feature type styles of <code>style</code> returning only
     * those that apply to <code>featureType</code>
     * <p>
     * This methods returns feature types for which
     * <code>featureTypeStyle.getFeatureTypeName()</code> matches the name
     * of the feature type of <code>featureType</code>, or matches the name of
     * any parent type of the feature type of <code>featureType</code>. This
     * method returns an empty array in the case of which no rules match.
     * </p>
     * @param style The style containing the feature type styles.
     * @param featureType The feature type being filtered against.
     *
     */
    public static FeatureTypeStyle[] filterFeatureTypeStyles(Style style,
            SimpleFeatureType ftype) {
        List<FeatureTypeStyle> featureTypeStyles = style.featureTypeStyles();

        if (featureTypeStyles == null || featureTypeStyles.isEmpty()) {
            return new FeatureTypeStyle[0];
        }

        ArrayList<FeatureTypeStyle> filtered = new ArrayList<FeatureTypeStyle>(featureTypeStyles.size());
        for(FeatureTypeStyle fts : featureTypeStyles) {
            String ftName = fts.getFeatureTypeName();
           
            // yeah, ugly, but exactly the same code as the streaming renderer... we should
            // really factor out this style massaging in a delegate object (StyleOverlord)
            if(fts.featureTypeNames().isEmpty() || ((ftype.getName().getLocalPart() != null)
                    && (ftype.getName().getLocalPart().equalsIgnoreCase(ftName) ||
                            FeatureTypes.isDecendedFrom(ftype, null, ftName)))) {
                filtered.add(fts);
            }
        }

        return (FeatureTypeStyle[]) filtered.toArray(new FeatureTypeStyle[filtered.size()]);
    }

    /**
     * Loads the feature collection based on the current styling and the scale denominator.
     * If no feature is going to be returned a null feature collection will be returned instead
     * @param featureSource
     * @param layer
     * @param mapContext
     * @param wms
     * @param scaleDenominator
     * @return
     * @throws Exception
     */
    public static SimpleFeatureCollection loadFeatureCollection(
            SimpleFeatureSource featureSource,
            MapLayer layer, WMSMapContext mapContext, WMS wms, double scaleDenominator) throws Exception {
        SimpleFeatureType schema = featureSource.getSchema();

        Envelope envelope = mapContext.getAreaOfInterest();
        ReferencedEnvelope aoi = new ReferencedEnvelope(envelope, mapContext
                .getCoordinateReferenceSystem());
        CoordinateReferenceSystem sourceCrs = schema
                .getCoordinateReferenceSystem();

        boolean reprojectBBox = (sourceCrs != null)
                && !CRS.equalsIgnoreMetadata(
                        aoi.getCoordinateReferenceSystem(), sourceCrs);
        if (reprojectBBox) {
            aoi = aoi.transform(sourceCrs, true);
        }

        Filter filter = createBBoxFilter(schema, aoi);

        // now build the query using only the attributes and the bounding
        // box needed
        Query q = new Query(schema.getTypeName());
        q.setFilter(filter);

        // now, if a definition query has been established for this layer,
        // be sure to respect it by combining it with the bounding box one.
        Query definitionQuery = layer.getQuery();

        if (definitionQuery != Query.ALL) {
            if (q == Query.ALL) {
                q = (Query) definitionQuery;
            } else {
                q = (Query) DataUtilities.mixQueries(definitionQuery, q,
                        "KMLEncoder");
            }
        }

        // handle startIndex requested by client query
        q.setStartIndex(definitionQuery.getStartIndex());

        // check the regionating strategy
        RegionatingStrategy regionatingStrategy = null;
        String stratname = (String) mapContext.getRequest().getFormatOptions()
                .get("regionateBy");
        if (("auto").equals(stratname)) {
            Catalog catalog = wms.getGeoServer().getCatalog();
            Name name = layer.getFeatureSource().getName();
            stratname = catalog.getFeatureTypeByName(name).getMetadata().get( "kml.regionateStrategy",String.class );
            if (stratname == null || "".equals( stratname ) ){
                stratname = "best_guess";
                LOGGER.log(
                        Level.FINE,
                        "No default regionating strategy has been configured in " + name
                        + "; using automatic best-guess strategy."
                    );
            }
        }

        if (stratname != null) {
            regionatingStrategy = findStrategyByName(stratname);
           
            // if a strategy was specified but we did not find it, let the user
            // know
            if (regionatingStrategy == null)
                throw new ServiceException("Unknown regionating strategy " + stratname);
        }

        // try to load less features by leveraging regionating strategy and the
        // SLD
        Filter regionatingFilter = Filter.INCLUDE;

        if (regionatingStrategy != null)
            regionatingFilter = regionatingStrategy.getFilter(mapContext, layer);

        Filter ruleFilter = summarizeRuleFilters(getLayerRules(featureSource
                .getSchema(), layer.getStyle()), scaleDenominator);
        Filter finalFilter = joinFilters(q.getFilter(), ruleFilter,
                regionatingFilter);
        if(finalFilter == Filter.EXCLUDE) {
            // if we don't have any feature to return
            return null;
        }
        q.setFilter(finalFilter);

        // make sure we output in 4326 since that's what KML mandates
        if (sourceCrs != null && !CRS.equalsIgnoreMetadata(WGS84, sourceCrs)) {
            return new ReprojectFeatureResults(featureSource.getFeatures(q),
                    WGS84);
        }

        return featureSource.getFeatures(q);
    }

    public static RegionatingStrategy findStrategyByName(String name) {
        List<RegionatingStrategyFactory> factories = GeoServerExtensions
            .extensions(RegionatingStrategyFactory.class);
        Iterator<RegionatingStrategyFactory> it = factories.iterator();
        while (it.hasNext()) {
            RegionatingStrategyFactory factory = it.next();
            if (factory.canHandle(name)) {
                return factory.createStrategy();
            }
        }

        return null;
    }

    /**
     * Creates the bounding box filters (one for each geometric attribute)
     * needed to query a <code>MapLayer</code>'s feature source to return
     * just the features for the target rendering extent
     *
     * @param schema
     *            the layer's feature source schema
     * @param bbox
     *            the expression holding the target rendering bounding box
     * @return an or'ed list of bbox filters, one for each geometric attribute
     *         in <code>attributes</code>. If there are just one geometric
     *         attribute, just returns its corresponding
     *         <code>GeometryFilter</code>.
     * @throws IllegalFilterException
     *             if something goes wrong creating the filter
     */
    private static Filter createBBoxFilter(SimpleFeatureType schema,
            Envelope bbox) throws IllegalFilterException {
        List filters = new ArrayList();
        for (int j = 0; j < schema.getAttributeCount(); j++) {
            AttributeDescriptor attType = schema.getDescriptor(j);

            if (attType instanceof GeometryDescriptor) {
                Filter gfilter = filterFactory.bbox(attType.getLocalName(),
                        bbox.getMinX(), bbox.getMinY(), bbox.getMaxX(), bbox
                                .getMaxY(), null);
                filters.add(gfilter);
            }
        }

        if (filters.size() == 0)
            return Filter.INCLUDE;
        else if (filters.size() == 1)
            return (Filter) filters.get(0);
        else
            return filterFactory.or(filters);
    }

    private static List[] getLayerRules(SimpleFeatureType ftype, Style style) {
        List[] result = new List[] { new ArrayList(), new ArrayList() };

        final String typeName = ftype.getTypeName();
       
        FeatureTypeStyle[] featureStyles = filterFeatureTypeStyles(style, ftype);
        final int length = featureStyles.length;
        for (int i = 0; i < length; i++) {
            // getting feature styles
            FeatureTypeStyle fts = featureStyles[i];

            // get applicable rules at the current scale
            Rule[] ftsRules = fts.getRules();
            for (int j = 0; j < ftsRules.length; j++) {
                // getting rule
                Rule r = ftsRules[j];

                if (r.hasElseFilter()) {
                    result[ELSE_RULES].add(r);
                } else {
                    result[RULES].add(r);
                }
            }
        }

        return result;
    }

    private static Filter joinFilters(Filter... filters) {
        if(filters == null || filters.length == 0) {
            return Filter.EXCLUDE;
        }
       
        Filter result = null;
        if(filters.length > 0) {
            FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
            result = ff.and(Arrays.asList(filters));
        } else if(filters.length == 1) {
            result = filters[0];
        }
       
        SimplifyingFilterVisitor visitor = new SimplifyingFilterVisitor();
        return (Filter) result.accept(visitor, null);
    }

    /**
     * Summarizes, when possible, the rule filters into one.
     *
     * @param rules
     * @param originalFiter
     * @param scaleDenominator The actual scale denominator, or a value <= 0 if no scale denominator
     *                         checks have to be performed
     * @return
     */
    private static Filter summarizeRuleFilters(List[] rules, double scaleDenominator) {
        if (rules[RULES].size() == 0 && rules[ELSE_RULES].size() > 0)
            return Filter.EXCLUDE;

        List filters = new ArrayList();
        for (Iterator it = rules[RULES].iterator(); it.hasNext();) {
            Rule rule = (Rule) it.next();
            if(scaleDenominator <= 0 || isWithInScale(rule, scaleDenominator)) {
                // if there is a single rule asking for all filters, we have to
                // return everything that the original filter returned already
                if (rule.getFilter() == null
                        || Filter.INCLUDE.equals(rule.getFilter()))
                    return Filter.INCLUDE;
                else
                    filters.add(rule.getFilter());
            }
        }

        if(filters.size() > 0) {
            FilterFactory ff = CommonFactoryFinder.getFilterFactory(null);
            return ff.or(filters);
        } else {
            return Filter.EXCLUDE;
        }
    }
   
    /**
     * Returns the superoverlay mode (either specified in the request, or the default one)
     * @return
     */
    public static String getSuperoverlayMode(GetMapRequest request, WMS wms) {
        String overlayMode = (String) request.getFormatOptions().get("superoverlay_mode");
        if(overlayMode != null) {
            return overlayMode;
        }
       
        overlayMode = (String) request.getFormatOptions().get("overlayMode");
        if(overlayMode != null) {
            return overlayMode;
        } else {
            return wms.getKmlSuperoverlayMode();
        }
    }
   
    /**
     * Returns the the kmattr value (either specified in the request, or the default one)
     * @param mapContext
     * @return
     */
    public static boolean getKMAttr(GetMapRequest request, WMS wms) {
        Object kmattr = request.getFormatOptions().get("kmattr");
        if (kmattr != null) {
            return Converters.convert(kmattr, Boolean.class);
        } else {
            return wms.getKmlKmAttr();
        }
    }
   
    /**
     * Returns the the kmplacemark value (either specified in the request, or the default one)
     * @param mapContext
     * @return
     */
    public static boolean getKmplacemark(GetMapRequest request, WMS wms) {
        Object kmplacemark = request.getFormatOptions().get("kmplacemark");
        if (kmplacemark != null) {
            return Converters.convert(kmplacemark, Boolean.class);
        } else {
            return wms.getKmlPlacemark();
        }
    }
   
    /**
     * Returns the the kmscore value (either specified in the request, or the default one)
     * @param mapContext
     * @return
     */
    public static int getKmScore(GetMapRequest request, WMS wms ) {
        Object kmscore = request.getFormatOptions().get("kmscore");
        if (kmscore != null) {
            return Converters.convert(kmscore, Integer.class);
        } else {
            return wms.getKmScore();
        }
    }

   
    /**
     * Returns true if the request is GWC compatible
     * @param mapContext
     * @return
     */
    public static boolean isRequestGWCCompatible(GetMapRequest request, int layerIndex, WMS wms) {
        // check the kml params are the same as the defaults (GWC uses always the defaults)
        boolean requestKmAttr = KMLUtils.getKMAttr(request, wms);
        if(requestKmAttr != wms.getKmlKmAttr()) {
            return false;
        }
           
        boolean requestKmplacemark = KMLUtils.getKmplacemark(request, wms);
        if(requestKmplacemark != wms.getKmlPlacemark()) {
            return false;
        }
       
        int requestKmscore = KMLUtils.getKmScore(request, wms);
        if(requestKmscore != wms.getKmScore()) {
            return false;
        }
       
        // check the layer is local
        if(request.getLayers().get(layerIndex).getType() == MapLayerInfo.TYPE_REMOTE_VECTOR) {
            return false;
        }
       
        // check the layer is using the default style
        Style requestedStyle = request.getStyles().get(layerIndex);
        Style defaultStyle = request.getLayers().get(layerIndex).getDefaultStyle();
        if(!defaultStyle.equals(requestedStyle)) {
            return false;
        }
       
        // check there is no extra filtering applied to the layer
        List<Filter> filters = request.getFilter();
        if(filters != null && filters.size() > 0 && filters.get(layerIndex) != Filter.INCLUDE) {
            return false;
        }
       
        // no fiddling with antialiasing settings
        String antialias = (String) request.getFormatOptions().get("antialias");
        if(antialias != null && !"FULL".equalsIgnoreCase(antialias)) {
            return false;
        }
       
        // no custom palette
        if(request.getPalette() != null) {
            return false;
        }
       
        // no custom start index
        if(request.getStartIndex() != null && request.getStartIndex() != 0) {
            return false;
        }
       
        // no custom max features
        if(request.getMaxFeatures() != null) {
            return false;
        }
       
        // no sql view params
        if(request.getViewParams() != null && request.getViewParams().size() > 0) {
            return false;
        }
   
        // ok, it seems everything is the same as GWC cached it
        return true;
    }
   
    /**
     * Returns true if the request is GWC compatible
     * @param mapContext
     * @return
     */
    public static boolean isRequestGWCCompatible(WMSMapContext mapContext, MapLayer layer, WMS wms) {
        MapLayer[] layers = mapContext.getLayers();
        for(int i = 0; i < layers.length; i++) {
            if(layers[i] == layer) {
                return isRequestGWCCompatible(mapContext.getRequest(), i, wms);
            }
        }
        LOGGER.warning("Could not find map layer " + layer.getTitle() + " in the map context");
       
        return false;
    }

   
}
TOP

Related Classes of org.geoserver.kml.KMLUtils

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.