Package org.geoserver.kml.decorator

Source Code of org.geoserver.kml.decorator.PlacemarkStyleDecoratorFactory$PlacemarkStyleDecorator

/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.kml.decorator;

import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.kml.KmlEncodingContext;
import org.geoserver.kml.icons.IconProperties;
import org.geoserver.kml.icons.IconPropertyExtractor;
import org.geoserver.kml.icons.IconPropertyInjector;
import org.geoserver.wms.WMSInfo;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.renderer.style.ExpressionExtractor;
import org.geotools.styling.ExternalGraphic;
import org.geotools.styling.Fill;
import org.geotools.styling.Font;
import org.geotools.styling.LineSymbolizer;
import org.geotools.styling.PointSymbolizer;
import org.geotools.styling.PolygonSymbolizer;
import org.geotools.styling.Stroke;
import org.geotools.styling.Symbolizer;
import org.geotools.styling.TextSymbolizer;
import org.geotools.util.logging.Logging;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.Expression;
import org.opengis.style.GraphicalSymbol;

import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;

import de.micromata.opengis.kml.v_2_2_0.Feature;
import de.micromata.opengis.kml.v_2_2_0.Icon;
import de.micromata.opengis.kml.v_2_2_0.IconStyle;
import de.micromata.opengis.kml.v_2_2_0.LabelStyle;
import de.micromata.opengis.kml.v_2_2_0.LineStyle;
import de.micromata.opengis.kml.v_2_2_0.Placemark;
import de.micromata.opengis.kml.v_2_2_0.PolyStyle;
import de.micromata.opengis.kml.v_2_2_0.Style;

/**
* Encodes the SLD styles into KML corresponding styles and adds them to the Placemark
*
* @author Andrea Aime - GeoSolutions
*/
public class PlacemarkStyleDecoratorFactory implements KmlDecoratorFactory {

    public KmlDecorator getDecorator(Class<? extends Feature> featureClass,
            KmlEncodingContext context) {
        // this decorator makes sense only for WMS
        if(!(context.getService() instanceof WMSInfo)) {
            return null;
        }
       
        if (Placemark.class.isAssignableFrom(featureClass)) {
            return new PlacemarkStyleDecorator();
        } else {
            return null;
        }
    }

    static class PlacemarkStyleDecorator implements KmlDecorator {

        static final Logger LOGGER = Logging.getLogger(PlacemarkStyleDecorator.class);

        FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();

        @Override
        public Feature decorate(Feature feature, KmlEncodingContext context) {
            Placemark pm = (Placemark) feature;
            // while it's possible to have more than one style object, GE will only paint
            // the first one
            Style style = pm.createAndAddStyle();
            List<Symbolizer> symbolizers = context.getCurrentSymbolizers();
            SimpleFeature sf = context.getCurrentFeature();
            if (symbolizers.size() > 0 && sf.getDefaultGeometry() != null) {
                // sort by point, text, line and polygon
                Map<Class, List<Symbolizer>> classified = classifySymbolizers(symbolizers);

                // if no point symbolizers, create a default one
                List<Symbolizer> points = classified.get(PointSymbolizer.class);
                if (points.size() == 0) {
                    if(context.isDescriptionEnabled()) {
                        setDefaultIconStyle(style, sf, context);
                    }
                } else {
                    org.geotools.styling.Style wholeStyle = context.getCurrentLayer().getStyle();
                    IconProperties properties =
                        IconPropertyExtractor.extractProperties(wholeStyle, sf);
                    setIconStyle(style, wholeStyle, properties, context);
                }

                // handle label styles
                List<Symbolizer> texts = classified.get(TextSymbolizer.class);
                if (texts.size() == 0) {
                    if(context.isDescriptionEnabled()) {
                        setDefaultLabelStyle(style);
                    }
                } else {
                    // the XML schema allows only one text style, follow painter's model
                    // and set the last one
                    TextSymbolizer lastTextSymbolizer = (TextSymbolizer) texts.get(texts.size() - 1);
                    setLabelStyle(style, sf, lastTextSymbolizer);
                }

                // handle line styles
                List<Symbolizer> lines = classified.get(LineSymbolizer.class);
                // the XML schema allows only one line style, follow painter's model
                // and set the last one
                if(lines.size() > 0) {
                    LineSymbolizer lastLineSymbolizer = (LineSymbolizer) lines.get(lines.size() - 1);
                    setLineStyle(style, sf, lastLineSymbolizer.getStroke());
                }

                // handle polygon styles
                boolean forceOutiline = lines.size() == 0;
                List<Symbolizer> polygons = classified.get(PolygonSymbolizer.class);
                if(polygons.size() > 0) {
                    // the XML schema allows only one polygon style, follow painter's model
                    // and set the last one
                    PolygonSymbolizer lastPolygonSymbolizer = (PolygonSymbolizer) polygons.get(polygons.size() - 1);
                    setPolygonStyle(style, sf, lastPolygonSymbolizer, forceOutiline);
                }
            }

            return feature;
        }

        private Map<Class, List<Symbolizer>> classifySymbolizers(List<Symbolizer> symbolizers) {
            Map<Class, List<Symbolizer>> result = new HashMap<Class, List<Symbolizer>>();
            result.put(PointSymbolizer.class, new ArrayList<Symbolizer>());
            result.put(LineSymbolizer.class, new ArrayList<Symbolizer>());
            result.put(PolygonSymbolizer.class, new ArrayList<Symbolizer>());
            result.put(TextSymbolizer.class, new ArrayList<Symbolizer>());

            for (Symbolizer s : symbolizers) {
                if (s instanceof PointSymbolizer) {
                    result.get(PointSymbolizer.class).add(s);
                } else if (s instanceof LineSymbolizer) {
                    result.get(LineSymbolizer.class).add(s);
                } else if (s instanceof PolygonSymbolizer) {
                    result.get(PolygonSymbolizer.class).add(s);
                } else if (s instanceof TextSymbolizer) {
                    result.get(TextSymbolizer.class).add(s);
                } else {
                    throw new IllegalArgumentException("Unrecognized symbolizer type: " + s);
                }
            }

            return result;
        }

        protected void setDefaultIconStyle(Style style, SimpleFeature feature,
                KmlEncodingContext context) {
            // figure out if line or polygon
            boolean line = feature.getDefaultGeometry() != null
                    && (feature.getDefaultGeometry() instanceof LineString || feature
                            .getDefaultGeometry() instanceof MultiLineString);
            boolean poly = feature.getDefaultGeometry() != null
                    && (feature.getDefaultGeometry() instanceof Polygon || feature
                            .getDefaultGeometry() instanceof MultiPolygon);

            // Final pre-flight check
            if (!line && !poly) {
                LOGGER.log(Level.FINER, "Unexpectedly entered encodeDefaultIconStyle() "
                        + "with something that does not have a multipoint geometry.");
                return;
            }

            IconStyle is = style.createAndSetIconStyle();
            // make transparent if they ask for attributes, since we'll have a label
            if (context.isDescriptionEnabled()) {
                is.setColor("00ffffff");
            }
            // if line or polygon scale the label
            if (line || poly) {
                is.setScale(0.4);
            }
            String imageURL = "http://icons.opengeo.org/markers/icon-"
                    + (poly ? "poly.1" : "line.1") + ".png";
            Icon icon = is.createAndSetIcon();
            icon.setHref(imageURL);
            icon.setViewBoundScale(1);
        }

        /**
         * Encodes a KML IconStyle from a point style and symbolizer.
         */
        protected void setIconStyle(Style style, org.geotools.styling.Style sld,
            IconProperties properties, KmlEncodingContext context) {
            if (context.isLiveIcons() || properties.isExternal()) {
                setLiveIconStyle(style, sld, properties, context);
            } else {
                setInlineIconStyle(style, sld, properties, context);
            }
        }

        protected void setInlineIconStyle(Style style, org.geotools.styling.Style sld,
            IconProperties properties, KmlEncodingContext context) {
            final String name = properties.getIconName(sld);

            Map<String,org.geotools.styling.Style> iconStyles = context.getIconStyles();
            if (!iconStyles.containsKey(name)) {
                final org.geotools.styling.Style injectedStyle =
                    IconPropertyInjector.injectProperties(sld, properties.getProperties());

                iconStyles.put(name, injectedStyle);
            }
            final Double scale = properties.getScale();
            final String path = "icons/" + name + ".png";

            IconStyle is = style.createAndSetIconStyle();
            if (properties.getHeading() != null) {
                is.setHeading(0.0);
            }
            if (scale != null) {
                is.setScale(scale);
            }

            Icon icon = is.createAndSetIcon();
            icon.setHref(path);
        }

        protected void setLiveIconStyle(Style style, org.geotools.styling.Style sld,
            IconProperties properties, KmlEncodingContext context) {
            final Double opacity = properties.getOpacity();
            final Double scale = properties.getScale();
            final Double heading = properties.getHeading();

            IconStyle is = style.createAndSetIconStyle();
           
            if (opacity != null) {
                is.setColor(colorToHex(Color.WHITE, opacity));
            }
           
            if (scale != null) {
                is.setScale(scale);
            }
           
            if (heading != null) {
                is.setHeading(heading);
            }
           
            // Get the name of the workspace
           
            WorkspaceInfo ws =
                context.getWms().getCatalog().getStyleByName(sld.getName()).getWorkspace();
            String wsName = null;
            if(ws!=null) wsName = ws.getName();

           
            Icon icon = is.createAndSetIcon();
            icon.setHref(properties.href(context.getMapContent().getRequest().getBaseUrl(),
                wsName, sld.getName()));
        }

        /**
         * Encodes a transparent KML LabelStyle
         */
        protected void setDefaultLabelStyle(Style style) {
            LabelStyle ls = style.createAndSetLabelStyle();
            ls.setColor("00ffffff");
        }

        protected void setLabelStyle(Style style, SimpleFeature feature, TextSymbolizer symbolizer) {
            LabelStyle ls = style.createAndSetLabelStyle();
            double scale = 1;
            Font font = symbolizer.getFont();
            if(font != null && font.getSize() != null) {
                // we make the scale proportional to the normal font size
                double size = font.getSize().evaluate(feature, Double.class);
                scale = Math.round(size / Font.DEFAULT_FONTSIZE * 100) / 100.0;
            }
            ls.setScale(scale);

            Fill fill = symbolizer.getFill();
            if (fill != null) {
                Double opacity = fill.getOpacity().evaluate(feature, Double.class);
                if (opacity == null || Double.isNaN(opacity)) {
                    opacity = 1.0;
                }
                Color color = fill.getColor().evaluate(feature, Color.class);
                ls.setColor(colorToHex(color, opacity));
            } else {
                ls.setColor("ffffffff");
            }
        }

        /**
         * Encodes a KML IconStyle + PolyStyle from a polygon style and symbolizer.
         */
        protected void setPolygonStyle(Style style, SimpleFeature feature,
                PolygonSymbolizer symbolizer, boolean forceOutline) {
            // if stroke specified add line style as well (it has to be before the fill, otherwise
            // we'll get a white filling...)
            if (symbolizer.getStroke() != null) {
                setLineStyle(style, feature, symbolizer.getStroke());
            }

            // fill
            PolyStyle ps = style.createAndSetPolyStyle();
            Fill fill = symbolizer.getFill();
            if (fill != null) {
                // get opacity
                Double opacity = fill.getOpacity().evaluate(feature, Double.class);
                if (opacity == null || Double.isNaN(opacity)) {
                    opacity = 1.0;
                }

                Color color = (Color) fill.getColor().evaluate(feature, Color.class);
                ps.setColor(colorToHex(color, opacity));
            } else {
                // make it transparent
                ps.setColor("00aaaaaa");
            }

            // outline
            if (symbolizer.getStroke() != null || forceOutline) {
                ps.setOutline(true);
            }
        }

        /**
         * Encodes a KML IconStyle + LineStyle from a polygon style and symbolizer.
         */
        protected void setLineStyle(Style style, SimpleFeature feature, Stroke stroke) {
            LineStyle ls = style.createAndSetLineStyle();

            if (stroke != null) {
                // opacity
                Double opacity = stroke.getOpacity().evaluate(feature, Double.class);
                if (opacity == null || Double.isNaN(opacity)) {
                    opacity = 1.0;
                }

                Color color = null;
                Expression sc = stroke.getColor();
                if (sc != null) {
                    color = (Color) sc.evaluate(feature, Color.class);
                }
                if (color == null) {
                    color = Color.DARK_GRAY;
                }
                ls.setColor(colorToHex(color, opacity));

                // width
                Double width = null;
                Expression sw = stroke.getWidth();
                if (sw != null) {
                    width = sw.evaluate(feature, Double.class);
                }
                if (width == null) {
                    width = 1d;
                }
                ls.setWidth(width);
            } else {
                // default
                ls.setColor("ffaaaaaa");
                ls.setWidth(1);
            }
        }

        private ExternalGraphic getExternalGraphic(PointSymbolizer symbolizer) {
            for (GraphicalSymbol s : symbolizer.getGraphic().graphicalSymbols()) {
                if (s instanceof ExternalGraphic) {
                    return (ExternalGraphic) s;
                }
            }

            return null;
        }

        /**
         * Does value substitution on a URL with embedded CQL expressions
         *
         * @param strLocation the URL as a string, possibly with expressions
         * @param feature the feature providing the context in which the expressions are evaluated
         * @return a string containing the final URL
         */
        protected String evaluateDynamicSymbolizer(String strLocation, SimpleFeature feature) {
            if (strLocation == null)
                return null;

            // parse the eventual ${cqlExpression} embedded in the URL
            Expression location;
            try {
                location = ExpressionExtractor.extractCqlExpressions(strLocation);
            } catch (IllegalArgumentException e) {
                // in the unlikely event that a URL is using one of the chars reserved for
                // ${cqlExpression}
                // let's try and use the location as a literal
                if (LOGGER.isLoggable(Level.SEVERE))
                    LOGGER.log(Level.SEVERE, "Could not parse cql expressions out of "
                            + strLocation, e);
                location = ff.literal(strLocation);
            }

            return location.evaluate(feature, String.class);
        }
       
        /**
         * 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".
         */
        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();
        }
       
        /**
         * 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
         */
        String intToHex(int i) {
            String prelim = Integer.toHexString(i);

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

            return prelim;
        }

    }

}
TOP

Related Classes of org.geoserver.kml.decorator.PlacemarkStyleDecoratorFactory$PlacemarkStyleDecorator

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.