view artifacts/src/main/java/org/dive4elements/river/themes/ThemeDocument.java @ 6906:7a9cbb3a3d5a

Artifacts: Moved logic of ThemeUtils into ThemeDocument. TODO: Remove ThemeUtils
author Sascha L. Teichmann <teichmann@intevation.de>
date Fri, 23 Aug 2013 01:16:13 +0200
parents 1b35b2ddfc28
children 819481cc9195
line wrap: on
line source
/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU AGPL (>=v3)
 * and comes with ABSOLUTELY NO WARRANTY! Check out the
 * documentation coming with Dive4Elements River for details.
 */

package org.dive4elements.river.themes;

import java.awt.Color;
import java.awt.Font;

import org.apache.log4j.Logger;
import org.dive4elements.artifacts.CallMeta;
import org.dive4elements.artifacts.common.utils.XMLUtils;
import org.dive4elements.river.artifacts.model.MapserverStyle;
import org.dive4elements.river.artifacts.model.MapserverStyle.Clazz;
import org.dive4elements.river.artifacts.model.MapserverStyle.Expression;
import org.dive4elements.river.artifacts.model.MapserverStyle.Label;
import org.dive4elements.river.artifacts.model.MapserverStyle.Style;
import org.dive4elements.river.artifacts.resources.Resources;
import org.w3c.dom.Document;

public class ThemeDocument
{
    private static Logger logger = Logger.getLogger(ThemeDocument.class);

    private static final String MSG_ISOBATH_CLASS = "floodmap.isobath.class";

    private static final String MSG_ISOBATH_LASTCLASS = "floodmap.isobath.lastclass";

    public final static String XPATH_FILL_COLOR =
            "/theme/field[@name='fillcolor']/@default";

    public final static String XPATH_LINE_COLOR =
            "/theme/field[@name='linecolor']/@default";

    public final static String XPATH_AREA_LINE_COLOR =
            "/theme/field[@name='areabordercolor']/@default";

    public static final String XPATH_LINE_SIZE =
            "/theme/field[@name='linesize']/@default";

    public static final String XPATH_LINE_STYLE =
            "/theme/field[@name='linetype']/@default";

    public static final String XPATH_POINT_SIZE =
            "/theme/field[@name='pointsize']/@default";

    public static final String XPATH_POINT_COLOR =
            "/theme/field[@name='pointcolor']/@default";

    public final static String XPATH_SHOW_BORDER =
            "/theme/field[@name='showborder']/@default";

    public final static String XPATH_AREA_SHOW_BORDER =
            "/theme/field[@name='showborder']/@default";

    public final static String XPATH_SHOW_POINTS =
            "/theme/field[@name='showpoints']/@default";

    public final static String XPATH_SHOW_LINE =
            "/theme/field[@name='showlines']/@default";

    public final static String XPATH_SHOW_VERTICAL_LINE =
            "/theme/field[@name='showverticalline']/@default";

    public final static String XPATH_SHOW_HORIZONTAL_LINE =
            "/theme/field[@name='showhorizontalline']/@default";

    public final static String XPATH_SHOW_LINE_LABEL =
            "/theme/field[@name='showlinelabel']/@default";

    public final static String XPATH_SHOW_POINT_LABEL =
            "/theme/field[@name='showpointlabel']/@default";

    public final static String XPATH_SHOW_WIDTH =
            "/theme/field[@name='showwidth']/@default";

    public final static String XPATH_SHOW_LEVEL =
            "/theme/field[@name='showlevel']/@default";

    public final static String XPATH_TRANSPARENCY =
            "/theme/field[@name='transparency']/@default";

    public final static String XPATH_AREA_TRANSPARENCY =
            "/theme/field[@name='areatransparency']/@default";

    public final static String XPATH_SHOW_AREA =
            "/theme/field[@name='showarea']/@default";

    public final static String XPATH_SHOW_MIDDLE_HEIGHT =
            "/theme/field[@name='showmiddleheight']/@default";

    public final static String XPATH_LABEL_FONT_COLOR =
            "/theme/field[@name='labelfontcolor']/@default";

    public final static String XPATH_LABEL_FONT_SIZE =
            "/theme/field[@name='labelfontsize']/@default";

    public final static String XPATH_LABEL_FONT_FACE =
            "/theme/field[@name='labelfontface']/@default";

    public final static String XPATH_LABEL_FONT_STYLE =
            "/theme/field[@name='labelfontstyle']/@default";

    public final static String XPATH_TEXT_ORIENTATION =
            "/theme/field[@name='textorientation']/@default";

    public final static String XPATH_LABEL_BGCOLOR =
            "/theme/field[@name='labelbgcolor']/@default";

    public final static String XPATH_LABEL_SHOW_BACKGROUND =
            "/theme/field[@name='labelshowbg']/@default";

    public final static String XPATH_BACKGROUND_COLOR =
            "/theme/field[@name='backgroundcolor']/@default";

    public final static String XPATH_AREA_BACKGROUND_COLOR =
            "/theme/field[@name='areabgcolor']/@default";

    public final static String XPATH_SYMBOL =
            "/theme/field[@name='symbol']/@default";

    public final static String XPATH_SHOW_MINIMUM =
            "/theme/field[@name='showminimum']/@default";

    public final static String XPATH_SHOW_MAXIMUM =
            "/theme/field[@name='showmaximum']/@default";

    public final static String XPATH_WSPLGEN_FIELDS =
            "/theme[@name='WSPLGEN']/field";

    public final static String XPATH_WSPLGEN_STARTCOLOR =
            "/theme/field[@name='startcolor']/@default";

    public final static String XPATH_WSPLGEN_ENDCOLOR =
            "/theme/field[@name='endcolor']/@default";

    public final static String XPATH_WSPLGEN_NUMCLASSES =
            "/theme/field[@name='numclasses']/@default";

    /** XPATH to bandwidth field. */
    public final static String XPATH_BANDWIDTH =
            "/theme/field[@name='bandwidth']/@default";

    /** XPATH to find showextramark field. */
    public final static String XPATH_SHOWEXTRAMARK =
            "/theme/field[@name='showextramark']/@default";

    private Document document;

    public ThemeDocument() {
    }

    public ThemeDocument(Document document) {
        this.document = document;
    }

    public Document getDocument() {
        return document;
    }

    /** Parse string to be boolean with default if empty or unrecognized. */
    public static boolean parseBoolean(String value, boolean defaultsTo) {
        if (value == null || value.length() == 0) {
            return defaultsTo;
        }
        if (value.equals("false")) {
            return false;
        }
        else if (value.equals("true")) {
            return true;
        }
        else {
            return defaultsTo;
        }
    }


    /**
     * Attempt converting \param value to an integer, in failing cases,
     * return \param defaultsTo.
     * @param value String to be converted to integer.
     * @param defaultsTo Default to return if conversion failed.
     * @return \param value as integer or defaultsto if conversion failed.
     */
    public static int parseInteger(String value, int defaultsTo) {
        if (value == null || value.length() == 0) {
            return defaultsTo;
        }

        try {
            return Integer.parseInt(value);
        }
        catch (NumberFormatException nfe) {
            // do nothing
        }

        return defaultsTo;
    }


    /**
     * Attempt converting \param value to a double, in failing cases,
     * return \param defaultsTo.
     * @param value String to be converted to double.
     * @param defaultsTo Default to return if conversion failed.
     * @return \param value as integer or defaultsto if conversion failed.
     */
    public static double parseDouble(String value, double defaultsTo) {
        if (value == null || value.length() == 0) {
            return defaultsTo;
        }

        try {
            return Double.parseDouble(value);
        }
        catch (NumberFormatException nfe) {
            // do nothing
        }

        return defaultsTo;
    }

    public boolean parseShowLineLabel() {
        String show = XMLUtils.xpathString(document, XPATH_SHOW_LINE_LABEL, null);
        return parseBoolean(show, false);
    }

    public boolean parseShowWidth() {
        String show = XMLUtils.xpathString(document, XPATH_SHOW_WIDTH, null);
        return parseBoolean(show, false);
    }

    public boolean parseShowLevel() {
        String show = XMLUtils.xpathString(document, XPATH_SHOW_LEVEL, null);
        return parseBoolean(show, false);
    }

    public String parseTextOrientation() {
        String o = XMLUtils.xpathString(document, XPATH_TEXT_ORIENTATION, null);
        if ("true".equals(o)) {
            return "horizontal";
        }
        else {
            return "vertical";
        }
    }

    public boolean parseShowMiddleHeight() {
        String show = XMLUtils.xpathString(document, XPATH_SHOW_MIDDLE_HEIGHT, null);
        return parseBoolean(show, false);
    }

    public boolean parseLabelShowBackground() {
        String show = XMLUtils.xpathString(document, XPATH_LABEL_SHOW_BACKGROUND, null);
        return parseBoolean(show, false);
    }

    public Font parseTextFont() {
        String font = XMLUtils.xpathString(document, XPATH_LABEL_FONT_FACE, null);
        if (font == null || font.length() == 0) {
            return null;
        }

        int size = parseTextSize();
        int style = parseTextStyle();
        Font f = new Font(font, style, size);
        return f;
    }

    public Color parseTextColor() {
        return parseRGB(getTextColorString());
    }

    public String getTextColorString() {
        String textColor = XMLUtils.xpathString(document, XPATH_LABEL_FONT_COLOR, null);
        return textColor;
    }

    public Color parseTextBackground() {
        String color = getLabelBackgroundColorString();
        if (color == null || color.length() == 0) {
            return Color.WHITE;
        }
        return parseRGB(color);
    }

    public String getLabelBackgroundColorString() {
        return XMLUtils.xpathString(document, XPATH_LABEL_BGCOLOR, null);
    }


    public int parseLineWidth() {
        String size = XMLUtils.xpathString(document, XPATH_LINE_SIZE, null);
        if (size == null || size.length() == 0) {
            return 0;
        }

        try {
            return Integer.parseInt(size);
        }
        catch (NumberFormatException nfe) {
            logger.warn("Unable to set line size from string: '" + size + "'");
        }
        return 0;
    }

    public float [] parseLineStyle() {
        String dash = XMLUtils.xpathString(document, XPATH_LINE_STYLE, null);

        float[] def = {10};
        if (dash == null || dash.length() == 0) {
            return def;
        }

        String[] pattern = dash.split(",");
        if(pattern.length == 1) {
            return def;
        }

        try {
            float[] dashes = new float[pattern.length];
            for (int i = 0; i < pattern.length; i++) {
                dashes[i] = Float.parseFloat(pattern[i]);
            }
            return dashes;
        }
        catch(NumberFormatException nfe) {
            logger.warn("Unable to set dash from string: '" + dash + "'");
            return def;
        }
    }

    public int parsePointWidth() {
        String width = XMLUtils.xpathString(document, XPATH_POINT_SIZE, null);

        return parseInteger(width, 3);
    }

    public Color parsePointColor() {
        String color = XMLUtils.xpathString(document, XPATH_POINT_COLOR, null);
        logger.debug("parsePointColor(): color = " + color);
        return parseColor(color);
    }

    public boolean parseShowPoints() {
        String show = XMLUtils.xpathString(document, XPATH_SHOW_POINTS, null);
        return parseBoolean(show, false);
    }

    public boolean parseShowLine() {
        String show = XMLUtils.xpathString(document, XPATH_SHOW_LINE, null);
        return parseBoolean(show, true);
    }

    public int parseTextStyle() {
        return parseTextStyle(XPATH_LABEL_FONT_STYLE);
    }

    public int parseTextStyle(String path) {
        String style = XMLUtils.xpathString(document, path, null);
        if (style == null || style.length() == 0) {
            return Font.PLAIN;
        }

        if (style.equals("italic")) {
            return Font.ITALIC;
        }
        else if (style.equals("bold")) {
            return Font.BOLD;
        }
        else {
            return Font.PLAIN;
        }
    }

    public TextStyle parseComplexTextStyle() {
        return new TextStyle(
            parseTextColor(),
            parseTextFont(),
            parseTextBackground(),
            parseLabelShowBackground(),
            !parseTextOrientation().equals("horizontal"));
    }

    public LineStyle parseComplexLineStyle() {
        return new LineStyle(
            parseLineColorField(),
            Integer.valueOf(parseLineWidth()));
    }

    public Color parseComplexLineColorField() {
        return null;
    }

    public boolean parseShowVerticalLine() {
        String show = XMLUtils.xpathString(document, XPATH_SHOW_VERTICAL_LINE, null);
        return parseBoolean(show, true);
    }

    public boolean parseShowHorizontalLine() {
        String show = XMLUtils.xpathString(document, XPATH_SHOW_HORIZONTAL_LINE, null);
        return parseBoolean(show, true);
    }

    public double parseBandWidth() {
        String bandWidth = XMLUtils.xpathString(document, XPATH_BANDWIDTH, null);
        return parseDouble(bandWidth, 0);
    }

    public static Color parseColor(String colorString) {
        if (colorString == null || colorString.length() == 0) {
            return null;
        }
        else if (colorString.indexOf("#") == 0) {
            return parseHexColor(colorString);
        }
        else if (colorString.indexOf(",") >= 0) {
            return parseRGB(colorString);
        }

        return null;
    }


    /**
     * Parse a string like "#00CC22" and return the corresponding color.
     *
     * @param hex The hex color value.
     *
     * @return a Color or null, if <i>hex</i> is empty.
     */
    public static Color parseHexColor(String hex) {
        if (hex == null) {
            return null;
        }

        return Color.decode(hex);
    }


    public boolean parseShowArea() {
        String show = XMLUtils.xpathString(document, XPATH_SHOW_AREA, null);
        return parseBoolean(show, false);
    }

    public boolean parseShowPointLabel() {
        String show = XMLUtils.xpathString(document, XPATH_SHOW_POINT_LABEL, null);
        return parseBoolean(show, false);
    }

    public boolean parseShowExtraMark() {
        String show = XMLUtils.xpathString(document, XPATH_SHOWEXTRAMARK, null);
        return parseBoolean(show, false);
    }

    public int parseTextSize() {
        return parseTextSize(XPATH_LABEL_FONT_SIZE);
    }

    public int parseTextSize(String path) {
        String size = XMLUtils.xpathString(document, path, null);
        if (size == null || size.length() == 0) {
            return 10;
        }

        try {
            return Integer.parseInt(size);
        }
        catch (NumberFormatException nfe) {
        }
        return 10;
    }

    /**
     * Parse a string like "103, 100, 0" and return a corresping color.
     * @param rgbtext Color as string representation, e.g. "255,0,20".
     * @return Color, null in case of issues.
     */
    public static Color parseRGB(String rgbtext) {
        if (rgbtext == null) {
            return null;
        }
        String rgb[] = rgbtext.split(",");
        Color c = null;
        try {
            c = new Color(
                    Integer.parseInt(rgb[0].trim()),
                    Integer.parseInt(rgb[1].trim()),
                    Integer.parseInt(rgb[2].trim()));
        }
        catch (NumberFormatException nfe) {
            c = null;
        }
        return c;
    }

    public String getLineColorString() {
        return XMLUtils.xpathString(document, XPATH_LINE_COLOR, null);
    }


    /** Get show border as string. */
    public String getShowBorderString() {
        return XMLUtils.xpathString(document, XPATH_SHOW_BORDER, null);
    }


    /** Get fill color as string. */
    public String getFillColorString() {
        return XMLUtils.xpathString(document, XPATH_FILL_COLOR, null);
    }


    public String getBackgroundColorString() {
        return XMLUtils.xpathString(document, XPATH_BACKGROUND_COLOR, null);
    }

    public String getSymbol() {
        return XMLUtils.xpathString(document, XPATH_SYMBOL, null);
    }


    public String getTransparencyString() {
        return XMLUtils.xpathString(document, XPATH_TRANSPARENCY, null);
    }


    public String getAreaTransparencyString() {
        return XMLUtils.xpathString(document, XPATH_AREA_TRANSPARENCY, null);
    }


    public String getShowMinimum() {
        return XMLUtils.xpathString(document, XPATH_SHOW_MINIMUM, null);
    }


    public String getShowMaximum() {
        return XMLUtils.xpathString(document, XPATH_SHOW_MAXIMUM, null);
    }


    /**
     * Gets color from color field.
     * @param theme    the theme document.
     * @return color.
     */
    public Color parseFillColorField() {
        return parseRGB(getFillColorString());
    }


    public boolean parseShowBorder() {
        return parseBoolean(getShowBorderString(), false);
    }


    public int parseTransparency() {
        return parseInteger(getTransparencyString(), 50);
    }


    /**
     * Gets color from color field.
     * @param theme    the theme document.
     * @return color.
     */
    public Color parseLineColorField() {
        String lineColorStr = getLineColorString();
        logger.debug("parseLineColorField: lineColorStr = " +
                (lineColorStr == null ? "null" : lineColorStr));
        return parseColor(lineColorStr);
    }


    public Color parseAreaLineColorField() {
        String lineColorStr = getAreaLineColorString();
        logger.debug("parseLineColorField: lineColorStr = " +
                (lineColorStr == null ? "null" : lineColorStr));
        return parseColor(lineColorStr);
    }


    private String getAreaLineColorString() {
        return XMLUtils.xpathString(document, XPATH_AREA_LINE_COLOR, null);
    }


    public boolean parseShowMinimum() {
        return parseBoolean(getShowMinimum(), false);
    }


    public boolean parseShowMaximum() {
        return parseBoolean(getShowMaximum(), false);
    }


    /**
     * Creates a MapserverStyle from the given XML theme.
     * This method uses a start- and endcolor to interpolate a
     * given number of color classes for the MapserverStyle.
     * @param theme
     * @return String representation of the MapserverStyle
     */
    public String createDynamicMapserverStyle(
            float from, float to, float step, CallMeta meta)
    {
        MapserverStyle ms = new MapserverStyle();

        String strStartColor = XMLUtils.xpathString(document, XPATH_WSPLGEN_STARTCOLOR, null);
        Color startColor = strStartColor != null ? parseColor(strStartColor) : new Color(178, 201, 215);
        String strEndColor = XMLUtils.xpathString(document, XPATH_WSPLGEN_ENDCOLOR, null);
        Color endColor = strEndColor != null? parseColor(strEndColor) : new Color(2, 27, 42);

        to = to != 0 ? to : 9999;
        step = step != 0 ? step : to;

        int numClasses = (int)((to - from) / step);

        float rd = (endColor.getRed()   - startColor.getRed())   / (float)numClasses;
        float gd = (endColor.getGreen() - startColor.getGreen()) / (float)numClasses;
        float bd = (endColor.getBlue()  - startColor.getBlue())  / (float)numClasses;

        if (numClasses > 1) {
            // Desktop Flys always added a last "and larger class"
            numClasses += 1;
        }

        for (int n = 0; n < numClasses; n++) {
            StringBuilder newColor = new StringBuilder();
            newColor.append(startColor.getRed()   + Math.round(n * rd));
            newColor.append(' ');
            newColor.append(startColor.getGreen() + Math.round(n * gd));
            newColor.append(' ');
            newColor.append(startColor.getBlue()  + Math.round(n * bd));

            String expr = createWSPLGENClassExpression(from + n * step, step, n + 1, numClasses);
            String name = createWSPLGENClassName(from + n * step, step, n + 1, numClasses, meta);

            Clazz c = new Clazz(name);
            Style s = new Style();
            s.setColor(newColor.toString());
            s.setSize(5);

            c.addItem(new Expression("(" + expr + ")"));
            c.addItem(s);

            ms.addClazz(c);
        }

        return ms.toString();
    }


    protected static String createWSPLGENClassExpression(float val, float step, int idx, int maxIdx) {
        if (idx < maxIdx) {
            return "[DIFF] >= " + val + " AND  [DIFF] < " + (val + step);
        }
        else {
            return "[DIFF] >= " + val;
        }
    }

    /**
     * Creates a class name for the mapfile style that visualizes a floodmap.
     * The class names are used in the map's legend.
     *
     * @param val       Current isobath value.
     * @param step      Difference between to class values.
     * @param idx       Current class index that is being processed.
     * @param maxIdx    Highest class index.
     * @param meta      Caller meta object used to determine locale.
     * @return
     */
    protected static String createWSPLGENClassName(float val, float step, int idx, int maxIdx, CallMeta meta) {
        assert meta != null : "CallMeta instance is null";

        if (idx < maxIdx) {
            return Resources.getMsg(meta, MSG_ISOBATH_CLASS,
                    new Object[] {val, val + step});
        }
        else {
            return Resources.getMsg(meta, MSG_ISOBATH_LASTCLASS,
                    new Object[] {val});
        }
    }


    public String createMapserverStyle() {
        String symbol    = getSymbol();
        String backcolor = getLabelBackgroundColorString();
        String linecolor = getLineColorString();
        if (linecolor == null || "".equals(linecolor)) {
            logger.warn("createMapserverStyle: linecolor String is empty");
            linecolor = "0,128,255";
        }

        int linewidth = parseLineWidth();

        MapserverStyle ms = new MapserverStyle();

        Clazz c = new Clazz(" ");

        Style s = new Style();
        s.setOutlineColor(linecolor.replace(",", " "));

        if (backcolor != null && backcolor.length() > 0) {
            s.setColor(backcolor.replace(",", " "));
        }

        s.setSize(linewidth);
        s.setSymbol(symbol);
        c.addItem(s);

        String textcolor = getTextColorString();
        int    textsize  = parseTextSize();

        if (textcolor != null && textcolor.length() > 0 && textsize > 0) {
            Label l = new Label();
            l.setColor(textcolor.replace(",", " "));
            l.setSize(textsize);
            c.addItem(l);
        }

        ms.addClazz(c);

        return ms.toString();
    }


    public String getAreaBackgroundColorString() {
        return XMLUtils.xpathString(document, XPATH_AREA_BACKGROUND_COLOR, null);
    }


    public Color parseAreaBackgroundColor() {
        return parseColor(getAreaBackgroundColorString());
    }


    public int parseAreaTransparency() {
        return parseInteger(getAreaTransparencyString(), 50);
    }


    public boolean parseAreaShowBorder() {
        return parseBoolean(getAreaShowBorderString(), false);
    }


    private String getAreaShowBorderString() {
        return XMLUtils.xpathString(document, XPATH_AREA_SHOW_BORDER, null);
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org