eu.esdihumboldt.hale.ui.style.StyleHelper.java Source code

Java tutorial

Introduction

Here is the source code for eu.esdihumboldt.hale.ui.style.StyleHelper.java

Source

/*
 * Copyright (c) 2012 Data Harmonisation Panel
 * 
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution. If not, see <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *     HUMBOLDT EU Integrated Project #030962
 *     Data Harmonisation Panel <http://www.dhpanel.eu>
 */

package eu.esdihumboldt.hale.ui.style;

import java.awt.Color;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;

import javax.annotation.Nullable;

import org.eclipse.ui.PlatformUI;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.NameImpl;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.Fill;
import org.geotools.styling.LineSymbolizer;
import org.geotools.styling.Mark;
import org.geotools.styling.PointSymbolizer;
import org.geotools.styling.PolygonSymbolizer;
import org.geotools.styling.Rule;
import org.geotools.styling.SLD;
import org.geotools.styling.Style;
import org.geotools.styling.StyleBuilder;
import org.geotools.styling.StyleFactory;
import org.geotools.styling.Symbolizer;
import org.opengis.filter.FilterFactory;

import com.google.common.collect.SetMultimap;

import eu.esdihumboldt.hale.common.instance.model.DataSet;
import eu.esdihumboldt.hale.common.schema.model.TypeDefinition;
import eu.esdihumboldt.hale.common.schema.model.constraint.type.AbstractFlag;
import eu.esdihumboldt.hale.ui.geometry.service.GeometrySchemaService;
import eu.esdihumboldt.hale.ui.style.service.internal.StylePreferences;

/**
 * Style helper methods
 * 
 * @author Simon Templer
 * @partner 01 / Fraunhofer Institute for Computer Graphics Research
 */
public abstract class StyleHelper {

    /**
     * Default fill opacity
     */
    public static final double DEFAULT_FILL_OPACITY = 0.4;

    private static final StyleBuilder styleBuilder = new StyleBuilder();

    private static final StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory(null);

    private static final FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory(null);

    /**
     * Returns a default style for the given type.
     * 
     * @param typeDef the type definition
     * @param dataSet the data set (if known)
     * @return the style
     */
    public static FeatureTypeStyle getDefaultStyle(TypeDefinition typeDef, @Nullable DataSet dataSet) {
        //      GeometrySchemaService gss = (GeometrySchemaService) PlatformUI.getWorkbench().getService(GeometrySchemaService.class);
        //      List<QName> geomPath = gss.getDefaultGeometry(typeDef);
        // TODO determine default style from default geometry?

        Color defColor;
        if (dataSet != null) {
            defColor = StylePreferences.getDefaultColor(dataSet);
        } else {
            defColor = Color.DARK_GRAY;
        }
        int defWidth = StylePreferences.getDefaultWidth();

        FeatureTypeStyle result;

        // XXX for now create a polygon style in any case, as it contains fill
        // and stroke

        //      if (type != null) {
        //         if (type.isAssignableFrom(Polygon.class)
        //               || type.isAssignableFrom(MultiPolygon.class)) {
        result = createPolygonStyle(defColor, defWidth);
        //         } else if (type.isAssignableFrom(LineString.class)
        //               || type.isAssignableFrom(MultiLineString.class)) {
        //            result = createLineStyle(defColor, defWidth);
        //         } else {
        //            result = createPointStyle(defColor, defWidth);
        //         }
        //      }
        //      else {
        //         result = createPointStyle(defColor, defWidth);
        //      }

        // XXX StyleBuilder does not support feature type names with namespace
        //      QName name = getFeatureTypeName(typeDef);
        //      result.featureTypeNames().add(new NameImpl(name.getNamespaceURI(), name.getLocalPart()));
        result.featureTypeNames().add(new NameImpl(getFeatureTypeName(typeDef)));

        return result;
    }

    /**
     * Returns a default style for the given type.
     * 
     * @param dataSetTypes type definitions associated to their data set
     * @return the style
     */
    public static Style getRandomStyles(SetMultimap<DataSet, TypeDefinition> dataSetTypes) {
        int defWidth = StylePreferences.getDefaultWidth();

        Style style = styleFactory.createStyle();

        for (Entry<DataSet, TypeDefinition> entry : dataSetTypes.entries()) {
            DataSet dataSet = entry.getKey();
            TypeDefinition typeDef = entry.getValue();

            FeatureTypeStyle fts;

            // TODO based on default geometry?
            // polygon is always OK as it contains stroke and fill

            // Color color = generateRandomColor(Color.WHITE);
            float saturation;
            float brightness;
            switch (dataSet) {
            case TRANSFORMED:
                saturation = 0.8f;
                brightness = 0.6f;
                break;
            case SOURCE:
            default:
                saturation = 0.75f;
                brightness = 0.8f;
                break;
            }
            Color color = generateRandomColor(saturation, brightness);
            fts = createPolygonStyle(color, defWidth);

            fts.featureTypeNames().add(new NameImpl(getFeatureTypeName(typeDef)));

            style.featureTypeStyles().add(fts);
        }

        return style;
    }

    /**
     * Returns a default style for the given type.
     * 
     * @param dataSetTypes type definitions associated to their data set
     * @return the style
     */
    public static Style getSpectrumStyles(SetMultimap<DataSet, TypeDefinition> dataSetTypes) {
        int defWidth = StylePreferences.getDefaultWidth();

        Style style = styleFactory.createStyle();

        GeometrySchemaService gss = PlatformUI.getWorkbench().getService(GeometrySchemaService.class);

        for (DataSet dataSet : dataSetTypes.keySet()) {
            float saturation;
            float brightness;
            switch (dataSet) {
            case TRANSFORMED:
                saturation = 0.8f;
                brightness = 0.6f;
                break;
            case SOURCE:
            default:
                saturation = 0.75f;
                brightness = 0.8f;
                break;
            }

            Set<TypeDefinition> types = new HashSet<>(dataSetTypes.get(dataSet));
            Iterator<TypeDefinition> it = types.iterator();
            while (it.hasNext()) {
                TypeDefinition type = it.next();
                // remove invalid types
                if (type.getConstraint(AbstractFlag.class).isEnabled() || gss.getDefaultGeometry(type) == null) {
                    it.remove();
                }
            }

            int numberOfTypes = types.size();
            int index = 0;
            for (TypeDefinition typeDef : types) {
                FeatureTypeStyle fts;

                // TODO based on default geometry?
                // polygon is always OK as it contains stroke and fill

                // Color color = generateRandomColor(Color.WHITE);

                Color color;
                if (numberOfTypes == 1) {
                    color = generateRandomColor(saturation, brightness);
                } else {
                    color = Color.getHSBColor((float) index / (float) numberOfTypes, saturation, brightness);
                }
                fts = createPolygonStyle(color, defWidth);

                fts.featureTypeNames().add(new NameImpl(getFeatureTypeName(typeDef)));

                style.featureTypeStyles().add(fts);

                index++;
            }
        }

        return style;
    }

    /**
     * Generate a random color. Mixing in WHITE will create pastel colors.
     * Mixing in a pastel color will create tinted colors.
     * 
     * @param mix color to mix in (use average of RGB values)
     * @return the generated color
     */
    public static Color generateRandomColor(@Nullable Color mix) {
        Random random = new Random();
        int red = random.nextInt(256);
        int green = random.nextInt(256);
        int blue = random.nextInt(256);

        // mix the color
        if (mix != null) {
            red = (red + mix.getRed()) / 2;
            green = (green + mix.getGreen()) / 2;
            blue = (blue + mix.getBlue()) / 2;
        }

        Color color = new Color(red, green, blue);
        return color;
    }

    private static float GOLDEN_RATIO_CONJUGATE = 0.618033988749895f;

    /**
     * Generate a random color.
     * 
     * Inspired by
     * http://martin.ankerl.com/2009/12/09/how-to-create-random-colors
     * -programmatically/
     * 
     * @param saturation the saturation (between 0.0f and 1.0f)
     * @param brightness the brightness (between 0.0f and 1.0f)
     * 
     * @return the random color
     */
    public static Color generateRandomColor(float saturation, float brightness) {
        Random random = new Random();

        float rand = random.nextFloat();
        rand = rand + GOLDEN_RATIO_CONJUGATE;
        rand = rand % 1;

        return Color.getHSBColor(rand, saturation, brightness);
    }

    // XXX StyleBuilder does not support feature type names with namespace
    //   /**
    //    * Get the name used in styles for the given type definition.
    //    * @param typeDef the type definition
    //    * @return the feature type name
    //    */
    //   public static QName getFeatureTypeName(TypeDefinition typeDef) {
    //      // default to element name
    //      Collection<? extends XmlElement> elements = typeDef.getConstraint(XmlElements.class).getElements();
    //      if (elements.size() == 1) {
    //         // only use element name if it is unique
    //         return elements.iterator().next().getName();
    //      }
    //      
    //      // type
    //      return typeDef.getName();
    //   }

    /**
     * Get the name used in styles for the given type definition.
     * 
     * @param typeDef the type definition
     * @return the feature type name
     */
    public static String getFeatureTypeName(TypeDefinition typeDef) {
        // type or element name
        return typeDef.getDisplayName();
    }

    /**
     * Get a style containing the default style for the given type.
     * 
     * @param type the type definition
     * @param dataSet the data set
     * @return the style with the default type style
     */
    public static Style getStyle(TypeDefinition type, DataSet dataSet) {
        Style style = styleFactory.createStyle();

        style.featureTypeStyles().add(getDefaultStyle(type, dataSet));

        return style;
    }

    /**
     * Create a new point symbolizer based on the given one.
     * 
     * @param symbolizer the point symbolizer
     * @param color the new color
     * @param width the new line width
     * @return the mutated symbolizer
     */
    public static Symbolizer mutatePointSymbolizer(PointSymbolizer symbolizer, Color color, int width) {
        // mutate mark
        Mark mark = SLD.mark(symbolizer);

        Mark mutiMark = styleBuilder.createMark(mark.getWellKnownName(),
                styleBuilder.createFill(color, DEFAULT_FILL_OPACITY), styleBuilder.createStroke(color, width));

        // XXX commented because unsupported in Geotools 8.0-M1
        //      mutiMark.setSize(mark.getSize());
        //      mutiMark.setRotation(mark.getRotation());

        // create new symbolizer
        return styleBuilder.createPointSymbolizer(styleBuilder.createGraphic(null, mutiMark, null));
    }

    /**
     * Manually create a default point style.
     * 
     * @param color the point color
     * @param width the line width
     * @return a Style for Point objects.
     */
    @SuppressWarnings("unused")
    private static FeatureTypeStyle createPointStyle(Color color, double width) {
        PointSymbolizer symbolizer = createPointSymbolizer(color, width);
        // symbolizer.getGraphic().setSize(filterFactory.literal(1));
        Rule rule = styleFactory.createRule();
        rule.symbolizers().add(symbolizer);
        FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle();
        fts.rules().add(rule);
        return fts;
    }

    /**
     * Create a default point symbolizer.
     * 
     * @param color the color
     * @param width the line width
     * @return the point symbolizer
     */
    public static PointSymbolizer createPointSymbolizer(Color color, double width) {
        return styleBuilder.createPointSymbolizer(styleBuilder.createGraphic(null,
                styleBuilder.createMark(StyleBuilder.MARK_X, styleBuilder.createFill(color, DEFAULT_FILL_OPACITY),
                        styleBuilder.createStroke(color, width)),
                null));
    }

    /**
     * Create a default line style.
     * 
     * @param color the line color
     * @param width the line width
     * @return a Style for Line/LineString objects.
     */
    @SuppressWarnings("unused")
    private static FeatureTypeStyle createLineStyle(Color color, double width) {
        LineSymbolizer symbolizer = createLineSymbolizer(color, width);

        Rule rule = styleFactory.createRule();
        rule.symbolizers().add(symbolizer);
        FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle();
        fts.rules().add(rule);
        return fts;
    }

    /**
     * Create a default line symbolizer.
     * 
     * @param color the color
     * @param width the line width
     * @return the line symbolizer
     */
    public static LineSymbolizer createLineSymbolizer(Color color, double width) {
        LineSymbolizer symbolizer = styleFactory.createLineSymbolizer();
        SLD.setLineColour(symbolizer, color);
        symbolizer.getStroke().setWidth(filterFactory.literal(width));
        return symbolizer;
    }

    /**
     * Create a default polygon style.
     * 
     * @param color the polygon color
     * @param width the line width
     * @return a Style for Polygon objects
     */
    private static FeatureTypeStyle createPolygonStyle(Color color, double width) {
        PolygonSymbolizer symbolizer = createPolygonSymbolizer(color, width);
        Rule rule = styleFactory.createRule();
        rule.symbolizers().add(symbolizer);
        FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle();
        fts.rules().add(rule);
        return fts;
    }

    /**
     * Create a default polygon symbolizer.
     * 
     * @param color the color
     * @param width the line width
     * @return the polygon symbolizer
     */
    public static PolygonSymbolizer createPolygonSymbolizer(Color color, double width) {
        PolygonSymbolizer symbolizer = styleFactory.createPolygonSymbolizer();
        SLD.setPolyColour(symbolizer, color);
        symbolizer.getStroke().setWidth(filterFactory.literal(width));
        Fill fill = styleFactory.createFill(filterFactory.literal(color),
                filterFactory.literal(DEFAULT_FILL_OPACITY));
        symbolizer.setFill(fill);
        return symbolizer;
    }

}